From e92e9e839ca2cf42b56322b2489ccc0d8bf767af Mon Sep 17 00:00:00 2001 From: Luca Muscariello Date: Thu, 15 Apr 2021 09:05:46 +0200 Subject: [HICN-690] Transport Library Major Refactory The current patch provides a major refactory of the transportlibrary. A summary of the different components that underwent major modifications is reported below. - Transport protocol updates The hierarchy of classes has been optimized to have common transport services across different transport protocols. This can allow to customize a transport protocol with new features. - A new real-time communication protocol The RTC protocol has been optimized in terms of algorithms to reduce consumer-producer synchronization latency. - A novel socket API The API has been reworked to be easier to consumer but also to have a more efficient integration in L4 proxies. - Several performance improvements A large number of performance improvements have been included in particular to make the entire stack zero-copy and optimize cache miss. - New memory buffer framework Memory management has been reworked entirely to provide a more efficient infra with a richer API. Buffers are now allocated in blocks and a single buffer holds the memory for (1) the shared_ptr control block, (2) the metadata of the packet (e.g. name, pointer to other buffers if buffer is chained and relevant offsets), and (3) the packet itself, as it is sent/received over the network. - A new slab allocator Dynamic memory allocation is now managed by a novel slab allocator that is optimised for packet processing and connection management. Memory is organized in pools of blocks all of the same size which are used during the processing of outgoing/incoming packets. When a memory block Is allocated is always taken from a global pool and when it is deallocated is returned to the pool, thus avoiding the cost of any heap allocation in the data path. - New transport connectors Consumer and producer end-points can communication either using an hicn packet forwarder or with direct connector based on shared memories or sockets. The usage of transport connectors typically for unit and funcitonal testing but may have additional usage. - Support for FEC/ECC for transport services FEC/ECC via reed solomon is supported by default and made available to transport services as a modular component. Reed solomon block codes is a default FEC model that can be replaced in a modular way by many other codes including RLNC not avaiable in this distribution. The current FEC framework support variable size padding and efficiently makes use of the infra memory buffers to avoid additiona copies. - Secure transport framework for signature computation and verification Crypto support is nativelty used in hICN for integrity and authenticity. Novel support that includes RTC has been implemented and made modular and reusable acrosso different transport protocols. - TLS - Transport layer security over hicn Point to point confidentiality is provided by integrating TLS on top of hICN reliable and non-reliable transport. The integration is common and makes a different use of the TLS record. - MLS - Messaging layer security over hicn MLS integration on top of hICN is made by using the MLSPP implemetation open sourced by Cisco. We have included instrumentation tools to deploy performance and functional tests of groups of end-points. - Android support The overall code has been heavily tested in Android environments and has received heavy lifting to better run natively in recent Android OS. Co-authored-by: Mauro Sardara Co-authored-by: Michele Papalini Co-authored-by: Olivier Roques Co-authored-by: Giulio Grassi Change-Id: If477ba2fa686e6f47bdf96307ac60938766aef69 Signed-off-by: Luca Muscariello --- CMakeLists.txt | 3 - apps/.clang-format | 14 + apps/CMakeLists.txt | 8 +- apps/higet/higet.cc | 12 +- apps/http-proxy/src/forwarder_interface.cc | 3 +- apps/http-proxy/src/http_proxy.cc | 1 + apps/http-proxy/src/icn_receiver.cc | 2 +- cmake/Modules/BuildMacros.cmake | 87 +- cmake/Modules/FindLibFec.cmake | 24 + cmake/Modules/FindLibRely.cmake | 24 + cmake/Modules/FindLibconfig++.cmake | 43 + cmake/Modules/FindLibconfig.cmake | 43 + cmake/Modules/FindLibhicn.cmake | 2 +- cmake/Modules/GTestImport.cmake | 7 +- cmake/Modules/ServiceScript.cmake | 2 +- ctrl/facemgr/CMakeLists.txt | 2 +- ctrl/facemgr/src/CMakeLists.txt | 6 +- ctrl/libhicnctrl/src/CMakeLists.txt | 2 +- ctrl/libhicnctrl/src/hicn_plugin_api.c | 10 + docs/source/index.rst | 1 - extras/CMakeLists.txt | 1 - extras/libmemif/CMakeLists2.patch | 2 +- hicn-light/src/hicn/CMakeLists.txt | 2 +- hicn-light/src/hicn/core/message.c | 3 +- hicn-light/src/hicn/core/messageHandler.h | 36 +- hicn-plugin/src/CMakeLists.txt | 14 +- hicn-plugin/src/faces/face.c | 1 + hicn-plugin/src/faces/face.h | 5 +- hicn-plugin/src/faces/iface_node.c | 10 +- hicn-plugin/src/hicn.h | 1 + hicn-plugin/src/interest_hitcs_node.c | 3 + hicn-plugin/vapi/CMakeLists.txt | 2 +- lib/.clang-format | 21 + lib/includes/hicn/base.h | 18 +- lib/includes/hicn/common.h | 9 + lib/includes/hicn/compat.h | 299 +++--- lib/includes/hicn/ops.h | 14 +- lib/src/CMakeLists.txt | 4 +- lib/src/common.c | 12 +- lib/src/compat.c | 472 +++++---- lib/src/name.c | 2 +- lib/src/protocol/ah.c | 2 +- lib/src/protocol/icmp.c | 2 +- lib/src/protocol/ipv4.c | 4 +- lib/src/protocol/ipv6.c | 20 +- lib/src/protocol/tcp.c | 28 +- libtransport/CMakeLists.txt | 60 +- .../includes/hicn/transport/CMakeLists.txt | 4 +- .../includes/hicn/transport/auth/CMakeLists.txt | 29 + libtransport/includes/hicn/transport/auth/common.h | 29 + .../includes/hicn/transport/auth/crypto_hash.h | 121 +++ .../hicn/transport/auth/crypto_hash_type.h | 35 + .../includes/hicn/transport/auth/crypto_hasher.h | 70 ++ .../includes/hicn/transport/auth/crypto_suite.h | 39 + .../includes/hicn/transport/auth/identity.h | 66 ++ libtransport/includes/hicn/transport/auth/key_id.h | 27 + .../includes/hicn/transport/auth/policies.h | 33 + libtransport/includes/hicn/transport/auth/signer.h | 92 ++ .../includes/hicn/transport/auth/verifier.h | 169 ++++ .../includes/hicn/transport/core/CMakeLists.txt | 4 + .../includes/hicn/transport/core/connector.h | 212 ++++ .../includes/hicn/transport/core/connector_stats.h | 40 + .../includes/hicn/transport/core/content_object.h | 50 +- .../includes/hicn/transport/core/endpoint.h | 80 ++ .../hicn/transport/core/global_object_pool.h | 122 +++ .../includes/hicn/transport/core/interest.h | 78 +- .../includes/hicn/transport/core/io_module.h | 127 +++ libtransport/includes/hicn/transport/core/name.h | 6 +- libtransport/includes/hicn/transport/core/packet.h | 143 ++- .../includes/hicn/transport/core/payload_type.h | 3 +- libtransport/includes/hicn/transport/core/prefix.h | 8 +- .../includes/hicn/transport/errors/errors.h | 5 +- .../hicn/transport/http/client_connection.h | 4 +- .../hicn/transport/interfaces/CMakeLists.txt | 3 +- .../includes/hicn/transport/interfaces/callbacks.h | 21 +- .../transport/interfaces/global_conf_interface.h | 60 ++ .../interfaces/p2psecure_socket_producer.h | 5 +- .../includes/hicn/transport/interfaces/portal.h | 8 +- .../hicn/transport/interfaces/socket_consumer.h | 30 +- .../interfaces/socket_options_default_values.h | 1 + .../transport/interfaces/socket_options_keys.h | 11 +- .../hicn/transport/interfaces/socket_producer.h | 49 +- .../hicn/transport/interfaces/statistics.h | 79 +- .../hicn/transport/portability/c_portability.h | 2 +- .../includes/hicn/transport/portability/platform.h | 108 ++ .../hicn/transport/portability/portability.h | 2 +- .../hicn/transport/portability/win_portability.h | 1 + .../includes/hicn/transport/utils/CMakeLists.txt | 5 + .../includes/hicn/transport/utils/conversions.h | 2 +- .../includes/hicn/transport/utils/enum_iterator.h | 43 + .../includes/hicn/transport/utils/event_thread.h | 63 +- libtransport/includes/hicn/transport/utils/file.h | 28 + .../hicn/transport/utils/fixed_block_allocator.h | 209 ++-- libtransport/includes/hicn/transport/utils/linux.h | 4 +- .../includes/hicn/transport/utils/membuf.h | 32 +- .../includes/hicn/transport/utils/move_wrapper.h | 39 + .../includes/hicn/transport/utils/noncopyable.h | 29 + .../includes/hicn/transport/utils/object_pool.h | 25 +- .../hicn/transport/utils/shared_ptr_utils.h | 28 + .../includes/hicn/transport/utils/singleton.h | 39 + .../includes/hicn/transport/utils/string_utils.h | 2 +- libtransport/src/CMakeLists.txt | 28 +- libtransport/src/auth/CMakeLists.txt | 22 + libtransport/src/auth/identity.cc | 116 +++ libtransport/src/auth/signer.cc | 208 ++++ libtransport/src/auth/verifier.cc | 335 +++++++ libtransport/src/config.h.in | 4 - libtransport/src/core/CMakeLists.txt | 50 +- libtransport/src/core/content_object.cc | 45 +- libtransport/src/core/errors.cc | 49 + libtransport/src/core/errors.h | 87 ++ libtransport/src/core/facade.h | 22 - libtransport/src/core/fec.cc | 878 ++++++++++++++++ libtransport/src/core/fec.h | 65 ++ libtransport/src/core/global_configuration.cc | 173 ++++ libtransport/src/core/global_configuration.h | 102 ++ libtransport/src/core/interest.cc | 82 +- libtransport/src/core/io_module.cc | 84 ++ libtransport/src/core/local_connector.cc | 47 + libtransport/src/core/local_connector.h | 62 ++ libtransport/src/core/manifest.h | 19 +- libtransport/src/core/manifest_format.h | 10 +- libtransport/src/core/manifest_format_fixed.cc | 43 +- libtransport/src/core/manifest_format_fixed.h | 19 +- libtransport/src/core/manifest_inline.h | 37 +- libtransport/src/core/name.cc | 10 +- libtransport/src/core/packet.cc | 370 ++++--- libtransport/src/core/pending_interest.h | 16 +- libtransport/src/core/portal.cc | 147 +++ libtransport/src/core/portal.h | 285 +++--- libtransport/src/core/prefix.cc | 14 +- libtransport/src/core/rs.cc | 365 +++++++ libtransport/src/core/rs.h | 338 +++++++ libtransport/src/core/tcp_socket_connector.h | 3 +- libtransport/src/http/client_connection.cc | 31 +- libtransport/src/implementation/CMakeLists.txt | 10 +- .../implementation/p2psecure_socket_consumer.cc | 2 - .../src/implementation/p2psecure_socket_consumer.h | 2 - .../implementation/p2psecure_socket_producer.cc | 149 ++- .../src/implementation/p2psecure_socket_producer.h | 44 +- libtransport/src/implementation/socket.cc | 26 + libtransport/src/implementation/socket.h | 52 +- libtransport/src/implementation/socket_consumer.h | 194 +--- libtransport/src/implementation/socket_producer.h | 632 +++--------- .../src/implementation/tls_rtc_socket_producer.cc | 2 - .../src/implementation/tls_rtc_socket_producer.h | 7 +- .../src/implementation/tls_socket_consumer.cc | 8 +- .../src/implementation/tls_socket_consumer.h | 3 - .../src/implementation/tls_socket_producer.cc | 174 +--- .../src/implementation/tls_socket_producer.h | 33 +- libtransport/src/interfaces/CMakeLists.txt | 6 +- .../src/interfaces/global_configuration.cc | 50 + .../src/interfaces/p2psecure_socket_consumer.cc | 1 - .../src/interfaces/p2psecure_socket_producer.cc | 3 +- libtransport/src/interfaces/portal.cc | 37 +- libtransport/src/interfaces/socket_consumer.cc | 30 +- libtransport/src/interfaces/socket_producer.cc | 78 +- .../src/interfaces/tls_rtc_socket_producer.cc | 3 +- libtransport/src/interfaces/tls_socket_consumer.cc | 3 +- libtransport/src/interfaces/tls_socket_producer.cc | 3 +- libtransport/src/io_modules/CMakeLists.txt | 37 + .../src/io_modules/forwarder/CMakeLists.txt | 44 + .../src/io_modules/forwarder/configuration.h | 89 ++ libtransport/src/io_modules/forwarder/errors.cc | 52 + libtransport/src/io_modules/forwarder/errors.h | 91 ++ libtransport/src/io_modules/forwarder/forwarder.cc | 296 ++++++ libtransport/src/io_modules/forwarder/forwarder.h | 90 ++ .../src/io_modules/forwarder/forwarder_module.cc | 87 ++ .../src/io_modules/forwarder/forwarder_module.h | 70 ++ .../src/io_modules/forwarder/global_id_counter.h | 54 + .../src/io_modules/forwarder/udp_tunnel.cc | 288 ++++++ libtransport/src/io_modules/forwarder/udp_tunnel.h | 147 +++ .../io_modules/forwarder/udp_tunnel_listener.cc | 177 ++++ .../src/io_modules/forwarder/udp_tunnel_listener.h | 110 ++ .../src/io_modules/loopback/CMakeLists.txt | 34 + libtransport/src/io_modules/loopback/local_face.cc | 69 ++ libtransport/src/io_modules/loopback/local_face.h | 54 + .../src/io_modules/loopback/loopback_module.cc | 84 ++ .../src/io_modules/loopback/loopback_module.h | 70 ++ libtransport/src/io_modules/memif/CMakeLists.txt | 56 ++ libtransport/src/io_modules/memif/hicn_vapi.c | 229 +++++ libtransport/src/io_modules/memif/hicn_vapi.h | 82 ++ .../src/io_modules/memif/memif_connector.cc | 493 +++++++++ .../src/io_modules/memif/memif_connector.h | 130 +++ libtransport/src/io_modules/memif/memif_vapi.c | 127 +++ libtransport/src/io_modules/memif/memif_vapi.h | 54 + .../src/io_modules/memif/vpp_forwarder_module.cc | 263 +++++ .../src/io_modules/memif/vpp_forwarder_module.h | 83 ++ .../io_modules/raw_socket/raw_socket_connector.cc | 201 ++++ .../io_modules/raw_socket/raw_socket_connector.h | 80 ++ .../io_modules/raw_socket/raw_socket_interface.cc | 56 ++ .../io_modules/raw_socket/raw_socket_interface.h | 61 ++ libtransport/src/io_modules/udp/CMakeLists.txt | 47 + .../src/io_modules/udp/hicn_forwarder_module.cc | 181 ++++ .../src/io_modules/udp/hicn_forwarder_module.h | 86 ++ .../src/io_modules/udp/udp_socket_connector.cc | 211 ++++ .../src/io_modules/udp/udp_socket_connector.h | 89 ++ libtransport/src/protocols/CMakeLists.txt | 21 +- .../src/protocols/byte_stream_reassembly.cc | 49 +- .../src/protocols/byte_stream_reassembly.h | 4 +- .../src/protocols/data_processing_events.h | 3 +- libtransport/src/protocols/datagram_reassembly.cc | 4 +- libtransport/src/protocols/datagram_reassembly.h | 2 +- libtransport/src/protocols/errors.cc | 4 +- libtransport/src/protocols/fec_base.h | 86 ++ libtransport/src/protocols/incremental_indexer.cc | 31 +- libtransport/src/protocols/incremental_indexer.h | 35 +- libtransport/src/protocols/indexer.cc | 24 +- libtransport/src/protocols/indexer.h | 16 +- .../src/protocols/manifest_incremental_indexer.cc | 215 ++-- .../src/protocols/manifest_incremental_indexer.h | 39 +- .../src/protocols/prod_protocol_bytestream.cc | 390 ++++++++ .../src/protocols/prod_protocol_bytestream.h | 72 ++ libtransport/src/protocols/prod_protocol_rtc.cc | 481 +++++++++ libtransport/src/protocols/prod_protocol_rtc.h | 127 +++ libtransport/src/protocols/production_protocol.cc | 135 +++ libtransport/src/protocols/production_protocol.h | 108 ++ libtransport/src/protocols/raaqm.cc | 58 +- libtransport/src/protocols/raaqm.h | 15 +- libtransport/src/protocols/raaqm_data_path.cc | 1 - libtransport/src/protocols/raaqm_data_path.h | 1 - libtransport/src/protocols/rate_estimation.cc | 1 - libtransport/src/protocols/rate_estimation.h | 1 - libtransport/src/protocols/reassembly.cc | 1 - libtransport/src/protocols/reassembly.h | 2 +- libtransport/src/protocols/rtc/CMakeLists.txt | 38 + .../src/protocols/rtc/congestion_detection.cc | 101 ++ .../src/protocols/rtc/congestion_detection.h | 138 +++ libtransport/src/protocols/rtc/probe_handler.cc | 107 ++ libtransport/src/protocols/rtc/probe_handler.h | 75 ++ libtransport/src/protocols/rtc/rtc.cc | 607 ++++++++++++ libtransport/src/protocols/rtc/rtc.h | 113 +++ libtransport/src/protocols/rtc/rtc_consts.h | 121 +++ libtransport/src/protocols/rtc/rtc_data_path.cc | 197 ++++ libtransport/src/protocols/rtc/rtc_data_path.h | 97 ++ libtransport/src/protocols/rtc/rtc_ldr.cc | 427 ++++++++ libtransport/src/protocols/rtc/rtc_ldr.h | 108 ++ libtransport/src/protocols/rtc/rtc_packet.h | 89 ++ libtransport/src/protocols/rtc/rtc_rc.h | 58 ++ libtransport/src/protocols/rtc/rtc_rc_frame.cc | 79 ++ libtransport/src/protocols/rtc/rtc_rc_frame.h | 46 + libtransport/src/protocols/rtc/rtc_rc_queue.cc | 106 ++ libtransport/src/protocols/rtc/rtc_rc_queue.h | 47 + libtransport/src/protocols/rtc/rtc_state.cc | 560 +++++++++++ libtransport/src/protocols/rtc/rtc_state.h | 253 +++++ .../src/protocols/rtc/trendline_estimator.cc | 334 +++++++ .../src/protocols/rtc/trendline_estimator.h | 147 +++ libtransport/src/protocols/transport_protocol.cc | 132 +++ libtransport/src/protocols/transport_protocol.h | 104 ++ libtransport/src/test/CMakeLists.txt | 13 +- libtransport/src/test/fec_reed_solomon.cc | 154 +++ libtransport/src/test/fec_rely.cc | 156 +++ libtransport/src/test/packet_samples.h | 58 ++ libtransport/src/test/test_auth.cc | 110 ++ .../src/test/test_consumer_producer_rtc.cc | 176 ++++ libtransport/src/test/test_core_manifest.cc | 44 +- libtransport/src/test/test_event_thread.cc | 106 ++ libtransport/src/test/test_fec_reedsolomon.cc | 291 ++++++ libtransport/src/test/test_interest.cc | 267 +++++ libtransport/src/test/test_packet.cc | 1047 ++++++++++++++++++++ libtransport/src/transport.config | 27 + libtransport/src/utils/content_store.cc | 4 +- libtransport/src/utils/content_store.h | 2 +- libtransport/src/utils/daemonizator.cc | 1 - libtransport/src/utils/epoll_event_reactor.cc | 7 +- libtransport/src/utils/epoll_event_reactor.h | 2 +- libtransport/src/utils/fd_deadline_timer.h | 6 +- libtransport/src/utils/membuf.cc | 44 +- libtransport/src/utils/memory_pool_allocator.h | 2 +- libtransport/src/utils/min_filter.h | 5 + scripts/build-packages.sh | 20 +- scripts/functions.sh | 23 +- utils/.clang-format | 14 + utils/src/hiperf.cc | 635 ++++++++---- utils/src/ping_client.cc | 55 +- utils/src/ping_server.cc | 33 +- 276 files changed, 20362 insertions(+), 3007 deletions(-) create mode 100644 apps/.clang-format create mode 100644 cmake/Modules/FindLibFec.cmake create mode 100644 cmake/Modules/FindLibRely.cmake create mode 100644 cmake/Modules/FindLibconfig++.cmake create mode 100644 cmake/Modules/FindLibconfig.cmake create mode 100644 lib/.clang-format create mode 100644 libtransport/includes/hicn/transport/auth/CMakeLists.txt create mode 100644 libtransport/includes/hicn/transport/auth/common.h create mode 100644 libtransport/includes/hicn/transport/auth/crypto_hash.h create mode 100644 libtransport/includes/hicn/transport/auth/crypto_hash_type.h create mode 100644 libtransport/includes/hicn/transport/auth/crypto_hasher.h create mode 100644 libtransport/includes/hicn/transport/auth/crypto_suite.h create mode 100644 libtransport/includes/hicn/transport/auth/identity.h create mode 100644 libtransport/includes/hicn/transport/auth/key_id.h create mode 100644 libtransport/includes/hicn/transport/auth/policies.h create mode 100644 libtransport/includes/hicn/transport/auth/signer.h create mode 100644 libtransport/includes/hicn/transport/auth/verifier.h create mode 100644 libtransport/includes/hicn/transport/core/connector.h create mode 100644 libtransport/includes/hicn/transport/core/connector_stats.h create mode 100644 libtransport/includes/hicn/transport/core/endpoint.h create mode 100644 libtransport/includes/hicn/transport/core/global_object_pool.h create mode 100644 libtransport/includes/hicn/transport/core/io_module.h create mode 100644 libtransport/includes/hicn/transport/interfaces/global_conf_interface.h create mode 100644 libtransport/includes/hicn/transport/portability/platform.h create mode 100644 libtransport/includes/hicn/transport/utils/enum_iterator.h create mode 100644 libtransport/includes/hicn/transport/utils/file.h create mode 100644 libtransport/includes/hicn/transport/utils/move_wrapper.h create mode 100644 libtransport/includes/hicn/transport/utils/noncopyable.h create mode 100644 libtransport/includes/hicn/transport/utils/shared_ptr_utils.h create mode 100644 libtransport/includes/hicn/transport/utils/singleton.h create mode 100644 libtransport/src/auth/CMakeLists.txt create mode 100644 libtransport/src/auth/identity.cc create mode 100644 libtransport/src/auth/signer.cc create mode 100644 libtransport/src/auth/verifier.cc create mode 100644 libtransport/src/core/errors.cc create mode 100644 libtransport/src/core/errors.h create mode 100644 libtransport/src/core/fec.cc create mode 100644 libtransport/src/core/fec.h create mode 100644 libtransport/src/core/global_configuration.cc create mode 100644 libtransport/src/core/global_configuration.h create mode 100644 libtransport/src/core/io_module.cc create mode 100644 libtransport/src/core/local_connector.cc create mode 100644 libtransport/src/core/local_connector.h create mode 100644 libtransport/src/core/portal.cc create mode 100644 libtransport/src/core/rs.cc create mode 100644 libtransport/src/core/rs.h create mode 100644 libtransport/src/implementation/socket.cc create mode 100644 libtransport/src/interfaces/global_configuration.cc create mode 100644 libtransport/src/io_modules/CMakeLists.txt create mode 100644 libtransport/src/io_modules/forwarder/CMakeLists.txt create mode 100644 libtransport/src/io_modules/forwarder/configuration.h create mode 100644 libtransport/src/io_modules/forwarder/errors.cc create mode 100644 libtransport/src/io_modules/forwarder/errors.h create mode 100644 libtransport/src/io_modules/forwarder/forwarder.cc create mode 100644 libtransport/src/io_modules/forwarder/forwarder.h create mode 100644 libtransport/src/io_modules/forwarder/forwarder_module.cc create mode 100644 libtransport/src/io_modules/forwarder/forwarder_module.h create mode 100644 libtransport/src/io_modules/forwarder/global_id_counter.h create mode 100644 libtransport/src/io_modules/forwarder/udp_tunnel.cc create mode 100644 libtransport/src/io_modules/forwarder/udp_tunnel.h create mode 100644 libtransport/src/io_modules/forwarder/udp_tunnel_listener.cc create mode 100644 libtransport/src/io_modules/forwarder/udp_tunnel_listener.h create mode 100644 libtransport/src/io_modules/loopback/CMakeLists.txt create mode 100644 libtransport/src/io_modules/loopback/local_face.cc create mode 100644 libtransport/src/io_modules/loopback/local_face.h create mode 100644 libtransport/src/io_modules/loopback/loopback_module.cc create mode 100644 libtransport/src/io_modules/loopback/loopback_module.h create mode 100644 libtransport/src/io_modules/memif/CMakeLists.txt create mode 100644 libtransport/src/io_modules/memif/hicn_vapi.c create mode 100644 libtransport/src/io_modules/memif/hicn_vapi.h create mode 100644 libtransport/src/io_modules/memif/memif_connector.cc create mode 100644 libtransport/src/io_modules/memif/memif_connector.h create mode 100644 libtransport/src/io_modules/memif/memif_vapi.c create mode 100644 libtransport/src/io_modules/memif/memif_vapi.h create mode 100644 libtransport/src/io_modules/memif/vpp_forwarder_module.cc create mode 100644 libtransport/src/io_modules/memif/vpp_forwarder_module.h create mode 100644 libtransport/src/io_modules/raw_socket/raw_socket_connector.cc create mode 100644 libtransport/src/io_modules/raw_socket/raw_socket_connector.h create mode 100644 libtransport/src/io_modules/raw_socket/raw_socket_interface.cc create mode 100644 libtransport/src/io_modules/raw_socket/raw_socket_interface.h create mode 100644 libtransport/src/io_modules/udp/CMakeLists.txt create mode 100644 libtransport/src/io_modules/udp/hicn_forwarder_module.cc create mode 100644 libtransport/src/io_modules/udp/hicn_forwarder_module.h create mode 100644 libtransport/src/io_modules/udp/udp_socket_connector.cc create mode 100644 libtransport/src/io_modules/udp/udp_socket_connector.h create mode 100644 libtransport/src/protocols/fec_base.h create mode 100644 libtransport/src/protocols/prod_protocol_bytestream.cc create mode 100644 libtransport/src/protocols/prod_protocol_bytestream.h create mode 100644 libtransport/src/protocols/prod_protocol_rtc.cc create mode 100644 libtransport/src/protocols/prod_protocol_rtc.h create mode 100644 libtransport/src/protocols/production_protocol.cc create mode 100644 libtransport/src/protocols/production_protocol.h create mode 100644 libtransport/src/protocols/rtc/CMakeLists.txt create mode 100644 libtransport/src/protocols/rtc/congestion_detection.cc create mode 100644 libtransport/src/protocols/rtc/congestion_detection.h create mode 100644 libtransport/src/protocols/rtc/probe_handler.cc create mode 100644 libtransport/src/protocols/rtc/probe_handler.h create mode 100644 libtransport/src/protocols/rtc/rtc.cc create mode 100644 libtransport/src/protocols/rtc/rtc.h create mode 100644 libtransport/src/protocols/rtc/rtc_consts.h create mode 100644 libtransport/src/protocols/rtc/rtc_data_path.cc create mode 100644 libtransport/src/protocols/rtc/rtc_data_path.h create mode 100644 libtransport/src/protocols/rtc/rtc_ldr.cc create mode 100644 libtransport/src/protocols/rtc/rtc_ldr.h create mode 100644 libtransport/src/protocols/rtc/rtc_packet.h create mode 100644 libtransport/src/protocols/rtc/rtc_rc.h create mode 100644 libtransport/src/protocols/rtc/rtc_rc_frame.cc create mode 100644 libtransport/src/protocols/rtc/rtc_rc_frame.h create mode 100644 libtransport/src/protocols/rtc/rtc_rc_queue.cc create mode 100644 libtransport/src/protocols/rtc/rtc_rc_queue.h create mode 100644 libtransport/src/protocols/rtc/rtc_state.cc create mode 100644 libtransport/src/protocols/rtc/rtc_state.h create mode 100644 libtransport/src/protocols/rtc/trendline_estimator.cc create mode 100644 libtransport/src/protocols/rtc/trendline_estimator.h create mode 100644 libtransport/src/protocols/transport_protocol.cc create mode 100644 libtransport/src/protocols/transport_protocol.h create mode 100644 libtransport/src/test/fec_reed_solomon.cc create mode 100644 libtransport/src/test/fec_rely.cc create mode 100644 libtransport/src/test/packet_samples.h create mode 100644 libtransport/src/test/test_auth.cc create mode 100644 libtransport/src/test/test_consumer_producer_rtc.cc create mode 100644 libtransport/src/test/test_event_thread.cc create mode 100644 libtransport/src/test/test_fec_reedsolomon.cc create mode 100644 libtransport/src/test/test_interest.cc create mode 100644 libtransport/src/test/test_packet.cc create mode 100644 libtransport/src/transport.config create mode 100644 utils/.clang-format diff --git a/CMakeLists.txt b/CMakeLists.txt index d6d67ec9f..31f40aaa6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,6 @@ endif () option(BUILD_HICNPLUGIN "Build the hicn vpp plugin" OFF) option(BUILD_SYSREPOPLUGIN "Build the sysrepo plugin" OFF) option(BUILD_EXTRAS "Build external projects" OFF) -option(BUILD_WSPLUGIN "Build the hicn plugin for wireshark" OFF) option(BUILD_TELEMETRY "Build telemetry projects" OFF) option(BUILD_TESTS "Build unit tests" OFF) option(DISABLE_EXECUTABLES "Disable executables" OFF) @@ -72,7 +71,6 @@ list(APPEND dir_options BUILD_APPS BUILD_SYSREPOPLUGIN BUILD_EXTRAS - BUILD_WSPLUGIN BUILD_TELEMETRY ) @@ -85,7 +83,6 @@ set(BUILD_CTRL_DIR ctrl) set(BUILD_HICNPLUGIN_DIR hicn-plugin) set(BUILD_SYSREPOPLUGIN_DIR ctrl/sysrepo-plugins) set(BUILD_EXTRAS_DIR extras/) -set(BUILD_WSPLUGIN_DIR extras/packethicn) set(BUILD_TELEMETRY_DIR telemetry) ## Add enabled components diff --git a/apps/.clang-format b/apps/.clang-format new file mode 100644 index 000000000..cd21e2017 --- /dev/null +++ b/apps/.clang-format @@ -0,0 +1,14 @@ +# Copyright (c) 2017-2021 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +BasedOnStyle: Google diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 5737a1d09..9b9011800 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -49,15 +49,11 @@ else() ) endif() -set(SUFFIX "") -if (${LIBTRANSPORT_LIBRARIES} MATCHES ".*-memif.*") - set(DEPENDENCIES ${LIBMEMIF_SHARED}) - set(SUFFIX "-memif") +# Worksroung for unresolved symbols in vpp libraries +if(${CMAKE_SYSTEM_NAME} MATCHES Linux) set(LINK_FLAGS "-Wl,-unresolved-symbols=ignore-in-shared-libs") endif() -set(HICN_APPS "${HICN_APPS}${SUFFIX}") - list(APPEND LIBRARIES ${LIBTRANSPORT_LIBRARIES} ${LIBHICNCTRL_LIBRARIES} diff --git a/apps/higet/higet.cc b/apps/higet/higet.cc index 7584148a9..194e616ed 100644 --- a/apps/higet/higet.cc +++ b/apps/higet/higet.cc @@ -14,10 +14,11 @@ */ #include -#include -#include + #include +#include #include +#include #define ASIO_STANDALONE #include @@ -334,9 +335,14 @@ int main(int argc, char **argv) { {"Connection", "Keep-Alive"}, {"Range", range}}; } + transport::http::HTTPClientConnection connection; + if (!conf.producer_certificate.empty()) { - connection.setCertificate(conf.producer_certificate); + std::shared_ptr verifier = + std::make_shared( + conf.producer_certificate); + connection.setVerifier(verifier); } t1 = std::chrono::system_clock::now(); diff --git a/apps/http-proxy/src/forwarder_interface.cc b/apps/http-proxy/src/forwarder_interface.cc index d80939b8b..7d8235ac6 100644 --- a/apps/http-proxy/src/forwarder_interface.cc +++ b/apps/http-proxy/src/forwarder_interface.cc @@ -13,9 +13,8 @@ * limitations under the License. */ -#include - #include +#include #include #include diff --git a/apps/http-proxy/src/http_proxy.cc b/apps/http-proxy/src/http_proxy.cc index 262fcb8e1..c252afe88 100644 --- a/apps/http-proxy/src/http_proxy.cc +++ b/apps/http-proxy/src/http_proxy.cc @@ -225,6 +225,7 @@ class HTTPClientConnectionCallback : interface::ConsumerSocket::ReadCallback { const char* reply = nullptr; if (result) { reply = HTTPMessageFastParser::http_ok; + prefix_hash_ = configured_prefix; } else { reply = HTTPMessageFastParser::http_failed; } diff --git a/apps/http-proxy/src/icn_receiver.cc b/apps/http-proxy/src/icn_receiver.cc index 8823907dc..23e5b5623 100644 --- a/apps/http-proxy/src/icn_receiver.cc +++ b/apps/http-proxy/src/icn_receiver.cc @@ -189,7 +189,7 @@ void AsyncConsumerProducer::publishContent(const uint8_t* data, } it->second.first += - producer_socket_.produce(name, data, size, is_last, start_suffix); + producer_socket_.produceStream(name, data, size, is_last, start_suffix); if (is_last) { it->second.second = false; diff --git a/cmake/Modules/BuildMacros.cmake b/cmake/Modules/BuildMacros.cmake index 7119541dd..4c55f32fe 100644 --- a/cmake/Modules/BuildMacros.cmake +++ b/cmake/Modules/BuildMacros.cmake @@ -21,7 +21,7 @@ macro(build_executable exec) cmake_parse_arguments(ARG "NO_INSTALL" "COMPONENT" - "SOURCES;LINK_LIBRARIES;DEPENDS;INCLUDE_DIRS;DEFINITIONS;LINK_FLAGS" + "SOURCES;LINK_LIBRARIES;DEPENDS;INCLUDE_DIRS;DEFINITIONS;COMPILE_OPTIONS;LINK_FLAGS" ${ARGN} ) @@ -48,6 +48,10 @@ macro(build_executable exec) add_dependencies(${exec}-bin ${ARG_DEPENDS}) endif() + if (ARG_COMPILE_OPTIONS) + target_compile_options(${exec}-bin ${ARG_COMPILE_OPTIONS}) + endif() + if(ARG_DEFINITIONS) target_compile_definitions(${exec}-bin PRIVATE ${ARG_DEFINITIONS}) endif() @@ -71,12 +75,17 @@ endmacro() macro(build_library lib) cmake_parse_arguments(ARG - "SHARED;STATIC;MODULE;NO_DEV" + "SHARED;STATIC;NO_DEV" "COMPONENT;" - "SOURCES;LINK_LIBRARIES;INSTALL_HEADERS;DEPENDS;INCLUDE_DIRS;DEFINITIONS;INSTALL_ROOT_DIR;INSTALL_FULL_PATH_DIR;EMPTY_PREFIX;" + "SOURCES;LINK_LIBRARIES;INSTALL_HEADERS;DEPENDS;INCLUDE_DIRS;DEFINITIONS;HEADER_ROOT_DIR;LIBRARY_ROOT_DIR;INSTALL_FULL_PATH_DIR;EMPTY_PREFIX;COMPILE_OPTIONS;VERSION" ${ARGN} ) + message(STATUS "Building library ${lib}") + + # Clear target_libs + unset(TARGET_LIBS) + if (ARG_SHARED) list(APPEND TARGET_LIBS ${lib}.shared @@ -91,13 +100,6 @@ macro(build_library lib) add_library(${lib}.static STATIC ${ARG_SOURCES}) endif() - if(ARG_MODULE) - list(APPEND TARGET_LIBS - ${lib}.module - ) - add_library(${lib}.module MODULE ${ARG_SOURCES}) - endif() - if(NOT ARG_COMPONENT) set(ARG_COMPONENT hicn) endif() @@ -135,13 +137,13 @@ macro(build_library lib) endif() if (WIN32) - target_compile_options(${library} PRIVATE) + target_compile_options(${library} PRIVATE ${ARG_COMPILE_OPTIONS}) set_target_properties(${library} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE ) else () - target_compile_options(${library} PRIVATE -Wall) + target_compile_options(${library} PRIVATE -Wall ${ARG_COMPILE_OPTIONS}) set_target_properties(${library} PROPERTIES OUTPUT_NAME ${lib} @@ -164,7 +166,14 @@ macro(build_library lib) ) endif() - set(INSTALL_LIB_PATH ${CMAKE_INSTALL_LIBDIR}) + if(ARG_VERSION) + set_target_properties(${library} + PROPERTIES + VERSION ${ARG_VERSION} + ) + endif() + + set(INSTALL_LIB_PATH "${CMAKE_INSTALL_LIBDIR}/${ARG_LIBRARY_ROOT_DIR}") if (ARG_INSTALL_FULL_PATH_DIR) set(INSTALL_LIB_PATH ${ARG_INSTALL_FULL_PATH_DIR}) @@ -185,8 +194,8 @@ macro(build_library lib) # install headers if(ARG_INSTALL_HEADERS) - if (NOT ARG_INSTALL_ROOT_DIR) - set(ARG_INSTALL_ROOT_DIR "hicn") + if (NOT ARG_HEADER_ROOT_DIR) + set(ARG_HEADER_ROOT_DIR "hicn") endif() list(APPEND local_comps @@ -204,7 +213,7 @@ macro(build_library lib) if ("${dir}" STREQUAL includes) set(dir "") endif() - if ("${dir}" STREQUAL ${ARG_INSTALL_ROOT_DIR}) + if ("${dir}" STREQUAL ${ARG_HEADER_ROOT_DIR}) set(dir "") endif() else() @@ -217,12 +226,56 @@ macro(build_library lib) endif() install( FILES ${file} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${ARG_INSTALL_ROOT_DIR}/${dir} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${ARG_HEADER_ROOT_DIR}/${dir} COMPONENT ${COMPONENT} ) endforeach() endif() endmacro() +macro (build_module module) + cmake_parse_arguments(ARG + "SHARED;STATIC;NO_DEV" + "COMPONENT;" + "SOURCES;LINK_LIBRARIES;INSTALL_HEADERS;DEPENDS;INCLUDE_DIRS;DEFINITIONS;HEADER_ROOT_DIR;LIBRARY_ROOT_DIR;INSTALL_FULL_PATH_DIR;EMPTY_PREFIX;COMPILE_OPTIONS;VERSION" + ${ARGN} + ) + + message(STATUS "Building module ${module}") + + build_library(${module} + SHARED + SOURCES ${ARG_SOURCES} + LINK_LIBRARIES ${ARG_LINK_LIBRARIES} + INSTALL_HEADERS ${ARG_INSTALL_HEADERS} + DEPENDS ${ARG_DEPENDS} + COMPONENT lib${LIBTRANSPORT} + INCLUDE_DIRS ${ARG_INCLUDE_DIRS} + HEADER_ROOT_DIR ${ARG_HEADER_ROOT_DIR} + LIBRARY_ROOT_DIR ${ARG_LIBRARY_ROOT_DIR} + INSTALL_FULL_PATH_DIR ${ARG_INSTALL_FULL_PATH_DIR} + DEFINITIONS ${ARG_DEFINITIONS} + EMPTY_PREFIX ${ARG_EMPTY_PREFIX} + COMPILE_OPTIONS ${ARG_COMPILE_OPTIONS} + VERSION ${ARG_VERSION} + ) + + if (${CMAKE_SYSTEM_NAME} MATCHES Darwin) + set(LINK_FLAGS "-Wl,-undefined,dynamic_lookup") + elseif(${CMAKE_SYSTEM_NAME} MATCHES Linux) + set(LINK_FLAGS "-Wl,-unresolved-symbols=ignore-all") + else() + message(FATAL_ERROR "Trying to build module on a not supportd platform. Aborting.") + endif() + + set_target_properties(${module}.shared + PROPERTIES + LINKER_LANGUAGE C + PREFIX "" + LINK_FLAGS ${LINK_FLAGS} + ) + +endmacro(build_module) + include(IosMacros) include(WindowsMacros) diff --git a/cmake/Modules/FindLibFec.cmake b/cmake/Modules/FindLibFec.cmake new file mode 100644 index 000000000..f8b33ad6b --- /dev/null +++ b/cmake/Modules/FindLibFec.cmake @@ -0,0 +1,24 @@ +set(LIBFEC_SEARCH_PATH_LIST + ${LIBFEC_HOME} + $ENV{DEPENDENCIES} + $ENV{LIBFEC_HOME} + /usr/local + /opt + /usr + ) + +find_path(LIBFEC_INCLUDE_DIR fec/version.h + HINTS ${LIBFEC_SEARCH_PATH_LIST} + PATH_SUFFIXES include + DOC "Find the LibFec includes" ) + +find_library(LIBFEC_LIBRARY NAMES fec + HINTS ${LIBFEC_SEARCH_PATH_LIST} + PATH_SUFFIXES lib + DOC "Find the LibFec libraries" ) + +set(LIBFEC_LIBRARIES ${LIBFEC_LIBRARY}) +set(LIBFEC_INCLUDE_DIRS ${LIBFEC_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibFec DEFAULT_MSG LIBFEC_LIBRARY LIBFEC_INCLUDE_DIR) \ No newline at end of file diff --git a/cmake/Modules/FindLibRely.cmake b/cmake/Modules/FindLibRely.cmake new file mode 100644 index 000000000..4b8960041 --- /dev/null +++ b/cmake/Modules/FindLibRely.cmake @@ -0,0 +1,24 @@ +set(LIBRELY_SEARCH_PATH_LIST + ${LIBRELY_HOME} + $ENV{DEPENDENCIES} + $ENV{LIBRELY_HOME} + /usr/local + /opt + /usr + ) + +find_path(LIBRELY_INCLUDE_DIR rely/version.hpp + HINTS ${LIBRELY_SEARCH_PATH_LIST} + PATH_SUFFIXES include + DOC "Find the LibRely includes" ) + +find_library(LIBRELY_LIBRARY NAMES rely + HINTS ${LIBRELY_SEARCH_PATH_LIST} + PATH_SUFFIXES lib + DOC "Find the LibRely libraries" ) + +set(LIBRELY_LIBRARIES ${LIBRELY_LIBRARY}) +set(LIBRELY_INCLUDE_DIRS ${LIBRELY_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibRely DEFAULT_MSG LIBRELY_LIBRARY LIBRELY_INCLUDE_DIR) \ No newline at end of file diff --git a/cmake/Modules/FindLibconfig++.cmake b/cmake/Modules/FindLibconfig++.cmake new file mode 100644 index 000000000..865f75078 --- /dev/null +++ b/cmake/Modules/FindLibconfig++.cmake @@ -0,0 +1,43 @@ +set(LIBCONFIG_SEARCH_PATH_LIST + ${LIBCONFIG_HOME} + $ENV{LIBCONFIG_HOME} + /usr/local + /opt + /usr +) + +find_path(LIBCONFIG_INCLUDE_DIR libconfig.h++ + HINTS ${LIBCONFIG_SEARCH_PATH_LIST} + PATH_SUFFIXES include + DOC "Find the libconfig include" +) + +if (WIN32) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + find_library(LIBCONFIG_LIBRARY NAMES libconfig.lib + HINTS ${LIBCONFIG_SEARCH_PATH_LIST} + PATH_SUFFIXES lib/x64 + DOC "Find the libconfig libraries" + ) + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + find_library(LIBCONFIG_LIBRARY NAMES libconfig.lib + HINTS ${LIBCONFIG_SEARCH_PATH_LIST} + PATH_SUFFIXES lib/x32 + DOC "Find the libconfig libraries" + ) + endif() +else() + find_library(LIBCONFIG_CPP_LIBRARY NAMES config++ + HINTS ${LIBCONFIG_SEARCH_PATH_LIST} + PATH_SUFFIXES lib + DOC "Find the libconfig++ libraries" + ) +endif() + +set(LIBCONFIG_CPP_LIBRARIES ${LIBCONFIG_CPP_LIBRARY}) +set(LIBCONFIG_INCLUDE_DIRS ${LIBCONFIG_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libconfig++ LIBCONFIG_CPP_LIBRARIES LIBCONFIG_INCLUDE_DIRS) + +mark_as_advanced(LIBCONFIG_CPP_LIBRARIES LIBCONFIG_INCLUDE_DIRS) diff --git a/cmake/Modules/FindLibconfig.cmake b/cmake/Modules/FindLibconfig.cmake new file mode 100644 index 000000000..55d2a0fad --- /dev/null +++ b/cmake/Modules/FindLibconfig.cmake @@ -0,0 +1,43 @@ +set(LIBCONFIG_SEARCH_PATH_LIST + ${LIBCONFIG_HOME} + $ENV{LIBCONFIG_HOME} + /usr/local + /opt + /usr +) + +find_path(LIBCONFIG_INCLUDE_DIR libconfig.h + HINTS ${LIBCONFIG_SEARCH_PATH_LIST} + PATH_SUFFIXES include + DOC "Find the libconfig include" +) + +if (WIN32) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + find_library(LIBCONFIG_LIBRARY NAMES libconfig.lib + HINTS ${LIBCONFIG_SEARCH_PATH_LIST} + PATH_SUFFIXES lib/x64 + DOC "Find the libconfig libraries" + ) + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + find_library(LIBCONFIG_LIBRARY NAMES libconfig.lib + HINTS ${LIBCONFIG_SEARCH_PATH_LIST} + PATH_SUFFIXES lib/x32 + DOC "Find the libconfig libraries" + ) + endif() +else() + find_library(LIBCONFIG_LIBRARY NAMES config + HINTS ${LIBCONFIG_SEARCH_PATH_LIST} + PATH_SUFFIXES lib + DOC "Find the libconfig libraries" + ) +endif() + +set(LIBCONFIG_LIBRARIES ${LIBCONFIG_LIBRARY}) +set(LIBCONFIG_INCLUDE_DIRS ${LIBCONFIG_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libconfig LIBCONFIG_LIBRARIES LIBCONFIG_INCLUDE_DIRS) + +mark_as_advanced(LIBCONFIG_LIBRARIES LIBCONFIG_INCLUDE_DIRS) diff --git a/cmake/Modules/FindLibhicn.cmake b/cmake/Modules/FindLibhicn.cmake index 38775be6a..5f241a6cd 100644 --- a/cmake/Modules/FindLibhicn.cmake +++ b/cmake/Modules/FindLibhicn.cmake @@ -44,7 +44,7 @@ find_library(HICN_LIBRARY NAMES hicn set(HICN_LIBRARIES ${HICN_LIBRARY}) if (${CMAKE_SYSTEM_NAME} STREQUAL "Android") -set(HICN_LIBRARIES ${HICN_LIBRARIES} log) + set(HICN_LIBRARIES ${HICN_LIBRARIES} log) endif() set(HICN_INCLUDE_DIRS ${HICN_INCLUDE_DIR}) diff --git a/cmake/Modules/GTestImport.cmake b/cmake/Modules/GTestImport.cmake index 4e2e18dc9..d9d182578 100644 --- a/cmake/Modules/GTestImport.cmake +++ b/cmake/Modules/GTestImport.cmake @@ -18,6 +18,11 @@ include(ExternalProject) ExternalProject_Add(gtest URL https://github.com/google/googletest/archive/v1.10.x.zip PREFIX ${CMAKE_BINARY_DIR}/gtest + BUILD_BYPRODUCTS + ${CMAKE_BINARY_DIR}/gtest/src/gtest-build/lib/libgmock_main.a + ${CMAKE_BINARY_DIR}/gtest/src/gtest-build/lib/libgmock.a + ${CMAKE_BINARY_DIR}/gtest/src/gtest-build/lib/libgtest_main.a + ${CMAKE_BINARY_DIR}/gtest/src/gtest-build/lib/libgtest.a INSTALL_COMMAND "" ) @@ -37,4 +42,4 @@ macro(add_test_internal test) endif() endmacro(add_test_internal) -enable_testing() \ No newline at end of file +enable_testing() diff --git a/cmake/Modules/ServiceScript.cmake b/cmake/Modules/ServiceScript.cmake index 110aa816b..8e7056a5a 100644 --- a/cmake/Modules/ServiceScript.cmake +++ b/cmake/Modules/ServiceScript.cmake @@ -39,4 +39,4 @@ cmake_parse_arguments(ARG if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND ${CMAKE_INSTALL_PREFIX} STREQUAL "/usr") install (FILES ${script} DESTINATION ${SYSTEMD_SERVICE_FOLDER} COMPONENT ${ARG_COMPONENT}) endif() -endmacro(install_service_script) \ No newline at end of file +endmacro(install_service_script) diff --git a/ctrl/facemgr/CMakeLists.txt b/ctrl/facemgr/CMakeLists.txt index 8b24810fb..290f96fa0 100644 --- a/ctrl/facemgr/CMakeLists.txt +++ b/ctrl/facemgr/CMakeLists.txt @@ -58,7 +58,7 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH ON) -find_package_wrapper(Config REQUIRED) +find_package_wrapper(Libconfig REQUIRED) find_package_wrapper(LibEvent REQUIRED) set(FACEMGR facemgr CACHE INTERNAL "" FORCE) diff --git a/ctrl/facemgr/src/CMakeLists.txt b/ctrl/facemgr/src/CMakeLists.txt index 4bcad475d..3650ef4f5 100644 --- a/ctrl/facemgr/src/CMakeLists.txt +++ b/ctrl/facemgr/src/CMakeLists.txt @@ -64,7 +64,7 @@ set(LIBRARIES m ${HICN_LIBRARIES} ${LIBHICNCTRL_LIBRARIES} - ${CONFIG_LIBRARY} + ${LIBCONFIG_LIBRARIES} ${LIBEVENT_LIBRARY} ) @@ -106,7 +106,7 @@ if (DISABLE_SHARED_LIBRARIES) LINK_LIBRARIES ${LIBRARIES} COMPONENT ${FACEMGR} INCLUDE_DIRS ${INCLUDE_DIRS} - INSTALL_ROOT_DIR hicn + HEADER_ROOT_DIR hicn DEFINITIONS ${COMPILER_DEFINITIONS} ) else () @@ -118,7 +118,7 @@ else () LINK_LIBRARIES ${LIBRARIES} COMPONENT ${FACEMGR} INCLUDE_DIRS ${INCLUDE_DIRS} - INSTALL_ROOT_DIR hicn + HEADER_ROOT_DIR hicn DEFINITIONS ${COMPILER_DEFINITIONS} ) endif () diff --git a/ctrl/libhicnctrl/src/CMakeLists.txt b/ctrl/libhicnctrl/src/CMakeLists.txt index 00661a2a0..f5d3c49a3 100644 --- a/ctrl/libhicnctrl/src/CMakeLists.txt +++ b/ctrl/libhicnctrl/src/CMakeLists.txt @@ -69,7 +69,7 @@ build_library(${LIBHICNCTRL} DEPENDS ${DEPENDENCIES} COMPONENT ${LIBHICNCTRL_COMPONENT} INCLUDE_DIRS ${INCLUDE_DIRS} - INSTALL_ROOT_DIR hicn + HEADER_ROOT_DIR hicn DEFINITIONS ${COMPILER_DEFINITIONS} ) diff --git a/ctrl/libhicnctrl/src/hicn_plugin_api.c b/ctrl/libhicnctrl/src/hicn_plugin_api.c index 0b0a9ad54..231c491ba 100644 --- a/ctrl/libhicnctrl/src/hicn_plugin_api.c +++ b/ctrl/libhicnctrl/src/hicn_plugin_api.c @@ -40,8 +40,18 @@ #include #include #include + + +#if __GNUC__ >= 9 +#pragma GCC diagnostic ignored "-Waddress-of-packed-member" +#endif + #include +#if __GNUC__ >= 9 +#pragma GCC diagnostic pop +#endif + #define APP_NAME "hicn_plugin" #define MAX_OUTSTANDING_REQUESTS 4 #define RESPONSE_QUEUE_SIZE 2 diff --git a/docs/source/index.rst b/docs/source/index.rst index 6d48ae646..99ea39afa 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -22,7 +22,6 @@ on VPP and a client stack is provided to support any mobile and desktop operatin control telemetry utils - packethicn apps diff --git a/extras/CMakeLists.txt b/extras/CMakeLists.txt index 1cb671c87..88668df0b 100644 --- a/extras/CMakeLists.txt +++ b/extras/CMakeLists.txt @@ -23,7 +23,6 @@ set (DESTDIR ${CMAKE_BINARY_DIR}/extras-root) # add_subdirectory(router-plugin) add_subdirectory(libmemif) -add_subdirectory(packethicn) # add_subdirectory(libyang) # add_subdirectory(sysrepo) diff --git a/extras/libmemif/CMakeLists2.patch b/extras/libmemif/CMakeLists2.patch index 20b67ea54..38d520bd9 100644 --- a/extras/libmemif/CMakeLists2.patch +++ b/extras/libmemif/CMakeLists2.patch @@ -37,6 +37,6 @@ build_library(${LIBMEMIF} SOURCES ${MEMIF_SOURCES} INSTALL_HEADERS ${MEMIF_HEADERS} LINK_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} - INSTALL_ROOT_DIR memif + HEADER_ROOT_DIR memif COMPONENT ${COMPONENT} ) \ No newline at end of file diff --git a/hicn-light/src/hicn/CMakeLists.txt b/hicn-light/src/hicn/CMakeLists.txt index 82de74ac7..639bfa179 100644 --- a/hicn-light/src/hicn/CMakeLists.txt +++ b/hicn-light/src/hicn/CMakeLists.txt @@ -56,7 +56,7 @@ build_library(${LIBHICN_LIGHT} DEPENDS ${DEPENDENCIES} COMPONENT ${HICN_LIGHT} INCLUDE_DIRS ${HICN_LIGHT_INCLUDE_DIRS} - INSTALL_ROOT_DIR hicn + HEADER_ROOT_DIR hicn DEFINITIONS ${COMPILER_DEFINITIONS} ) diff --git a/hicn-light/src/hicn/core/message.c b/hicn-light/src/hicn/core/message.c index 5d0d04ae4..c28938320 100644 --- a/hicn-light/src/hicn/core/message.c +++ b/hicn-light/src/hicn/core/message.c @@ -245,7 +245,8 @@ uint32_t message_GetPathLabel(const Message *message) { void message_SetPathLabel(Message *message, uint32_t label) { parcAssertNotNull(message, "Parameter must be non-null"); - messageHandler_SetPathLabel(message->messageHead, label); + messageHandler_SetPathLabel(message->messageHead, + messageHandler_GetPathLabel(message->messageHead), label); } void message_UpdatePathLabel(Message *message, uint8_t outFace) { diff --git a/hicn-light/src/hicn/core/messageHandler.h b/hicn-light/src/hicn/core/messageHandler.h index f6b3e4e36..b41c9a7f0 100644 --- a/hicn-light/src/hicn/core/messageHandler.h +++ b/hicn-light/src/hicn/core/messageHandler.h @@ -334,7 +334,7 @@ static inline bool messageHandler_IsInterest(const uint8_t *message) { if (!messageHandler_IsTCP(message)) return false; bool flag; - hicn_packet_test_ece((hicn_header_t *)message, + hicn_packet_test_ece(HF_INET6_TCP, (hicn_header_t *)message, &flag); // ECE flag is set to 0 in interest packets if (flag == false) return true; return false; @@ -344,7 +344,7 @@ static inline bool messageHandler_IsData(const uint8_t *message) { if (!messageHandler_IsTCP(message)) return false; bool flag; - hicn_packet_test_ece((hicn_header_t *)message, + hicn_packet_test_ece(HF_INET6_TCP, (hicn_header_t *)message, &flag); // ECE flag is set to 1 in data packets if (flag == true) return true; return false; @@ -505,13 +505,10 @@ static inline uint32_t messageHandler_GetPathLabel(const uint8_t *message) { } static inline void messageHandler_SetPathLabel(uint8_t *message, + uint32_t old_path_label, uint32_t new_path_label) { if (!messageHandler_IsTCP(message)) return; - uint32_t old_path_label; - int res = hicn_data_get_path_label((hicn_header_t *)message, &old_path_label); - if (res < 0) return; - hicn_data_set_path_label((hicn_header_t *)message, new_path_label); messageHandler_UpdateTCPCheckSum(message, (uint16_t *)&old_path_label, @@ -527,20 +524,11 @@ static inline void messageHandler_UpdatePathLabel(uint8_t *message, uint32_t pl_new_32bit = (uint32_t)((((pl_old_8bit << 1) | (pl_old_8bit >> 7)) ^ outFace) << 24UL); - hicn_data_set_path_label((hicn_header_t *)message, pl_new_32bit); - - messageHandler_UpdateTCPCheckSum(message, (uint16_t *)&pl_old_32bit, - (uint16_t *)&pl_new_32bit, 2); + messageHandler_SetPathLabel(message, pl_old_32bit, pl_new_32bit); } static inline void messageHandler_ResetPathLabel(uint8_t *message) { - if (!messageHandler_IsTCP(message)) return; - - uint32_t pl_old_32bit = messageHandler_GetPathLabel(message); - uint32_t pl_new_32bit = 0; - hicn_data_set_path_label((hicn_header_t *)message, pl_new_32bit); - messageHandler_UpdateTCPCheckSum(message, (uint16_t *)&pl_old_32bit, - (uint16_t *)&pl_new_32bit, 2); + messageHandler_SetPathLabel(message, messageHandler_GetPathLabel(message), 0); } static inline uint16_t messageHandler_GetInterestLifetime( @@ -703,7 +691,7 @@ static inline uint8_t * messageHandler_CreateProbePacket(hicn_format_t format, hicn_packet_init_header(format, (hicn_header_t *) pkt); - hicn_packet_set_dst_port((hicn_header_t *) pkt, BFD_PORT); + hicn_packet_set_dst_port(format, (hicn_header_t *) pkt, BFD_PORT); hicn_interest_set_lifetime ((hicn_header_t *) pkt, probe_lifetime); return pkt; @@ -721,10 +709,10 @@ static inline void messageHandler_CreateProbeReply(uint8_t * probe, uint16_t src_prt; uint16_t dst_prt; - hicn_packet_get_src_port((const hicn_header_t *) probe, &src_prt); - hicn_packet_get_dst_port((const hicn_header_t *) probe, &dst_prt); - hicn_packet_set_src_port((hicn_header_t *) probe, dst_prt); - hicn_packet_set_dst_port((hicn_header_t *) probe, src_prt); + hicn_packet_get_src_port(format, (const hicn_header_t *) probe, &src_prt); + hicn_packet_get_dst_port(format, (const hicn_header_t *) probe, &dst_prt); + hicn_packet_set_src_port(format, (hicn_header_t *) probe, dst_prt); + hicn_packet_set_dst_port(format, (hicn_header_t *) probe, src_prt); hicn_data_set_name (format, (hicn_header_t *) probe, &probe_name); hicn_data_set_locator (format, (hicn_header_t *) probe, &probe_locator); @@ -746,8 +734,8 @@ static inline void messageHandler_SetProbeName(uint8_t * probe, hicn_format_t fo static inline bool messageHandler_IsAProbe(const uint8_t *packet){ uint16_t src_prt; uint16_t dst_prt; - hicn_packet_get_src_port ((const hicn_header_t *) packet, &src_prt); - hicn_packet_get_dst_port ((const hicn_header_t *) packet, &dst_prt); + hicn_packet_get_src_port (HF_INET6_TCP, (const hicn_header_t *) packet, &src_prt); + hicn_packet_get_dst_port (HF_INET6_TCP, (const hicn_header_t *) packet, &dst_prt); if(dst_prt == BFD_PORT){ //interest probe diff --git a/hicn-plugin/src/CMakeLists.txt b/hicn-plugin/src/CMakeLists.txt index 14eb23d70..d81ed22b7 100644 --- a/hicn-plugin/src/CMakeLists.txt +++ b/hicn-plugin/src/CMakeLists.txt @@ -177,13 +177,6 @@ endif (NOT CMAKE_BUILD_TYPE) SET(HICN_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} CACHE STRING "hicn_install_prefix") -if (CMAKE_BUILD_TYPE STREQUAL "Release") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wall -march=native -O3 -g -Wno-address-of-packed-member") -elseif (CMAKE_BUILD_TYPE STREQUAL "Debug") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -march=native -O0 -g -Wno-address-of-packed-member") - add_definitions(-DCLIB_DEBUG -fPIC -fstack-protector-all) -endif() - file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/hicn) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/vapi) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/vnet/ip) @@ -259,12 +252,19 @@ set_target_properties(hicn_plugin PROPERTIES LINKER_LANGUAGE C INSTALL_RPATH ${VPP_INSTALL_PLUGIN} + COMPILE_FLAGS "-march=native -Wno-address-of-packed-member" PREFIX "") set_target_properties(hicn_api_test_plugin PROPERTIES LINKER_LANGUAGE C + COMPILE_FLAGS "-march=native -Wno-address-of-packed-member" PREFIX "") +if (${CMAKE_BUILD_TYPE} MATCHES "Debug") + target_compile_definitions(hicn_plugin PRIVATE "CLIB_DEBUG") + target_compile_definitions(hicn_api_test_plugin PRIVATE "CLIB_DEBUG") +endif() + message (STATUS "hicn-plugin variable ${HICN_PLUGIN}") install(DIRECTORY diff --git a/hicn-plugin/src/faces/face.c b/hicn-plugin/src/faces/face.c index 7ddf38910..d62054365 100644 --- a/hicn-plugin/src/faces/face.c +++ b/hicn-plugin/src/faces/face.c @@ -24,6 +24,7 @@ dpo_id_t *face_dpo_vec; hicn_face_vft_t *face_vft_vec; char **face_type_names_vec; +u8 pl_index = 1; hicn_face_t *hicn_dpoi_face_pool; diff --git a/hicn-plugin/src/faces/face.h b/hicn-plugin/src/faces/face.h index 234c3fcc2..95c78b206 100644 --- a/hicn-plugin/src/faces/face.h +++ b/hicn-plugin/src/faces/face.h @@ -189,6 +189,9 @@ extern hicn_face_vft_t *face_vft_vec; /* Vector holding the set of face names */ extern char **face_type_names_vec; +/* Pathlabel counter */ +extern u8 pl_index; + /* First face type registered in the sytem.*/ extern dpo_type_t first_type; @@ -526,7 +529,7 @@ hicn_iface_add (ip46_address_t * nat_address, int sw_if, face->dpo.dpoi_proto = DPO_PROTO_NONE; face->dpo.dpoi_index = adj_index; face->dpo.dpoi_next_node = 0; - face->pl_id = (u16) 0; + face->pl_id = pl_index++; face->flags = HICN_FACE_FLAGS_IFACE; face->locks = 1; diff --git a/hicn-plugin/src/faces/iface_node.c b/hicn-plugin/src/faces/iface_node.c index 8d8500995..82c0f75b2 100644 --- a/hicn-plugin/src/faces/iface_node.c +++ b/hicn-plugin/src/faces/iface_node.c @@ -535,8 +535,11 @@ hicn_rewrite_iface_data4 (vlib_main_t *vm, vlib_buffer_t *b0, ip46_address_t temp_addr; ip46_address_reset (&temp_addr); hicn_type_t type = hicn_get_buffer (b0)->type; + u8 flags = hicn_get_buffer (b0)->flags; + u8 reset_pl = flags & HICN_BUFFER_FLAGS_FROM_CS; int ret = hicn_ops_vft[type.l1]->rewrite_data ( - type, &hicn->protocol, &(iface->nat_addr), &(temp_addr), iface->pl_id); + type, &hicn->protocol, &(iface->nat_addr), &(temp_addr), + iface->pl_id, reset_pl); if (ret == HICN_LIB_ERROR_REWRITE_CKSUM_REQUIRED) { ensure_offload_flags (b0, 1 /* is_v4 */); @@ -567,8 +570,11 @@ hicn_rewrite_iface_data6 (vlib_main_t *vm, vlib_buffer_t *b0, ip46_address_t temp_addr; ip46_address_reset (&temp_addr); hicn_type_t type = hicn_get_buffer (b0)->type; + u8 flags = hicn_get_buffer (b0)->flags; + u8 reset_pl = flags & HICN_BUFFER_FLAGS_FROM_CS; int ret = hicn_ops_vft[type.l1]->rewrite_data ( - type, &hicn->protocol, &(iface->nat_addr), &(temp_addr), iface->pl_id); + type, &hicn->protocol, &(iface->nat_addr), &(temp_addr), + iface->pl_id, reset_pl); if (ret == HICN_LIB_ERROR_REWRITE_CKSUM_REQUIRED) { diff --git a/hicn-plugin/src/hicn.h b/hicn-plugin/src/hicn.h index 3d980bd49..437f91144 100644 --- a/hicn-plugin/src/hicn.h +++ b/hicn-plugin/src/hicn.h @@ -65,6 +65,7 @@ typedef u8 weight_t; #define HICN_BUFFER_FLAGS_PKT_LESS_TWO_CL 0x02 #define HICN_BUFFER_FLAGS_FROM_UDP4_TUNNEL 0x04 #define HICN_BUFFER_FLAGS_FROM_UDP6_TUNNEL 0x08 +#define HICN_BUFFER_FLAGS_FROM_CS 0x10 /* The following is stored in the opaque2 field in the vlib_buffer_t */ typedef struct diff --git a/hicn-plugin/src/interest_hitcs_node.c b/hicn-plugin/src/interest_hitcs_node.c index c2240513c..0212fa5f9 100644 --- a/hicn-plugin/src/interest_hitcs_node.c +++ b/hicn-plugin/src/interest_hitcs_node.c @@ -76,6 +76,9 @@ clone_from_cs (vlib_main_t *vm, u32 *bi0_cs, vlib_buffer_t *dest, u8 isv6) vlib_buffer_advance (cs_buf, buffer_advance); vlib_buffer_attach_clone (vm, dest, cs_buf); } + + /* Set fag for packet coming from CS */ + hicn_get_buffer (dest)->flags |= HICN_BUFFER_FLAGS_FROM_CS; } static uword diff --git a/hicn-plugin/vapi/CMakeLists.txt b/hicn-plugin/vapi/CMakeLists.txt index 03fd489ca..9464cd944 100644 --- a/hicn-plugin/vapi/CMakeLists.txt +++ b/hicn-plugin/vapi/CMakeLists.txt @@ -42,7 +42,7 @@ build_library(${SAFE_VAPI} LINK_LIBRARIES ${LIBRARIES} COMPONENT ${HICN_PLUGIN} INCLUDE_DIRS ${INCLUDE_DIRS} - INSTALL_ROOT_DIR / + HEADER_ROOT_DIR / DEFINITIONS ${COMPILER_DEFINITIONS} ) diff --git a/lib/.clang-format b/lib/.clang-format new file mode 100644 index 000000000..8b5c955ce --- /dev/null +++ b/lib/.clang-format @@ -0,0 +1,21 @@ +# Minimal clang-format version is 8 + +BasedOnStyle: GNU +UseTab: Always +SpaceAfterCStyleCast: true +SortIncludes: false +AlignConsecutiveMacros: true +BreakBeforeTernaryOperators: false +BreakBeforeBinaryOperators: None +ContinuationIndentWidth: 2 + +ForEachMacros: + - 'clib_bitmap_foreach' + - 'pool_foreach' + - 'pool_foreach_index' + - 'vec_foreach' + - 'vec_foreach_backwards' + - 'vec_foreach_index' + - 'vec_foreach_index_backwards' + - 'vlib_foreach_rx_tx' + diff --git a/lib/includes/hicn/base.h b/lib/includes/hicn/base.h index f38001d1c..797912f91 100644 --- a/lib/includes/hicn/base.h +++ b/lib/includes/hicn/base.h @@ -22,6 +22,7 @@ #define HICN_BASE_H #include "common.h" +#include /* Default header fields */ #define HICN_DEFAULT_TTL 254 @@ -104,6 +105,17 @@ HICN_TYPE(int x, int y, int z, int t) #define HICN_TYPE_IPV6_ICMP_AH HICN_TYPE(IPPROTO_IPV6, IPPROTO_ICMPV6, IPPROTO_AH, IPPROTO_NONE) #define HICN_TYPE_NONE HICN_TYPE(IPPROTO_NONE, IPPROTO_NONE, IPPROTO_NONE, IPPROTO_NONE) +/** + * @brief Check if type is none. + * @return 1 if none, 0 otherwise + */ +always_inline int +hicn_type_is_none(hicn_type_t type) +{ + return (type.l1 == IPPROTO_NONE) && (type.l2 == IPPROTO_NONE) && + (type.l3 == IPPROTO_NONE) && (type.l4 == IPPROTO_NONE); +} + /** * @brief hICN Payload type * @@ -127,7 +139,7 @@ typedef enum * NOTE: this computation is not (yet) part of the hICN specification. */ -#define HICN_PATH_LABEL_MASK 0xF000 /* 1000 0000 0000 0000 */ +#define HICN_PATH_LABEL_MASK 0x000000ff #define HICN_PATH_LABEL_SIZE 8 /** @@ -143,8 +155,8 @@ update_pathlabel (hicn_pathlabel_t current_label, hicn_faceid_t face_id, hicn_pathlabel_t * new_label) { hicn_pathlabel_t pl_face_id = - (hicn_pathlabel_t) ((face_id & HICN_PATH_LABEL_MASK) >> - (16 - HICN_PATH_LABEL_SIZE)); + (hicn_pathlabel_t) (face_id & HICN_PATH_LABEL_MASK); + *new_label = ((current_label << 1) | (current_label >> (HICN_PATH_LABEL_SIZE - 1))) ^ pl_face_id; diff --git a/lib/includes/hicn/common.h b/lib/includes/hicn/common.h index 6904c6314..a5ca2878b 100644 --- a/lib/includes/hicn/common.h +++ b/lib/includes/hicn/common.h @@ -143,8 +143,17 @@ struct iovec #include // ip4_address_t #include // ip6_address_t + +#if __GNUC__ >= 9 +#pragma GCC diagnostic ignored "-Waddress-of-packed-member" +#endif + #include +#if __GNUC__ >= 9 +#pragma GCC diagnostic pop +#endif + #else diff --git a/lib/includes/hicn/compat.h b/lib/includes/hicn/compat.h index 2796983c6..35b6e6fc5 100644 --- a/lib/includes/hicn/compat.h +++ b/lib/includes/hicn/compat.h @@ -34,15 +34,15 @@ /* HICN format options */ #define HFO_INET 1 << 0 #define HFO_INET6 1 << 1 -#define HFO_TCP 1 << 2 +#define HFO_TCP 1 << 2 #define HFO_ICMP 1 << 3 -#define HFO_AH 1 << 4 +#define HFO_AH 1 << 4 #define _is_ipv4(format) ((format & HFO_INET)) #define _is_ipv6(format) ((format & HFO_INET6) >> 1) -#define _is_tcp(format) ((format & HFO_TCP) >> 2) -#define _is_icmp(format) ((format & HFO_ICMP) >> 3) -#define _is_ah(format) ((format & HFO_AH) >> 4) +#define _is_tcp(format) ((format & HFO_TCP) >> 2) +#define _is_icmp(format) ((format & HFO_ICMP) >> 3) +#define _is_ah(format) ((format & HFO_AH) >> 4) typedef enum { @@ -54,28 +54,30 @@ typedef enum HF_INET_TCP_AH = HFO_INET | HFO_TCP | HFO_AH, HF_INET6_TCP_AH = HFO_INET6 | HFO_TCP | HFO_AH, HF_INET_ICMP_AH = HFO_INET | HFO_ICMP | HFO_AH, - HF_INET6_ICMP_AH = HFO_INET6 | HFO_ICMP | HFO_AH + HF_INET6_ICMP_AH = HFO_INET6 | HFO_ICMP | HFO_AH, } hicn_format_t; /** - * Minimum required header length to determine the type and length of a supposed - * hICN packet. - * This should be equal to the maximum value over all possible hICN packet - * formats, and less than the minimum possible IP packet size. + * Minimum required header length to determine the type and length of a + * supposed hICN packet. This should be equal to the maximum value over all + * possible hICN packet formats, and less than the minimum possible IP packet + * size. */ -#define HICN_V6_MIN_HDR_LEN 6 /* bytes */ -#define HICN_V4_MIN_HDR_LEN 4 /* bytes */ +#define HICN_V6_MIN_HDR_LEN 6 /* bytes */ +#define HICN_V4_MIN_HDR_LEN 4 /* bytes */ -// #define HICN_MIN_HDR_LEN ((HICN_V6_MIN_HDR_LEN > HICN_V4_MIN_HDR_LEN) ? HICN_V6_MIN_HDR_LEN : HICN_V4_MIN_HDR_LEN) +// #define HICN_MIN_HDR_LEN ((HICN_V6_MIN_HDR_LEN > HICN_V4_MIN_HDR_LEN) ? +// HICN_V6_MIN_HDR_LEN : HICN_V4_MIN_HDR_LEN) #define HICN_MIN_HDR_LEN HICN_V6_MIN_HDR_LEN /** * @brief Parse packet headers and return hICN format * @param [in] format - hICN Format - * @param [in, out] packet - Buffer containing the hICN header to be initialized + * @param [in, out] packet - Buffer containing the hICN header to be + * initialized * @return hICN error code */ -int hicn_packet_init_header (hicn_format_t format, hicn_header_t * packet); +int hicn_packet_init_header (hicn_format_t format, hicn_header_t *packet); /** * @brief Parse packet headers and return hICN format @@ -83,8 +85,8 @@ int hicn_packet_init_header (hicn_format_t format, hicn_header_t * packet); * @param [out] format - hICN format * @return hICN error code */ -int hicn_packet_get_format (const hicn_header_t * packet, - hicn_format_t * format); +int hicn_packet_get_format (const hicn_header_t *packet, + hicn_format_t *format); /** * @brief Update checksums in packet headers @@ -92,19 +94,18 @@ int hicn_packet_get_format (const hicn_header_t * packet, * @param [in,out] packet - packet header * @return hICN error code */ -int hicn_packet_compute_checksum (hicn_format_t format, - hicn_header_t * packet); +int hicn_packet_compute_checksum (hicn_format_t format, hicn_header_t *packet); /** - * @brief compute the checksum of the packet header, adding init_sum to the final value + * @brief compute the checksum of the packet header, adding init_sum to the + * final value * @param [in] format - hICN format * @param [in,out] packet - packet header * @param [in] init_sum - value to add to the final checksum * @return hICN error code */ int hicn_packet_compute_header_checksum (hicn_format_t format, - hicn_header_t * packet, - u16 init_sum); + hicn_header_t *packet, u16 init_sum); /** * @brief Verify checksums in packet headers @@ -112,8 +113,9 @@ int hicn_packet_compute_header_checksum (hicn_format_t format, * @param [in,out] packet - packet header * @return hICN error code */ -int hicn_packet_check_integrity (hicn_format_t format, - hicn_header_t * packet); +int hicn_packet_check_integrity_no_payload (hicn_format_t format, + hicn_header_t *packet, + u16 init_sum); // this is not accounted here /** @@ -123,7 +125,7 @@ int hicn_packet_check_integrity (hicn_format_t format, * @return hICN error code */ int hicn_packet_get_header_length_from_format (hicn_format_t format, - size_t * header_length); + size_t *header_length); /** * @brief Return total length of hicn headers (before payload) @@ -133,8 +135,8 @@ int hicn_packet_get_header_length_from_format (hicn_format_t format, * @return hICN error code */ int hicn_packet_get_header_length (hicn_format_t format, - const hicn_header_t * packet, - size_t * header_length); + const hicn_header_t *packet, + size_t *header_length); /** * @brief Return payload length @@ -144,8 +146,8 @@ int hicn_packet_get_header_length (hicn_format_t format, * @return hICN error code */ int hicn_packet_get_payload_length (hicn_format_t format, - const hicn_header_t * packet, - size_t * payload_length); + const hicn_header_t *packet, + size_t *payload_length); /** * @brief Sets payload length @@ -155,7 +157,7 @@ int hicn_packet_get_payload_length (hicn_format_t format, * @return hICN error code */ int hicn_packet_set_payload_length (hicn_format_t format, - hicn_header_t * packet, + hicn_header_t *packet, const size_t payload_length); /** @@ -164,8 +166,8 @@ int hicn_packet_set_payload_length (hicn_format_t format, * @param [in] packet_2 - Second packet * @return 0 if both packets are considered equal, any other value otherwise. */ -int hicn_packet_compare (const hicn_header_t * packet1, - const hicn_header_t * packet2); +int hicn_packet_compare (const hicn_header_t *packet1, + const hicn_header_t *packet2); /** * @brief Retrieve the name of an interest/data packet @@ -176,8 +178,8 @@ int hicn_packet_compare (const hicn_header_t * packet1, * data packet (0) * @return hICN error code */ -int hicn_packet_get_name (hicn_format_t format, const hicn_header_t * packet, - hicn_name_t * name, u8 is_interest); +int hicn_packet_get_name (hicn_format_t format, const hicn_header_t *packet, + hicn_name_t *name, u8 is_interest); /** * @brief Sets the name of an interest/data packet @@ -188,8 +190,8 @@ int hicn_packet_get_name (hicn_format_t format, const hicn_header_t * packet, * data packet (0) * @return hICN error code */ -int hicn_packet_set_name (hicn_format_t format, hicn_header_t * packet, - const hicn_name_t * name, u8 is_interest); +int hicn_packet_set_name (hicn_format_t format, hicn_header_t *packet, + const hicn_name_t *name, u8 is_interest); /** * @brief Sets the payload of a packet @@ -203,8 +205,8 @@ int hicn_packet_set_name (hicn_format_t format, hicn_header_t * packet, * - The buffer holding payload is assumed sufficiently large * - This function updates header fields with the new length, but no checksum. */ -int hicn_packet_set_payload (hicn_format_t format, hicn_header_t * packet, - const u8 * payload, u16 payload_length); +int hicn_packet_set_payload (hicn_format_t format, hicn_header_t *packet, + const u8 *payload, u16 payload_length); /** * @brief Retrieves the payload of a packet @@ -212,16 +214,17 @@ int hicn_packet_set_payload (hicn_format_t format, hicn_header_t * packet, * @param [in] packet - packet header * @param [out] payload - pointer to buffer for storing the result * @param [out] payload_length - size of the retreived payload - * @param [in] hard_copy - Flag : if true (eg. 1), a copy of the payload is made - * into the payload buffer, otherwise (0) the pointer is changed to point to the payload offset in the packet. + * @param [in] hard_copy - Flag : if true (eg. 1), a copy of the payload is + * made into the payload buffer, otherwise (0) the pointer is changed to point + * to the payload offset in the packet. * @return hICN error code * * NOTE: * - The buffer holding payload is assumed sufficiently large */ -int hicn_packet_get_payload (hicn_format_t format, - const hicn_header_t * packet, u8 ** payload, - size_t * payload_size, bool hard_copy); +int hicn_packet_get_payload (hicn_format_t format, const hicn_header_t *packet, + u8 **payload, size_t *payload_size, + bool hard_copy); /** * @brief Retrieve the locator of an interest / data packet @@ -232,9 +235,8 @@ int hicn_packet_get_payload (hicn_format_t format, * data packet (0) * @return hICN error code */ -int hicn_packet_get_locator (hicn_format_t format, - const hicn_header_t * packet, - ip_address_t * prefix, bool is_interest); +int hicn_packet_get_locator (hicn_format_t format, const hicn_header_t *packet, + ip_address_t *prefix, bool is_interest); /** * @brief Sets the locator of an interest / data packet @@ -245,9 +247,8 @@ int hicn_packet_get_locator (hicn_format_t format, * data packet (0) * @return hICN error code */ -int hicn_packet_set_locator (hicn_format_t format, hicn_header_t * packet, - const ip_address_t * prefix, - bool is_interest); +int hicn_packet_set_locator (hicn_format_t format, hicn_header_t *packet, + const ip_address_t *prefix, bool is_interest); /** * @brief Retrieves the signature size @@ -257,8 +258,8 @@ int hicn_packet_set_locator (hicn_format_t format, hicn_header_t * packet, * @return hICN error code */ int hicn_packet_get_signature_size (hicn_format_t format, - const hicn_header_t * packet, - size_t * bytes); + const hicn_header_t *packet, + size_t *bytes); /** * @brief Sets the signature size @@ -268,7 +269,7 @@ int hicn_packet_get_signature_size (hicn_format_t format, * @return hICN error code */ int hicn_packet_set_signature_size (hicn_format_t format, - hicn_header_t * packet, size_t bytes); + hicn_header_t *packet, size_t bytes); /** * @brief Sets the signature size @@ -278,7 +279,7 @@ int hicn_packet_set_signature_size (hicn_format_t format, * @return hICN error code */ int hicn_packet_set_signature_timestamp (hicn_format_t format, - hicn_header_t * h, + hicn_header_t *h, uint64_t signature_timestamp); /** @@ -289,8 +290,8 @@ int hicn_packet_set_signature_timestamp (hicn_format_t format, * @return hICN error code */ int hicn_packet_get_signature_timestamp (hicn_format_t format, - const hicn_header_t * h, - uint64_t * signature_timestamp); + const hicn_header_t *h, + uint64_t *signature_timestamp); /** * @brief Sets the signature size @@ -300,7 +301,7 @@ int hicn_packet_get_signature_timestamp (hicn_format_t format, * @return hICN error code */ int hicn_packet_set_validation_algorithm (hicn_format_t format, - hicn_header_t * h, + hicn_header_t *h, uint8_t validation_algorithm); /** @@ -311,8 +312,8 @@ int hicn_packet_set_validation_algorithm (hicn_format_t format, * @return hICN error code */ int hicn_packet_get_validation_algorithm (hicn_format_t format, - const hicn_header_t * h, - uint8_t * validation_algorithm); + const hicn_header_t *h, + uint8_t *validation_algorithm); /** * @brief Sets the signature size @@ -321,8 +322,8 @@ int hicn_packet_get_validation_algorithm (hicn_format_t format, * @param [in] key_id - Key id to set * @return hICN error code */ -int hicn_packet_set_key_id (hicn_format_t format, hicn_header_t * h, - uint8_t * key_id); +int hicn_packet_set_key_id (hicn_format_t format, hicn_header_t *h, + uint8_t *key_id); /** * @brief Sets the signature size @@ -331,8 +332,8 @@ int hicn_packet_set_key_id (hicn_format_t format, hicn_header_t * h, * @param [out] key_id - Retrieved key id * @return hICN error code */ -int hicn_packet_get_key_id (hicn_format_t format, hicn_header_t * h, - uint8_t ** key_id, uint8_t * key_id_length); +int hicn_packet_get_key_id (hicn_format_t format, hicn_header_t *h, + uint8_t **key_id, uint8_t *key_id_length); /** * @brief Retrieves the packet hop limit @@ -340,7 +341,7 @@ int hicn_packet_get_key_id (hicn_format_t format, hicn_header_t * h, * @param [out] hops - Retrieved hop limit * @return hICN error code */ -int hicn_packet_get_hoplimit (const hicn_header_t * packet, u8 * hops); +int hicn_packet_get_hoplimit (const hicn_header_t *packet, u8 *hops); /** * @brief Sets the packet hop limit @@ -348,107 +349,111 @@ int hicn_packet_get_hoplimit (const hicn_header_t * packet, u8 * hops); * @param [in] hops - Hop limit to set * @return hICN error code */ -int hicn_packet_set_hoplimit (hicn_header_t * packet, u8 hops); +int hicn_packet_set_hoplimit (hicn_header_t *packet, u8 hops); -int hicn_packet_copy_header (hicn_format_t format, - const hicn_header_t * packet, - hicn_header_t * destination, bool copy_ah); +int hicn_packet_copy_header (hicn_format_t format, const hicn_header_t *packet, + hicn_header_t *destination, bool copy_ah); -int hicn_packet_get_lifetime (const hicn_header_t * packet, u32 * lifetime); -int hicn_packet_set_lifetime (hicn_header_t * packet, u32 lifetime); -int hicn_packet_get_reserved_bits (const hicn_header_t * packet, - u8 * reserved_bits); -int hicn_packet_set_reserved_bits (hicn_header_t * packet, +int hicn_packet_get_lifetime (const hicn_header_t *packet, u32 *lifetime); +int hicn_packet_set_lifetime (hicn_header_t *packet, u32 lifetime); +int hicn_packet_get_reserved_bits (const hicn_header_t *packet, + u8 *reserved_bits); +int hicn_packet_set_reserved_bits (hicn_header_t *packet, const u8 reserved_bits); -int hicn_packet_get_payload_type (const hicn_header_t * packet, - hicn_payload_type_t * payload_type); -int hicn_packet_set_payload_type (hicn_header_t * packet, +int hicn_packet_get_payload_type (const hicn_header_t *packet, + hicn_payload_type_t *payload_type); +int hicn_packet_set_payload_type (hicn_header_t *packet, const hicn_payload_type_t payload_type); -int hicn_packet_set_syn (hicn_header_t * packet); -int hicn_packet_reset_syn (hicn_header_t * packet); -int hicn_packet_test_syn (const hicn_header_t * packet, bool * flag); -int hicn_packet_set_ack (hicn_header_t * packet); -int hicn_packet_reset_ack (hicn_header_t * packet); -int hicn_packet_test_ack (const hicn_header_t * packet, bool * flag); -int hicn_packet_set_rst (hicn_header_t * packet); -int hicn_packet_reset_rst (hicn_header_t * packet); -int hicn_packet_test_rst (const hicn_header_t * packet, bool * flag); -int hicn_packet_set_fin (hicn_header_t * packet); -int hicn_packet_reset_fin (hicn_header_t * packet); -int hicn_packet_test_fin (const hicn_header_t * packet, bool * flag); -int hicn_packet_set_ece (hicn_header_t * packet); -int hicn_packet_reset_ece (hicn_header_t * packet); -int hicn_packet_test_ece (const hicn_header_t * packet, bool * flag); - -int hicn_packet_set_src_port (hicn_header_t * packet, u16 src_port); -int hicn_packet_get_src_port (const hicn_header_t * packet, u16 * src_port); -int hicn_packet_set_dst_port (hicn_header_t * packet, u16 dst_port); -int hicn_packet_get_dst_port (const hicn_header_t * packet, u16 * dst_port); -int hicn_packet_get_signature (hicn_format_t format, hicn_header_t * packet, - uint8_t ** sign_buf); +int hicn_packet_set_syn (hicn_format_t format, hicn_header_t *packet); +int hicn_packet_reset_syn (hicn_format_t format, hicn_header_t *packet); +int hicn_packet_test_syn (hicn_format_t format, const hicn_header_t *packet, + bool *flag); +int hicn_packet_set_ack (hicn_format_t format, hicn_header_t *packet); +int hicn_packet_reset_ack (hicn_format_t format, hicn_header_t *packet); +int hicn_packet_test_ack (hicn_format_t format, const hicn_header_t *packet, + bool *flag); +int hicn_packet_set_rst (hicn_format_t format, hicn_header_t *packet); +int hicn_packet_reset_rst (hicn_format_t format, hicn_header_t *packet); +int hicn_packet_test_rst (hicn_format_t format, const hicn_header_t *packet, + bool *flag); +int hicn_packet_set_fin (hicn_format_t format, hicn_header_t *packet); +int hicn_packet_reset_fin (hicn_format_t format, hicn_header_t *packet); +int hicn_packet_test_fin (hicn_format_t format, const hicn_header_t *packet, + bool *flag); +int hicn_packet_set_ece (hicn_format_t format, hicn_header_t *packet); +int hicn_packet_reset_ece (hicn_format_t format, hicn_header_t *packet); +int hicn_packet_test_ece (hicn_format_t format, const hicn_header_t *packet, + bool *flag); + +int hicn_packet_set_src_port (hicn_format_t format, hicn_header_t *packet, + u16 src_port); +int hicn_packet_get_src_port (hicn_format_t format, + const hicn_header_t *packet, u16 *src_port); +int hicn_packet_set_dst_port (hicn_format_t format, hicn_header_t *packet, + u16 dst_port); +int hicn_packet_get_dst_port (hicn_format_t format, + const hicn_header_t *packet, u16 *dst_port); +int hicn_packet_get_signature (hicn_format_t format, hicn_header_t *packet, + uint8_t **sign_buf); /* Interest */ int hicn_interest_get_name (hicn_format_t format, - const hicn_header_t * interest, - hicn_name_t * name); -int hicn_interest_set_name (hicn_format_t format, hicn_header_t * interest, - const hicn_name_t * name); + const hicn_header_t *interest, hicn_name_t *name); +int hicn_interest_set_name (hicn_format_t format, hicn_header_t *interest, + const hicn_name_t *name); int hicn_interest_get_locator (hicn_format_t format, - const hicn_header_t * interest, - ip_address_t * prefix); -int hicn_interest_set_locator (hicn_format_t format, hicn_header_t * interest, - const ip_address_t * prefix); -int hicn_interest_compare (const hicn_header_t * interest_1, - const hicn_header_t * interest_2); -int hicn_interest_set_lifetime (hicn_header_t * interest, u32 lifetime); -int hicn_interest_get_lifetime (const hicn_header_t * interest, - u32 * lifetime); + const hicn_header_t *interest, + ip_address_t *prefix); +int hicn_interest_set_locator (hicn_format_t format, hicn_header_t *interest, + const ip_address_t *prefix); +int hicn_interest_compare (const hicn_header_t *interest_1, + const hicn_header_t *interest_2); +int hicn_interest_set_lifetime (hicn_header_t *interest, u32 lifetime); +int hicn_interest_get_lifetime (const hicn_header_t *interest, u32 *lifetime); int hicn_interest_get_header_length (hicn_format_t format, - const hicn_header_t * interest, - size_t * header_length); + const hicn_header_t *interest, + size_t *header_length); int hicn_interest_get_payload_length (hicn_format_t format, - const hicn_header_t * interest, - size_t * payload_length); -int hicn_interest_set_payload (hicn_format_t format, hicn_header_t * interest, - const u8 * payload, size_t payload_length); + const hicn_header_t *interest, + size_t *payload_length); +int hicn_interest_set_payload (hicn_format_t format, hicn_header_t *interest, + const u8 *payload, size_t payload_length); int hicn_interest_get_payload (hicn_format_t format, - const hicn_header_t * interest, u8 ** payload, - size_t * payload_size, bool hard_copy); -int hicn_interest_reset_for_hash (hicn_format_t format, - hicn_header_t * packet); + const hicn_header_t *interest, u8 **payload, + size_t *payload_size, bool hard_copy); +int hicn_interest_reset_for_hash (hicn_format_t format, hicn_header_t *packet); /* Data */ -int hicn_data_get_name (hicn_format_t format, const hicn_header_t * data, - hicn_name_t * name); -int hicn_data_set_name (hicn_format_t format, hicn_header_t * data, - const hicn_name_t * name); -int hicn_data_get_locator (hicn_format_t format, const hicn_header_t * data, - ip_address_t * prefix); -int hicn_data_set_locator (hicn_format_t format, hicn_header_t * data, - const ip_address_t * prefix); -int hicn_data_compare (const hicn_header_t * data_1, - const hicn_header_t * data_2); -int hicn_data_get_expiry_time (const hicn_header_t * data, u32 * expiry_time); -int hicn_data_set_expiry_time (hicn_header_t * data, u32 expiry_time); -int hicn_data_get_header_length (hicn_format_t format, hicn_header_t * data, - size_t * header_length); +int hicn_data_get_name (hicn_format_t format, const hicn_header_t *data, + hicn_name_t *name); +int hicn_data_set_name (hicn_format_t format, hicn_header_t *data, + const hicn_name_t *name); +int hicn_data_get_locator (hicn_format_t format, const hicn_header_t *data, + ip_address_t *prefix); +int hicn_data_set_locator (hicn_format_t format, hicn_header_t *data, + const ip_address_t *prefix); +int hicn_data_compare (const hicn_header_t *data_1, + const hicn_header_t *data_2); +int hicn_data_get_expiry_time (const hicn_header_t *data, u32 *expiry_time); +int hicn_data_set_expiry_time (hicn_header_t *data, u32 expiry_time); +int hicn_data_get_header_length (hicn_format_t format, hicn_header_t *data, + size_t *header_length); int hicn_data_get_payload_length (hicn_format_t format, - const hicn_header_t * data, - size_t * payload_length); -int hicn_data_get_path_label (const hicn_header_t * data, u32 * path_label); -int hicn_data_set_path_label (hicn_header_t * data, u32 path_label); -int hicn_data_get_payload (hicn_format_t format, const hicn_header_t * data, - u8 ** payload, size_t * payload_size, - bool hard_copy); -int hicn_data_set_payload (hicn_format_t format, hicn_header_t * data, - const u8 * payload, size_t payload_length); -int hicn_data_get_payload_type (const hicn_header_t * data, - hicn_payload_type_t * payload_type); -int hicn_data_set_payload_type (hicn_header_t * data, + const hicn_header_t *data, + size_t *payload_length); +int hicn_data_get_path_label (const hicn_header_t *data, u32 *path_label); +int hicn_data_set_path_label (hicn_header_t *data, u32 path_label); +int hicn_data_get_payload (hicn_format_t format, const hicn_header_t *data, + u8 **payload, size_t *payload_size, bool hard_copy); +int hicn_data_set_payload (hicn_format_t format, hicn_header_t *data, + const u8 *payload, size_t payload_length); +int hicn_data_get_payload_type (const hicn_header_t *data, + hicn_payload_type_t *payload_type); +int hicn_data_set_payload_type (hicn_header_t *data, hicn_payload_type_t payload_type); -int hicn_data_reset_for_hash (hicn_format_t format, hicn_header_t * packet); +int hicn_data_reset_for_hash (hicn_format_t format, hicn_header_t *packet); #endif /* HICN_COMPAT_H */ diff --git a/lib/includes/hicn/ops.h b/lib/includes/hicn/ops.h index e8feff92d..7d4ae86d8 100644 --- a/lib/includes/hicn/ops.h +++ b/lib/includes/hicn/ops.h @@ -253,7 +253,7 @@ typedef struct hicn_ops_s * @param [in,out] h - Buffer holding the packet * @param [in] partial_csum - Partial checksum (set to 0, used internally to * carry intermediate values from IP pseudo-header) - * @param [in] payload_length - Payload length (can be set to 0, retrieved + * @param [in] payload_length - Payload length (can be set to ~0, retrieved * and used internally to carry payload length across protocol headers) * @return hICN error code */ @@ -264,9 +264,8 @@ typedef struct hicn_ops_s * @brief Validate all checksums in packet headers * @param [in] type - hICN packet type * @param [in] h - Buffer holding the packet - * @param [in] partial_csum - Partial checksum (set to 0, used internally to - * carry intermediate values from IP pseudo-header) - * @param [in] payload_length - Payload length (can be set to 0, retrieved + * @param [in] partial_csum - Partial checksum, or zero if no partial checksum available + * @param [in] payload_length - Payload length (can be set to ~0, retrieved * and used internally to carry payload length across protocol headers) * @return hICN error code */ @@ -294,12 +293,15 @@ typedef struct hicn_ops_s * @param [in] addr_old - Old locator (set to NULL, used internally to * compute incremental checksums) * @param [in] face_id - Face identifier used to update pathlabel + * @param [in] reset_pl - If not zero, reset the current pathlabel + * before update it * @return hICN error code */ int (*rewrite_data) (hicn_type_t type, hicn_protocol_t * h, const ip46_address_t * addr_new, ip46_address_t * addr_old, - const hicn_faceid_t face_id); + const hicn_faceid_t face_id, + u8 reset_pl); /** * @brief Return the packet length @@ -610,7 +612,7 @@ PAYLOAD (hicn_type_t type, const hicn_protocol_t * h) int protocol ## _rewrite_interest(hicn_type_t type, hicn_protocol_t * h, const ip46_address_t * addr_new, ip46_address_t * addr_old) { return HICN_LIB_ERROR_ ## error ; } #define DECLARE_rewrite_data(protocol, error) \ - int protocol ## _rewrite_data(hicn_type_t type, hicn_protocol_t * h, const ip46_address_t * addr_new, ip46_address_t * addr_old, const hicn_faceid_t face_id) { return HICN_LIB_ERROR_ ## error ; } + int protocol ## _rewrite_data(hicn_type_t type, hicn_protocol_t * h, const ip46_address_t * addr_new, ip46_address_t * addr_old, const hicn_faceid_t face_id, u8 reset_pl) { return HICN_LIB_ERROR_ ## error ; } #define DECLARE_get_length(protocol, error) \ int protocol ## _get_length(hicn_type_t type, const hicn_protocol_t * h, size_t * length) { return HICN_LIB_ERROR_ ## error ; } diff --git a/lib/src/CMakeLists.txt b/lib/src/CMakeLists.txt index 7eecaf775..49b735f51 100644 --- a/lib/src/CMakeLists.txt +++ b/lib/src/CMakeLists.txt @@ -42,7 +42,7 @@ if (DISABLE_SHARED_LIBRARIES) COMPONENT lib${LIBHICN} INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../includes DEFINITIONS ${COMPILER_DEFINITIONS} - INSTALL_ROOT_DIR hicn + HEADER_ROOT_DIR hicn INSTALL_HEADERS ${LIBHICN_HEADER_FILES} ${LIBHICN_HEADER_FILES_PROTOCOL} ${LIBHICN_HEADER_FILES_UTIL} LINK_LIBRARIES ${WSOCK32_LIBRARY} ${WS2_32_LIBRARY} ) @@ -53,7 +53,7 @@ else () COMPONENT lib${LIBHICN} INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../includes DEFINITIONS ${COMPILER_DEFINITIONS} - INSTALL_ROOT_DIR hicn + HEADER_ROOT_DIR hicn INSTALL_HEADERS ${LIBHICN_HEADER_FILES} ${LIBHICN_HEADER_FILES_PROTOCOL} ${LIBHICN_HEADER_FILES_UTIL} LINK_LIBRARIES ${WSOCK32_LIBRARY} ${WS2_32_LIBRARY} ) diff --git a/lib/src/common.c b/lib/src/common.c index 228a59d1e..562771e09 100644 --- a/lib/src/common.c +++ b/lib/src/common.c @@ -119,7 +119,7 @@ hicn_packet_dump (const uint8_t * buffer, size_t len) // Output description if given. if (len == 0) { - TRACE (" ZERO LENGTH\n"); + printf (" ZERO LENGTH\n"); return; } @@ -132,14 +132,14 @@ hicn_packet_dump (const uint8_t * buffer, size_t len) { // Just don't print ASCII for the zeroth line. if (i != 0) - TRACE (" %s\n", buff); + printf (" %s\n", buff); // Output the offset. - TRACE (" %04x ", i); + printf (" %04x ", i); } // Now the hex code for the specific character. - TRACE (" %02x", pc[i]); + printf (" %02x", pc[i]); // And store a printable ASCII character for later. if ((pc[i] < 0x20) || (pc[i] > 0x7e)) @@ -152,12 +152,12 @@ hicn_packet_dump (const uint8_t * buffer, size_t len) // Pad out last line if not exactly 16 characters. while ((i % 16) != 0) { - TRACE (" "); + printf (" "); i++; } // And print the final ASCII bit. - TRACE (" %s\n", buff); + printf (" %s\n", buff); } /* diff --git a/lib/src/compat.c b/lib/src/compat.c index 615175e3b..779a47315 100644 --- a/lib/src/compat.c +++ b/lib/src/compat.c @@ -20,8 +20,8 @@ #ifndef _WIN32 #include #endif -#include // memset -#include // offsetof +#include // memset +#include // offsetof #include #include @@ -30,13 +30,13 @@ #include #include -#define member_size(type, member) sizeof(((type *)0)->member) -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) +#define member_size(type, member) sizeof (((type *) 0)->member) +#define ARRAY_SIZE(a) (sizeof (a) / sizeof (*(a))) #define HICN_NAME_COMPONENT_SIZE 2 int -hicn_packet_get_format (const hicn_header_t * h, hicn_format_t * format) +hicn_packet_get_format (const hicn_header_t *h, hicn_format_t *format) { *format = HF_UNSPEC; @@ -122,7 +122,7 @@ hicn_format_to_type (hicn_format_t format) * This function is used to wrap old API calls to new ones */ hicn_type_t -hicn_header_to_type (const hicn_header_t * h) +hicn_header_to_type (const hicn_header_t *h) { hicn_format_t format; hicn_packet_get_format (h, &format); @@ -130,39 +130,47 @@ hicn_header_to_type (const hicn_header_t * h) } int -hicn_packet_init_header (hicn_format_t format, hicn_header_t * packet) +hicn_packet_init_header (hicn_format_t format, hicn_header_t *packet) { hicn_type_t type = hicn_format_to_type (format); + + if (hicn_type_is_none (type)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + return hicn_ops_vft[type.l1]->init_packet_header (type, &packet->protocol); } int -hicn_packet_compute_checksum (hicn_format_t format, hicn_header_t * h) +hicn_packet_compute_checksum (hicn_format_t format, hicn_header_t *h) { hicn_type_t type = hicn_format_to_type (format); - return hicn_ops_vft[type.l1]->update_checksums (type, &h->protocol, 0, 0); + return hicn_ops_vft[type.l1]->update_checksums (type, &h->protocol, 0, ~0); } int -hicn_packet_compute_header_checksum (hicn_format_t format, hicn_header_t * h, +hicn_packet_compute_header_checksum (hicn_format_t format, hicn_header_t *h, u16 init_sum) { hicn_type_t type = hicn_format_to_type (format); - /* payload_length == ~0: ignore payload */ - return hicn_ops_vft[type.l1]->update_checksums (type, &h->protocol, - init_sum, ~0); + /* payload_length == 0: ignore payload */ + return hicn_ops_vft[type.l1]->update_checksums (type, &h->protocol, init_sum, + 0); } int -hicn_packet_check_integrity (hicn_format_t format, hicn_header_t * h) +hicn_packet_check_integrity_no_payload (hicn_format_t format, hicn_header_t *h, + u16 init_sum) { hicn_type_t type = hicn_format_to_type (format); - return hicn_ops_vft[type.l1]->verify_checksums (type, &h->protocol, 0, 0); + return hicn_ops_vft[type.l1]->verify_checksums (type, &h->protocol, init_sum, + 0); } int hicn_packet_get_header_length_from_format (hicn_format_t format, - size_t * header_length) + size_t *header_length) { *header_length = _is_ipv4 (format) * IPV4_HDRLEN; *header_length += _is_ipv6 (format) * IPV6_HDRLEN; @@ -174,8 +182,8 @@ hicn_packet_get_header_length_from_format (hicn_format_t format, } int -hicn_packet_get_header_length (hicn_format_t format, const hicn_header_t * h, - size_t * header_length) +hicn_packet_get_header_length (hicn_format_t format, const hicn_header_t *h, + size_t *header_length) { hicn_packet_get_header_length_from_format (format, header_length); int is_ah = _is_ah (format); @@ -184,15 +192,15 @@ hicn_packet_get_header_length (hicn_format_t format, const hicn_header_t * h, // The signature payload is expressed as number of 32 bits words if (is_ah && is_ipv4) *header_length += (h->v4ah.ah.payloadlen) << 2; - else if(is_ah && is_ipv6) + else if (is_ah && is_ipv6) *header_length += (h->v6ah.ah.payloadlen) << 2; return HICN_LIB_ERROR_NONE; } int -hicn_packet_get_payload_length (hicn_format_t format, const hicn_header_t * h, - size_t * payload_length) +hicn_packet_get_payload_length (hicn_format_t format, const hicn_header_t *h, + size_t *payload_length) { hicn_type_t type = hicn_format_to_type (format); return hicn_ops_vft[type.l1]->get_payload_length (type, &h->protocol, @@ -200,7 +208,7 @@ hicn_packet_get_payload_length (hicn_format_t format, const hicn_header_t * h, } int -hicn_packet_set_payload_length (hicn_format_t format, hicn_header_t * h, +hicn_packet_set_payload_length (hicn_format_t format, hicn_header_t *h, const size_t payload_length) { hicn_type_t type = hicn_format_to_type (format); @@ -209,8 +217,8 @@ hicn_packet_set_payload_length (hicn_format_t format, hicn_header_t * h, } int -hicn_packet_compare (const hicn_header_t * packet1, - const hicn_header_t * packet2) +hicn_packet_compare (const hicn_header_t *packet1, + const hicn_header_t *packet2) { hicn_type_t type1 = hicn_header_to_type (packet1); hicn_type_t type2 = hicn_header_to_type (packet2); @@ -233,25 +241,23 @@ hicn_packet_compare (const hicn_header_t * packet1, return HICN_LIB_ERROR_UNEXPECTED; return memcmp ((u8 *) packet1, (u8 *) packet2, len1); - } int -hicn_packet_get_name (hicn_format_t format, const hicn_header_t * h, - hicn_name_t * name, u8 is_interest) +hicn_packet_get_name (hicn_format_t format, const hicn_header_t *h, + hicn_name_t *name, u8 is_interest) { hicn_type_t type = hicn_format_to_type (format); if (is_interest) - return hicn_ops_vft[type.l1]->get_interest_name (type, &h->protocol, - name); + return hicn_ops_vft[type.l1]->get_interest_name (type, &h->protocol, name); else return hicn_ops_vft[type.l1]->get_data_name (type, &h->protocol, name); } int -hicn_packet_set_name (hicn_format_t format, hicn_header_t * h, - const hicn_name_t * name, u8 is_interest) +hicn_packet_set_name (hicn_format_t format, hicn_header_t *h, + const hicn_name_t *name, u8 is_interest) { hicn_type_t type = hicn_format_to_type (format); @@ -261,23 +267,21 @@ hicn_packet_set_name (hicn_format_t format, hicn_header_t * h, #endif /* HICN_VPP_PLUGIN */ if (is_interest) - return hicn_ops_vft[type.l1]->set_interest_name (type, &h->protocol, - name); + return hicn_ops_vft[type.l1]->set_interest_name (type, &h->protocol, name); else return hicn_ops_vft[type.l1]->set_data_name (type, &h->protocol, name); } int -hicn_packet_set_payload (hicn_format_t format, hicn_header_t * h, - const u8 * payload, u16 payload_length) +hicn_packet_set_payload (hicn_format_t format, hicn_header_t *h, + const u8 *payload, u16 payload_length) { hicn_type_t type = hicn_format_to_type (format); size_t header_length; int rc; - rc = - hicn_ops_vft[type.l1]->get_header_length (type, &h->protocol, - &header_length); + rc = hicn_ops_vft[type.l1]->get_header_length (type, &h->protocol, + &header_length); if (rc < 0) return rc; @@ -288,22 +292,20 @@ hicn_packet_set_payload (hicn_format_t format, hicn_header_t * h, } int -hicn_packet_get_payload (hicn_format_t format, const hicn_header_t * h, - u8 ** payload, size_t * payload_size, bool hard_copy) +hicn_packet_get_payload (hicn_format_t format, const hicn_header_t *h, + u8 **payload, size_t *payload_size, bool hard_copy) { size_t header_length, payload_length; int rc; hicn_type_t type = hicn_format_to_type (format); - rc = - hicn_ops_vft[type.l1]->get_header_length (type, &h->protocol, - &header_length); + rc = hicn_ops_vft[type.l1]->get_header_length (type, &h->protocol, + &header_length); if (rc < 0) return rc; - rc = - hicn_ops_vft[type.l1]->get_payload_length (type, &h->protocol, - &payload_length); + rc = hicn_ops_vft[type.l1]->get_payload_length (type, &h->protocol, + &payload_length); if (rc < 0) return rc; @@ -320,23 +322,21 @@ hicn_packet_get_payload (hicn_format_t format, const hicn_header_t * h, } int -hicn_packet_get_locator (hicn_format_t format, const hicn_header_t * h, - ip_address_t * address, bool is_interest) +hicn_packet_get_locator (hicn_format_t format, const hicn_header_t *h, + ip_address_t *address, bool is_interest) { int is_ipv4 = (format & HFO_INET); int is_ipv6 = (format & HFO_INET6) >> 1; if (is_ipv4) { - address->v4.as_inaddr = is_interest - ? h->v4.ip.saddr.as_inaddr - : h->v4.ip.daddr.as_inaddr; + address->v4.as_inaddr = + is_interest ? h->v4.ip.saddr.as_inaddr : h->v4.ip.daddr.as_inaddr; } else if (is_ipv6) { - address->v6.as_in6addr = is_interest - ? h->v6.ip.saddr.as_in6addr - : h->v6.ip.daddr.as_in6addr; + address->v6.as_in6addr = + is_interest ? h->v6.ip.saddr.as_in6addr : h->v6.ip.daddr.as_in6addr; } else { @@ -347,25 +347,25 @@ hicn_packet_get_locator (hicn_format_t format, const hicn_header_t * h, } int -hicn_packet_set_locator (hicn_format_t format, hicn_header_t * h, - const ip_address_t * address, bool is_interest) +hicn_packet_set_locator (hicn_format_t format, hicn_header_t *h, + const ip_address_t *address, bool is_interest) { int is_ipv4 = (format & HFO_INET); int is_ipv6 = (format & HFO_INET6) >> 1; if (is_ipv6) { - if (is_interest) - h->v6.ip.saddr.as_in6addr = address->v6.as_in6addr; - else - h->v6.ip.daddr.as_in6addr = address->v6.as_in6addr; + if (is_interest) + h->v6.ip.saddr.as_in6addr = address->v6.as_in6addr; + else + h->v6.ip.daddr.as_in6addr = address->v6.as_in6addr; } else if (is_ipv4) { - if (is_interest) - h->v4.ip.saddr.as_inaddr = address->v4.as_inaddr; - else - h->v4.ip.daddr.as_inaddr = address->v4.as_inaddr; + if (is_interest) + h->v4.ip.saddr.as_inaddr = address->v4.as_inaddr; + else + h->v4.ip.daddr.as_inaddr = address->v4.as_inaddr; } else { @@ -376,79 +376,78 @@ hicn_packet_set_locator (hicn_format_t format, hicn_header_t * h, } int -hicn_packet_get_signature_size (hicn_format_t format, const hicn_header_t * h, - size_t * bytes) +hicn_packet_get_signature_size (hicn_format_t format, const hicn_header_t *h, + size_t *bytes) { hicn_type_t type = hicn_format_to_type (format); - return hicn_ops_vft[type.l1]->get_signature_size (type, &h->protocol, - bytes); + return hicn_ops_vft[type.l1]->get_signature_size (type, &h->protocol, bytes); } int -hicn_packet_set_signature_size (hicn_format_t format, hicn_header_t * h, +hicn_packet_set_signature_size (hicn_format_t format, hicn_header_t *h, size_t bytes) { hicn_type_t type = hicn_format_to_type (format); - return hicn_ops_vft[type.l1]->set_signature_size (type, &h->protocol, - bytes); + return hicn_ops_vft[type.l1]->set_signature_size (type, &h->protocol, bytes); } int -hicn_packet_set_signature_timestamp (hicn_format_t format, hicn_header_t * h, - uint64_t signature_timestamp) +hicn_packet_set_signature_timestamp (hicn_format_t format, hicn_header_t *h, + uint64_t signature_timestamp) { hicn_type_t type = hicn_format_to_type (format); return hicn_ops_vft[type.l1]->set_signature_timestamp (type, &h->protocol, - signature_timestamp); + signature_timestamp); } int -hicn_packet_get_signature_timestamp (hicn_format_t format, const hicn_header_t * h, - uint64_t *signature_timestamp) +hicn_packet_get_signature_timestamp (hicn_format_t format, + const hicn_header_t *h, + uint64_t *signature_timestamp) { hicn_type_t type = hicn_format_to_type (format); return hicn_ops_vft[type.l1]->get_signature_timestamp (type, &h->protocol, - signature_timestamp); + signature_timestamp); } int -hicn_packet_set_validation_algorithm (hicn_format_t format, hicn_header_t * h, - uint8_t validation_algorithm) +hicn_packet_set_validation_algorithm (hicn_format_t format, hicn_header_t *h, + uint8_t validation_algorithm) { hicn_type_t type = hicn_format_to_type (format); - return hicn_ops_vft[type.l1]->set_validation_algorithm (type, &h->protocol, - validation_algorithm); + return hicn_ops_vft[type.l1]->set_validation_algorithm ( + type, &h->protocol, validation_algorithm); } int -hicn_packet_get_validation_algorithm (hicn_format_t format, const hicn_header_t * h, - uint8_t * validation_algorithm) +hicn_packet_get_validation_algorithm (hicn_format_t format, + const hicn_header_t *h, + uint8_t *validation_algorithm) { hicn_type_t type = hicn_format_to_type (format); - return hicn_ops_vft[type.l1]->get_validation_algorithm (type, &h->protocol, - validation_algorithm); + return hicn_ops_vft[type.l1]->get_validation_algorithm ( + type, &h->protocol, validation_algorithm); } int -hicn_packet_set_key_id (hicn_format_t format, hicn_header_t * h, - uint8_t *key_id) +hicn_packet_set_key_id (hicn_format_t format, hicn_header_t *h, + uint8_t *key_id) { hicn_type_t type = hicn_format_to_type (format); - return hicn_ops_vft[type.l1]->set_key_id (type, &h->protocol, - key_id); + return hicn_ops_vft[type.l1]->set_key_id (type, &h->protocol, key_id); } int -hicn_packet_get_key_id (hicn_format_t format, hicn_header_t * h, - uint8_t ** key_id, uint8_t *key_id_length) +hicn_packet_get_key_id (hicn_format_t format, hicn_header_t *h, + uint8_t **key_id, uint8_t *key_id_length) { hicn_type_t type = hicn_format_to_type (format); - return hicn_ops_vft[type.l1]->get_key_id (type, &h->protocol, - key_id, key_id_length); + return hicn_ops_vft[type.l1]->get_key_id (type, &h->protocol, key_id, + key_id_length); } int -hicn_packet_get_hoplimit (const hicn_header_t * h, u8 * hops) +hicn_packet_get_hoplimit (const hicn_header_t *h, u8 *hops) { switch (HICN_IP_VERSION (h)) { @@ -466,7 +465,7 @@ hicn_packet_get_hoplimit (const hicn_header_t * h, u8 * hops) } int -hicn_packet_set_hoplimit (hicn_header_t * h, u8 hops) +hicn_packet_set_hoplimit (hicn_header_t *h, u8 hops) { switch (HICN_IP_VERSION (h)) { @@ -483,9 +482,8 @@ hicn_packet_set_hoplimit (hicn_header_t * h, u8 hops) return HICN_LIB_ERROR_NONE; } - int -hicn_packet_get_lifetime (const hicn_header_t * h, u32 * lifetime) +hicn_packet_get_lifetime (const hicn_header_t *h, u32 *lifetime) { hicn_type_t type = hicn_header_to_type (h); return hicn_ops_vft[type.l1]->get_lifetime (type, &h->protocol, @@ -493,7 +491,7 @@ hicn_packet_get_lifetime (const hicn_header_t * h, u32 * lifetime) } int -hicn_packet_set_lifetime (hicn_header_t * h, u32 lifetime) +hicn_packet_set_lifetime (hicn_header_t *h, u32 lifetime) { hicn_type_t type = hicn_header_to_type (h); return hicn_ops_vft[type.l1]->set_lifetime (type, &h->protocol, @@ -501,15 +499,15 @@ hicn_packet_set_lifetime (hicn_header_t * h, u32 lifetime) } int -hicn_packet_get_reserved_bits (const hicn_header_t * h, u8 * reserved_bits) +hicn_packet_get_reserved_bits (const hicn_header_t *h, u8 *reserved_bits) { switch (HICN_IP_VERSION (h)) { case 6: - *reserved_bits = (u8)(h->v6.tcp.reserved); + *reserved_bits = (u8) (h->v6.tcp.reserved); break; case 4: - *reserved_bits = (u8)(h->v4.tcp.reserved); + *reserved_bits = (u8) (h->v4.tcp.reserved); break; default: return HICN_LIB_ERROR_UNEXPECTED; @@ -519,7 +517,7 @@ hicn_packet_get_reserved_bits (const hicn_header_t * h, u8 * reserved_bits) } int -hicn_packet_set_reserved_bits (hicn_header_t * h, const u8 reserved_bits) +hicn_packet_set_reserved_bits (hicn_header_t *h, const u8 reserved_bits) { switch (HICN_IP_VERSION (h)) { @@ -537,16 +535,18 @@ hicn_packet_set_reserved_bits (hicn_header_t * h, const u8 reserved_bits) } int -hicn_packet_get_payload_type (const hicn_header_t * h, - hicn_payload_type_t * payload_type) +hicn_packet_get_payload_type (const hicn_header_t *h, + hicn_payload_type_t *payload_type) { switch (HICN_IP_VERSION (h)) { case 6: - *payload_type = ((h->v6.tcp.flags & HICN_TCP_FLAG_URG) == HICN_TCP_FLAG_URG); + *payload_type = + ((h->v6.tcp.flags & HICN_TCP_FLAG_URG) == HICN_TCP_FLAG_URG); break; case 4: - *payload_type = ((h->v4.tcp.flags & HICN_TCP_FLAG_URG) == HICN_TCP_FLAG_URG); + *payload_type = + ((h->v4.tcp.flags & HICN_TCP_FLAG_URG) == HICN_TCP_FLAG_URG); break; default: return HICN_LIB_ERROR_UNEXPECTED; @@ -561,7 +561,7 @@ hicn_packet_get_payload_type (const hicn_header_t * h, } int -hicn_packet_set_payload_type (hicn_header_t * h, +hicn_packet_set_payload_type (hicn_header_t *h, hicn_payload_type_t payload_type) { if (payload_type != HPT_DATA && payload_type != HPT_MANIFEST) @@ -591,8 +591,13 @@ hicn_packet_set_payload_type (hicn_header_t * h, } int -hicn_packet_set_syn (hicn_header_t * h) +hicn_packet_set_syn (hicn_format_t format, hicn_header_t *h) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -608,8 +613,13 @@ hicn_packet_set_syn (hicn_header_t * h) } int -hicn_packet_reset_syn (hicn_header_t * h) +hicn_packet_reset_syn (hicn_format_t format, hicn_header_t *h) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -625,8 +635,13 @@ hicn_packet_reset_syn (hicn_header_t * h) } int -hicn_packet_test_syn (const hicn_header_t * h, bool * flag) +hicn_packet_test_syn (hicn_format_t format, const hicn_header_t *h, bool *flag) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -642,8 +657,13 @@ hicn_packet_test_syn (const hicn_header_t * h, bool * flag) } int -hicn_packet_set_ack (hicn_header_t * h) +hicn_packet_set_ack (hicn_format_t format, hicn_header_t *h) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -659,8 +679,13 @@ hicn_packet_set_ack (hicn_header_t * h) } int -hicn_packet_reset_ack (hicn_header_t * h) +hicn_packet_reset_ack (hicn_format_t format, hicn_header_t *h) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -676,8 +701,13 @@ hicn_packet_reset_ack (hicn_header_t * h) } int -hicn_packet_test_ack (const hicn_header_t * h, bool * flag) +hicn_packet_test_ack (hicn_format_t format, const hicn_header_t *h, bool *flag) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -693,8 +723,13 @@ hicn_packet_test_ack (const hicn_header_t * h, bool * flag) } int -hicn_packet_set_rst (hicn_header_t * h) +hicn_packet_set_rst (hicn_format_t format, hicn_header_t *h) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -710,8 +745,13 @@ hicn_packet_set_rst (hicn_header_t * h) } int -hicn_packet_reset_rst (hicn_header_t * h) +hicn_packet_reset_rst (hicn_format_t format, hicn_header_t *h) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -727,8 +767,13 @@ hicn_packet_reset_rst (hicn_header_t * h) } int -hicn_packet_test_rst (const hicn_header_t * h, bool * flag) +hicn_packet_test_rst (hicn_format_t format, const hicn_header_t *h, bool *flag) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -744,8 +789,13 @@ hicn_packet_test_rst (const hicn_header_t * h, bool * flag) } int -hicn_packet_set_fin (hicn_header_t * h) +hicn_packet_set_fin (hicn_format_t format, hicn_header_t *h) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -761,8 +811,13 @@ hicn_packet_set_fin (hicn_header_t * h) } int -hicn_packet_reset_fin (hicn_header_t * h) +hicn_packet_reset_fin (hicn_format_t format, hicn_header_t *h) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -778,8 +833,13 @@ hicn_packet_reset_fin (hicn_header_t * h) } int -hicn_packet_test_fin (const hicn_header_t * h, bool * flag) +hicn_packet_test_fin (hicn_format_t format, const hicn_header_t *h, bool *flag) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -795,8 +855,13 @@ hicn_packet_test_fin (const hicn_header_t * h, bool * flag) } int -hicn_packet_set_ece (hicn_header_t * h) +hicn_packet_set_ece (hicn_format_t format, hicn_header_t *h) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -812,8 +877,13 @@ hicn_packet_set_ece (hicn_header_t * h) } int -hicn_packet_reset_ece (hicn_header_t * h) +hicn_packet_reset_ece (hicn_format_t format, hicn_header_t *h) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -829,8 +899,13 @@ hicn_packet_reset_ece (hicn_header_t * h) } int -hicn_packet_test_ece (const hicn_header_t * h, bool * flag) +hicn_packet_test_ece (hicn_format_t format, const hicn_header_t *h, bool *flag) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -846,8 +921,13 @@ hicn_packet_test_ece (const hicn_header_t * h, bool * flag) } int -hicn_packet_set_src_port (hicn_header_t * h, u16 src_port) +hicn_packet_set_src_port (hicn_format_t format, hicn_header_t *h, u16 src_port) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -863,8 +943,14 @@ hicn_packet_set_src_port (hicn_header_t * h, u16 src_port) } int -hicn_packet_get_src_port (const hicn_header_t * h, u16 * src_port) +hicn_packet_get_src_port (hicn_format_t format, const hicn_header_t *h, + u16 *src_port) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -880,8 +966,13 @@ hicn_packet_get_src_port (const hicn_header_t * h, u16 * src_port) } int -hicn_packet_set_dst_port (hicn_header_t * h, u16 dst_port) +hicn_packet_set_dst_port (hicn_format_t format, hicn_header_t *h, u16 dst_port) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -897,8 +988,14 @@ hicn_packet_set_dst_port (hicn_header_t * h, u16 dst_port) } int -hicn_packet_get_dst_port (const hicn_header_t * h, u16 * dst_port) +hicn_packet_get_dst_port (hicn_format_t format, const hicn_header_t *h, + u16 *dst_port) { + if (!_is_tcp (format)) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + switch (HICN_IP_VERSION (h)) { case 6: @@ -914,8 +1011,8 @@ hicn_packet_get_dst_port (const hicn_header_t * h, u16 * dst_port) } int -hicn_packet_copy_header (hicn_format_t format, const hicn_header_t * packet, - hicn_header_t * destination, bool copy_ah) +hicn_packet_copy_header (hicn_format_t format, const hicn_header_t *packet, + hicn_header_t *destination, bool copy_ah) { size_t header_length = _is_ipv4 (format) * IPV4_HDRLEN; header_length += _is_ipv6 (format) * IPV6_HDRLEN; @@ -929,95 +1026,95 @@ hicn_packet_copy_header (hicn_format_t format, const hicn_header_t * packet, } #define _INTEREST 1 -#define _DATA 0 +#define _DATA 0 /* Interest */ int -hicn_interest_get_name (hicn_format_t format, const hicn_header_t * interest, - hicn_name_t * name) +hicn_interest_get_name (hicn_format_t format, const hicn_header_t *interest, + hicn_name_t *name) { return hicn_packet_get_name (format, interest, name, _INTEREST); } int -hicn_interest_set_name (hicn_format_t format, hicn_header_t * interest, - const hicn_name_t * name) +hicn_interest_set_name (hicn_format_t format, hicn_header_t *interest, + const hicn_name_t *name) { - int ret_err = hicn_packet_reset_ece (interest); //interest packet -> ece flag unset + int ret_err = + hicn_packet_reset_ece (format, interest); // interest packet -> ece flag unset if (ret_err < 0) return HICN_LIB_ERROR_UNEXPECTED; return hicn_packet_set_name (format, interest, name, _INTEREST); } int -hicn_interest_get_locator (hicn_format_t format, - const hicn_header_t * interest, - ip_address_t * address) +hicn_interest_get_locator (hicn_format_t format, const hicn_header_t *interest, + ip_address_t *address) { return hicn_packet_get_locator (format, interest, address, _INTEREST); } int -hicn_interest_set_locator (hicn_format_t format, hicn_header_t * interest, - const ip_address_t * address) +hicn_interest_set_locator (hicn_format_t format, hicn_header_t *interest, + const ip_address_t *address) { return hicn_packet_set_locator (format, interest, address, _INTEREST); } int -hicn_interest_compare (const hicn_header_t * interest_1, - const hicn_header_t * interest_2) +hicn_interest_compare (const hicn_header_t *interest_1, + const hicn_header_t *interest_2) { return hicn_packet_compare (interest_1, interest_2); } int -hicn_interest_get_lifetime (const hicn_header_t * interest, u32 * lifetime) +hicn_interest_get_lifetime (const hicn_header_t *interest, u32 *lifetime) { return hicn_packet_get_lifetime (interest, lifetime); } int -hicn_interest_set_lifetime (hicn_header_t * interest, u32 lifetime) +hicn_interest_set_lifetime (hicn_header_t *interest, u32 lifetime) { return hicn_packet_set_lifetime (interest, lifetime); } int hicn_interest_get_header_length (hicn_format_t format, - const hicn_header_t * interest, - size_t * header_length) + const hicn_header_t *interest, + size_t *header_length) { return hicn_packet_get_header_length (format, interest, header_length); } int hicn_interest_get_payload_length (hicn_format_t format, - const hicn_header_t * interest, - size_t * payload_length) + const hicn_header_t *interest, + size_t *payload_length) { return hicn_packet_get_payload_length (format, interest, payload_length); } int -hicn_interest_get_payload (hicn_format_t format, - const hicn_header_t * interest, u8 ** payload, - size_t * payload_size, bool hard_copy) +hicn_interest_get_payload (hicn_format_t format, const hicn_header_t *interest, + u8 **payload, size_t *payload_size, bool hard_copy) { return hicn_packet_get_payload (format, interest, payload, payload_size, hard_copy); } int -hicn_interest_set_payload (hicn_format_t format, hicn_header_t * interest, - const u8 * payload, size_t payload_length) +hicn_interest_set_payload (hicn_format_t format, hicn_header_t *interest, + const u8 *payload, size_t payload_length) { - return hicn_packet_set_payload (format, interest, payload, (u16)payload_length); + return hicn_packet_set_payload (format, interest, payload, + (u16) payload_length); } int -hicn_interest_reset_for_hash (hicn_format_t format, hicn_header_t * packet) +hicn_interest_reset_for_hash (hicn_format_t format, hicn_header_t *packet) { hicn_type_t type = hicn_format_to_type (format); return hicn_ops_vft[type.l1]->reset_interest_for_hash (type, @@ -1027,85 +1124,84 @@ hicn_interest_reset_for_hash (hicn_format_t format, hicn_header_t * packet) /* Data */ int -hicn_data_get_name (hicn_format_t format, const hicn_header_t * data, - hicn_name_t * name) +hicn_data_get_name (hicn_format_t format, const hicn_header_t *data, + hicn_name_t *name) { return hicn_packet_get_name (format, data, name, _DATA); } int -hicn_data_set_name (hicn_format_t format, hicn_header_t * data, - const hicn_name_t * name) +hicn_data_set_name (hicn_format_t format, hicn_header_t *data, + const hicn_name_t *name) { - int ret_err = hicn_packet_set_ece (data); //data packet -> ece flag set + int ret_err = hicn_packet_set_ece (format, data); // data packet -> ece flag set if (ret_err < 0) return HICN_LIB_ERROR_UNEXPECTED; return hicn_packet_set_name (format, data, name, _DATA); } int -hicn_data_get_locator (hicn_format_t format, const hicn_header_t * data, - ip_address_t * address) +hicn_data_get_locator (hicn_format_t format, const hicn_header_t *data, + ip_address_t *address) { return hicn_packet_get_locator (format, data, address, _DATA); } int -hicn_data_set_locator (hicn_format_t format, hicn_header_t * data, - const ip_address_t * address) +hicn_data_set_locator (hicn_format_t format, hicn_header_t *data, + const ip_address_t *address) { return hicn_packet_set_locator (format, data, address, _DATA); } int -hicn_data_compare (const hicn_header_t * data_1, const hicn_header_t * data_2) +hicn_data_compare (const hicn_header_t *data_1, const hicn_header_t *data_2) { return hicn_packet_compare (data_1, data_2); } int -hicn_data_get_expiry_time (const hicn_header_t * data, u32 * expiry_time) +hicn_data_get_expiry_time (const hicn_header_t *data, u32 *expiry_time) { return hicn_packet_get_lifetime (data, expiry_time); } int -hicn_data_set_expiry_time (hicn_header_t * data, u32 expiry_time) +hicn_data_set_expiry_time (hicn_header_t *data, u32 expiry_time) { return hicn_packet_set_lifetime (data, (hicn_lifetime_t) expiry_time); } int -hicn_data_get_header_length (hicn_format_t format, hicn_header_t * data, - size_t * header_length) +hicn_data_get_header_length (hicn_format_t format, hicn_header_t *data, + size_t *header_length) { return hicn_packet_get_header_length (format, data, header_length); } int -hicn_data_get_payload_length (hicn_format_t format, - const hicn_header_t * data, - size_t * payload_length) +hicn_data_get_payload_length (hicn_format_t format, const hicn_header_t *data, + size_t *payload_length) { return hicn_packet_get_payload_length (format, data, payload_length); } int -hicn_data_set_payload_type (hicn_header_t * data, +hicn_data_set_payload_type (hicn_header_t *data, hicn_payload_type_t payload_type) { return hicn_packet_set_payload_type (data, payload_type); } int -hicn_data_get_payload_type (const hicn_header_t * data, - hicn_payload_type_t * payload_type) +hicn_data_get_payload_type (const hicn_header_t *data, + hicn_payload_type_t *payload_type) { return hicn_packet_get_payload_type (data, payload_type); } int -hicn_data_get_path_label (const hicn_header_t * data, u32 * path_label) +hicn_data_get_path_label (const hicn_header_t *data, u32 *path_label) { hicn_type_t type = hicn_header_to_type (data); return hicn_ops_vft[type.l1]->get_data_pathlabel (type, &data->protocol, @@ -1113,7 +1209,7 @@ hicn_data_get_path_label (const hicn_header_t * data, u32 * path_label) } int -hicn_data_set_path_label (hicn_header_t * data, u32 path_label) +hicn_data_set_path_label (hicn_header_t *data, u32 path_label) { hicn_type_t type = hicn_header_to_type (data); return hicn_ops_vft[type.l1]->set_data_pathlabel (type, &data->protocol, @@ -1121,34 +1217,34 @@ hicn_data_set_path_label (hicn_header_t * data, u32 path_label) } int -hicn_data_set_payload (hicn_format_t format, hicn_header_t * data, - const u8 * payload, size_t payload_length) +hicn_data_set_payload (hicn_format_t format, hicn_header_t *data, + const u8 *payload, size_t payload_length) { - return hicn_packet_set_payload (format, data, payload, (u16)payload_length); + return hicn_packet_set_payload (format, data, payload, (u16) payload_length); } int -hicn_data_get_payload (hicn_format_t format, const hicn_header_t * data, - u8 ** payload, size_t * payload_size, bool hard_copy) +hicn_data_get_payload (hicn_format_t format, const hicn_header_t *data, + u8 **payload, size_t *payload_size, bool hard_copy) { return hicn_packet_get_payload (format, data, payload, payload_size, hard_copy); } int -hicn_data_reset_for_hash (hicn_format_t format, hicn_header_t * packet) +hicn_data_reset_for_hash (hicn_format_t format, hicn_header_t *packet) { hicn_type_t type = hicn_format_to_type (format); - return hicn_ops_vft[type.l1]->reset_data_for_hash (type, - &packet->protocol); - + return hicn_ops_vft[type.l1]->reset_data_for_hash (type, &packet->protocol); } -int hicn_packet_get_signature(hicn_format_t format, hicn_header_t * packet, uint8_t ** sign_buf) +int +hicn_packet_get_signature (hicn_format_t format, hicn_header_t *packet, + uint8_t **sign_buf) { hicn_type_t type = hicn_format_to_type (format); - return hicn_ops_vft[type.l1]->get_signature (type, - &packet->protocol, sign_buf); + return hicn_ops_vft[type.l1]->get_signature (type, &packet->protocol, + sign_buf); } /* diff --git a/lib/src/name.c b/lib/src/name.c index 9388c35e7..54b2a76aa 100644 --- a/lib/src/name.c +++ b/lib/src/name.c @@ -242,7 +242,7 @@ hicn_name_hash (const hicn_name_t * name, u32 * hash, bool consider_suffix) int hicn_name_empty (hicn_name_t * name) { - return name->type == HNT_UNSPEC ? HICN_LIB_ERROR_NONE : 1; + return name->type == HNT_UNSPEC ? 1 : 0; } int diff --git a/lib/src/protocol/ah.c b/lib/src/protocol/ah.c index da08d1ee8..03f3af04a 100644 --- a/lib/src/protocol/ah.c +++ b/lib/src/protocol/ah.c @@ -113,7 +113,7 @@ ah_rewrite_interest (hicn_type_t type, hicn_protocol_t * h, int ah_rewrite_data (hicn_type_t type, hicn_protocol_t * h, const ip46_address_t * addr_new, ip46_address_t * addr_old, - const hicn_faceid_t face_id) + const hicn_faceid_t face_id, u8 reset_pl) { /* Nothing to do on signature */ return HICN_LIB_ERROR_NONE; diff --git a/lib/src/protocol/icmp.c b/lib/src/protocol/icmp.c index b24c0f11e..a16353427 100644 --- a/lib/src/protocol/icmp.c +++ b/lib/src/protocol/icmp.c @@ -118,7 +118,7 @@ icmp_rewrite_interest (hicn_type_t type, hicn_protocol_t * h, int icmp_rewrite_data (hicn_type_t type, hicn_protocol_t * h, const ip46_address_t * addr_new, ip46_address_t * addr_old, - const hicn_faceid_t face_id) + const hicn_faceid_t face_id, u8 reset_pl) { return HICN_LIB_ERROR_NOT_IMPLEMENTED; // u16 *icmp_checksum = &(h->icmp.csum); diff --git a/lib/src/protocol/ipv4.c b/lib/src/protocol/ipv4.c index 781907231..73ce12ee0 100644 --- a/lib/src/protocol/ipv4.c +++ b/lib/src/protocol/ipv4.c @@ -324,7 +324,7 @@ ipv4_rewrite_interest (hicn_type_t type, hicn_protocol_t * h, int ipv4_rewrite_data (hicn_type_t type, hicn_protocol_t * h, const ip46_address_t * addr_new, ip46_address_t * addr_old, - const hicn_faceid_t face_id) + const hicn_faceid_t face_id, u8 reset_pl) { // ASSERT(addr_old == NULL); addr_old->ip4 = h->ipv4.daddr; @@ -336,7 +336,7 @@ ipv4_rewrite_data (hicn_type_t type, hicn_protocol_t * h, h->ipv4.csum = 0; h->ipv4.csum = csum (&h->ipv4, IPV4_HDRLEN, 0); - return CHILD_OPS (rewrite_data, type, h, addr_new, addr_old, face_id); + return CHILD_OPS (rewrite_data, type, h, addr_new, addr_old, face_id, reset_pl); } int diff --git a/lib/src/protocol/ipv6.c b/lib/src/protocol/ipv6.c index f23b01cd8..bf8123497 100644 --- a/lib/src/protocol/ipv6.c +++ b/lib/src/protocol/ipv6.c @@ -220,19 +220,13 @@ ipv6_update_checksums (hicn_type_t type, hicn_protocol_t * h, u16 partial_csum, size_t payload_length) { /* Retrieve payload length if not specified */ - if (payload_length == 0) + if (payload_length == ~0) { int rc = ipv6_get_payload_length (type, h, &payload_length); if (rc < 0) return rc; } - /* Ignore the payload if payload_length = ~0 */ - if (payload_length == ~0) - { - payload_length = 0; - } - /* Build pseudo-header */ ipv6_pseudo_header_t psh; psh.ip_src = h->ipv6.saddr; @@ -258,7 +252,7 @@ ipv6_verify_checksums (hicn_type_t type, hicn_protocol_t * h, u16 partial_csum, size_t payload_length) { /* Retrieve payload length if not specified */ - if (payload_length == 0) + if (payload_length == ~0) { int rc = ipv6_get_payload_length (type, h, &payload_length); if (rc < 0) @@ -276,7 +270,11 @@ ipv6_verify_checksums (hicn_type_t type, hicn_protocol_t * h, pseudo.protocol = h->ipv6.nxt; /* Compute partial checksum based on pseudo-header */ - partial_csum = csum (&pseudo, IPV6_PSHDRLEN, 0); + if (partial_csum != 0) + { + partial_csum = ~partial_csum; + } + partial_csum = csum (&pseudo, IPV6_PSHDRLEN, partial_csum); return CHILD_OPS (verify_checksums, type, h, partial_csum, payload_length); } @@ -296,13 +294,13 @@ ipv6_rewrite_interest (hicn_type_t type, hicn_protocol_t * h, int ipv6_rewrite_data (hicn_type_t type, hicn_protocol_t * h, const ip46_address_t * addr_new, ip46_address_t * addr_old, - const hicn_faceid_t face_id) + const hicn_faceid_t face_id, u8 reset_pl) { // ASSERT(addr_old == NULL); addr_old->ip6 = h->ipv6.daddr; h->ipv6.daddr = addr_new->ip6; - return CHILD_OPS (rewrite_data, type, h, addr_new, addr_old, face_id); + return CHILD_OPS (rewrite_data, type, h, addr_new, addr_old, face_id, reset_pl); } int diff --git a/lib/src/protocol/tcp.c b/lib/src/protocol/tcp.c index c6099bf8f..95f93c6af 100644 --- a/lib/src/protocol/tcp.c +++ b/lib/src/protocol/tcp.c @@ -170,12 +170,12 @@ tcp_update_data_pathlabel (hicn_type_t type, hicn_protocol_t * h, const hicn_faceid_t face_id) { hicn_pathlabel_t pl = - (hicn_pathlabel_t) ((h->tcp.pathlabel & HICN_PATH_LABEL_MASK) >> (32 - - HICN_PATH_LABEL_SIZE)); + (hicn_pathlabel_t) (h->tcp.seq_ack >> (32 - HICN_PATH_LABEL_SIZE)); + hicn_pathlabel_t new_pl; update_pathlabel (pl, face_id, &new_pl); - h->tcp.pathlabel = new_pl; + h->tcp.seq_ack = (new_pl << (32 - HICN_PATH_LABEL_SIZE)); return HICN_LIB_ERROR_NONE; } @@ -249,7 +249,12 @@ int tcp_verify_checksums (hicn_type_t type, hicn_protocol_t * h, u16 partial_csum, size_t payload_length) { - if (csum (h, TCP_HDRLEN + payload_length, ~partial_csum) != 0) + if (PREDICT_TRUE (partial_csum != 0)) + { + partial_csum = ~partial_csum; + } + + if (csum (h, TCP_HDRLEN + payload_length, partial_csum) != 0) return HICN_LIB_ERROR_CORRUPTED_PACKET; return CHILD_OPS (verify_checksums, type, h, 0, payload_length); } @@ -307,11 +312,19 @@ tcp_rewrite_interest (hicn_type_t type, hicn_protocol_t * h, int tcp_rewrite_data (hicn_type_t type, hicn_protocol_t * h, const ip46_address_t * addr_new, ip46_address_t * addr_old, - const hicn_faceid_t face_id) + const hicn_faceid_t face_id, u8 reset_pl) { + u16 *tcp_checksum = &(h->tcp.csum); int ret = check_tcp_checksum(*tcp_checksum); + /* + * update path label + */ + u16 old_pl = h->tcp.seq_ack; + if(reset_pl) h->tcp.seq_ack = 0; + tcp_update_data_pathlabel (type, h, face_id); + if (ret) { return ret; @@ -330,9 +343,8 @@ tcp_rewrite_data (hicn_type_t type, hicn_protocol_t * h, csum = ip_csum_add_even (csum, (ip_csum_t) (addr_new->ip6.as_u64[0])); csum = ip_csum_add_even (csum, (ip_csum_t) (addr_new->ip6.as_u64[1])); - csum = ip_csum_sub_even (csum, h->tcp.pathlabel); - tcp_update_data_pathlabel (type, h, face_id); - csum = ip_csum_add_even (csum, h->tcp.pathlabel); + csum = ip_csum_sub_even (csum, old_pl); + csum = ip_csum_add_even (csum, h->tcp.seq_ack); *tcp_checksum = ip_csum_fold (csum); diff --git a/libtransport/CMakeLists.txt b/libtransport/CMakeLists.txt index e86fa5d7b..3f828c4d6 100644 --- a/libtransport/CMakeLists.txt +++ b/libtransport/CMakeLists.txt @@ -44,23 +44,9 @@ set(TRANSPORT_LOG_LEVEL "INFO" CACHE STRING "Set log level") set(TRANSPORT_ROOT_PATH "src") -set(TRANSPORT_CORE ${TRANSPORT_ROOT_PATH}/core) -set(TRANSPORT_TRANSPORT ${TRANSPORT_ROOT_PATH}/transport) -set(TRANSPORT_ERRORS ${TRANSPORT_ROOT_PATH}/errors) -set(TRANSPORT_UTILS ${TRANSPORT_ROOT_PATH}/utils) -set(TRANSPORT_HTTP ${TRANSPORT_ROOT_PATH}/http) -set(TRANSPORT_PORTABILITY ${TRANSPORT_ROOT_PATH}/portability) -set(TRANSPORT_INTERFACES ${TRANSPORT_ROOT_PATH}/interfaces) - set(LIBTRANSPORT hicntransport) if ((BUILD_HICNPLUGIN OR BUILD_MEMIF_CONNECTOR) AND "${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") set(__vpp__ 1) - set(LIBTRANSPORT ${LIBTRANSPORT}-memif) - find_package(Vpp REQUIRED) - - list(APPEND LIBTRANSPORT_INTERNAL_INCLUDE_DIRS - ${VPP_INCLUDE_DIRS} - ) endif () set(LIBTRANSPORT ${LIBTRANSPORT} CACHE INTERNAL "" FORCE) @@ -70,29 +56,15 @@ set(LIBTRANSPORT_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/src CACHE INTERNAL "" include(WindowsMacros) include(IosMacros) + find_package_wrapper(Libparc REQUIRED) find_package_wrapper(Asio REQUIRED) find_package(OpenSSL REQUIRED) - -if (${OPENSSL_VERSION} VERSION_EQUAL "1.1.1a" OR ${OPENSSL_VERSION} VERSION_GREATER "1.1.1a") - set(SECURE_HICNTRANSPORT 1) -endif() - -if (__vpp__) - find_package(Libmemif REQUIRED) -endif() +find_package(Threads REQUIRED) +find_package(OpenSSL REQUIRED) +find_package(Libconfig++ REQUIRED) if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) - if (__vpp__) - find_package(HicnPlugin REQUIRED) - find_package(SafeVapi REQUIRED) - - list(APPEND LIBRARIES - ${LIBMEMIF_LIBRARIES} - ${SAFE_VAPI_LIBRARIES} - ) - endif() - find_package_wrapper(Libhicn REQUIRED) else() if (DISABLE_SHARED_LIBRARIES) @@ -109,33 +81,11 @@ else() list(APPEND DEPENDENCIES ${LIBHICN_SHARED} ) - - if (__vpp__) - list(APPEND DEPENDENCIES - ${SAFE_VAPI_SHARED} - ) - - list(APPEND LIBRARIES - ${LIBMEMIF_LIBRARIES} - ${SAFE_VAPI_LIBRARIES} - ) - endif() endif() endif() -list(APPEND LIBTRANSPORT_INTERNAL_INCLUDE_DIRS - ${LIBMEMIF_INCLUDE_DIRS} - ${SAFE_VAPI_INCLUDE_DIRS} -) - include(Packaging) -find_package(Threads REQUIRED) - -if(${CMAKE_SYSTEM_NAME} STREQUAL "Android") - find_package(OpenSSL REQUIRED) -endif () - list(APPEND LIBRARIES ${HICN_LIBRARIES} ${LIBPARC_LIBRARIES} @@ -144,6 +94,7 @@ list(APPEND LIBRARIES ${ANDROID_LIBRARIES} ${OPENSSL_LIBRARIES} ${WINDOWS_LIBRARIES} + ${LIBCONFIG_CPP_LIBRARIES} ) # Include dirs -- Order does matter! @@ -155,6 +106,7 @@ list(APPEND LIBTRANSPORT_INTERNAL_INCLUDE_DIRS ${ASIO_INCLUDE_DIRS} ${WINDOWS_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} + ${CONFIG_INCLUDE_DIRS} ) add_subdirectory(includes/hicn/transport) diff --git a/libtransport/includes/hicn/transport/CMakeLists.txt b/libtransport/includes/hicn/transport/CMakeLists.txt index 1099e701d..ca53bdffd 100644 --- a/libtransport/includes/hicn/transport/CMakeLists.txt +++ b/libtransport/includes/hicn/transport/CMakeLists.txt @@ -22,7 +22,7 @@ add_subdirectory(errors) add_subdirectory(http) add_subdirectory(interfaces) add_subdirectory(portability) -add_subdirectory(security) +add_subdirectory(auth) add_subdirectory(utils) set(LIBTRANSPORT_INCLUDE_DIRS @@ -35,4 +35,4 @@ set(LIBHICNTRANSPORT_TO_INSTALL_HEADER_FILES ${HEADER_FILES} "" CACHE INTERNAL "" FORCE -) \ No newline at end of file +) diff --git a/libtransport/includes/hicn/transport/auth/CMakeLists.txt b/libtransport/includes/hicn/transport/auth/CMakeLists.txt new file mode 100644 index 000000000..d855125b0 --- /dev/null +++ b/libtransport/includes/hicn/transport/auth/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}/common.h + ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hash.h + ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hash_type.h + ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hasher.h + ${CMAKE_CURRENT_SOURCE_DIR}/crypto_suite.h + ${CMAKE_CURRENT_SOURCE_DIR}/identity.h + ${CMAKE_CURRENT_SOURCE_DIR}/key_id.h + ${CMAKE_CURRENT_SOURCE_DIR}/policies.h + ${CMAKE_CURRENT_SOURCE_DIR}/signer.h + ${CMAKE_CURRENT_SOURCE_DIR}/verifier.h +) + +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) diff --git a/libtransport/includes/hicn/transport/auth/common.h b/libtransport/includes/hicn/transport/auth/common.h new file mode 100644 index 000000000..911bcbc6a --- /dev/null +++ b/libtransport/includes/hicn/transport/auth/common.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 + +namespace transport { +namespace auth { + +using Hash = std::vector; +using HashEntry = std::pair; +using PacketPtr = core::Packet *; +using Suffix = uint32_t; + +} // namespace auth +} // namespace transport diff --git a/libtransport/includes/hicn/transport/auth/crypto_hash.h b/libtransport/includes/hicn/transport/auth/crypto_hash.h new file mode 100644 index 000000000..26c251b38 --- /dev/null +++ b/libtransport/includes/hicn/transport/auth/crypto_hash.h @@ -0,0 +1,121 @@ +/* + * 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 +#include +#include +#include + +extern "C" { +#include +}; + +#include +#include + +namespace transport { +namespace auth { + +class CryptoHasher; + +struct EnumClassHash { + template + std::size_t operator()(T t) const { + return static_cast(t); + } +}; + +static std::unordered_map + 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 + CryptoHash(const T* buffer, std::size_t length, CryptoHashType hash_type) { + hash_ = parcCryptoHash_CreateFromArray( + static_cast(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 + utils::Array getDigest() const { + return utils::Array( + static_cast(parcBuffer_Overlay(parcCryptoHash_GetDigest(hash_), 0)), + parcBuffer_Remaining(parcCryptoHash_GetDigest(hash_))); + } + + CryptoHashType getType() { + return static_cast(parcCryptoHash_GetDigestType(hash_)); + } + + template + 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( + std::memcmp(digest1, digest2, hash_size_map[hash_type])); + } + + TRANSPORT_ALWAYS_INLINE void display() { + parcBuffer_Display(parcCryptoHash_GetDigest(hash_), 2); + } + + private: + PARCCryptoHash* hash_; +}; + +} // namespace auth +} // namespace transport diff --git a/libtransport/includes/hicn/transport/auth/crypto_hash_type.h b/libtransport/includes/hicn/transport/auth/crypto_hash_type.h new file mode 100644 index 000000000..9d792624e --- /dev/null +++ b/libtransport/includes/hicn/transport/auth/crypto_hash_type.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 +}; + +#include + +namespace transport { +namespace auth { + +enum class CryptoHashType : uint8_t { + SHA_256 = PARCCryptoHashType_SHA256, + SHA_512 = PARCCryptoHashType_SHA512, + CRC32C = PARCCryptoHashType_CRC32C, + NULL_HASH = PARCCryptoHashType_NULL +}; + +} // namespace auth +} // namespace transport diff --git a/libtransport/includes/hicn/transport/auth/crypto_hasher.h b/libtransport/includes/hicn/transport/auth/crypto_hasher.h new file mode 100644 index 000000000..ada1a6ee8 --- /dev/null +++ b/libtransport/includes/hicn/transport/auth/crypto_hasher.h @@ -0,0 +1,70 @@ +/* + * 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 + +extern "C" { +#include +}; + +namespace transport { +namespace auth { + +class CryptoHasher { + public: + CryptoHasher(CryptoHashType hash_type) + : hasher_(parcCryptoHasher_Create( + static_cast(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 + 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 auth +} // namespace transport diff --git a/libtransport/includes/hicn/transport/auth/crypto_suite.h b/libtransport/includes/hicn/transport/auth/crypto_suite.h new file mode 100644 index 000000000..11df6ac06 --- /dev/null +++ b/libtransport/includes/hicn/transport/auth/crypto_suite.h @@ -0,0 +1,39 @@ +/* + * 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 +}; + +#include + +namespace transport { +namespace auth { + +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 +}; + +} // namespace auth +} // namespace transport diff --git a/libtransport/includes/hicn/transport/auth/identity.h b/libtransport/includes/hicn/transport/auth/identity.h new file mode 100644 index 000000000..19157952e --- /dev/null +++ b/libtransport/includes/hicn/transport/auth/identity.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +extern "C" { +#include +#include +#include +#include +}; + +namespace transport { +namespace auth { + +class Identity { + // This class holds several information about a client, including its public + // key. + public: + // Generate a new identity from the given parameters. The identity will be + // saved in 'keystore_path' and encrypted using 'keystore_pwd'. + Identity(const std::string &keystore_path, const std::string &keystore_pwd, + CryptoSuite suite, unsigned int signature_len, + unsigned int validity_days, const std::string &subject_name); + + // Create an identity from an already existing keystore path. + Identity(std::string &keystore_path, std::string &keystore_pwd, + CryptoHashType hash_type); + + Identity(const Identity &other); + Identity(Identity &&other); + ~Identity(); + + // Return the asymmetric signer object created from the public key. + std::shared_ptr getSigner() const; + + // Return the key store filename. + std::string getFilename() const; + + // Return the key store password. + std::string getPassword() const; + + // Generate a new random identity. + static Identity generateIdentity(const std::string &subject_name = ""); + + private: + PARCIdentity *identity_; + std::shared_ptr signer_; +}; + +} // namespace auth +} // namespace transport diff --git a/libtransport/includes/hicn/transport/auth/key_id.h b/libtransport/includes/hicn/transport/auth/key_id.h new file mode 100644 index 000000000..3aa09336f --- /dev/null +++ b/libtransport/includes/hicn/transport/auth/key_id.h @@ -0,0 +1,27 @@ +/* + * 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 +#include + +namespace transport { +namespace auth { + +using KeyId = std::pair; + +} // namespace auth +} // namespace transport diff --git a/libtransport/includes/hicn/transport/auth/policies.h b/libtransport/includes/hicn/transport/auth/policies.h new file mode 100644 index 000000000..00464d54b --- /dev/null +++ b/libtransport/includes/hicn/transport/auth/policies.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 auth { + +/** + * These policies allows the verifier to tell the transport what action to + * perform after verification. + */ +enum class VerificationPolicy { + ABORT, + ACCEPT, + DROP, + UNKNOWN, +}; + +} // namespace auth +} // namespace transport diff --git a/libtransport/includes/hicn/transport/auth/signer.h b/libtransport/includes/hicn/transport/auth/signer.h new file mode 100644 index 000000000..fd5c4e6c6 --- /dev/null +++ b/libtransport/includes/hicn/transport/auth/signer.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +extern "C" { +#include +#include +#include +#include +} + +namespace transport { +namespace auth { + +class Signer { + // The base class from which all signer classes derive. + public: + Signer(); + + Signer(PARCSigner *signer); + + virtual ~Signer(); + + // Sign a packet. + virtual void signPacket(PacketPtr packet); + + // Set the signer object used to sign packets. + void setSigner(PARCSigner *signer); + + // Return the signature size. + size_t getSignatureSize() const; + + // Return the crypto suite associated to the signer. + CryptoSuite getCryptoSuite() const; + + // Return the hash algorithm associated to the signer. + CryptoHashType getCryptoHashType() const; + + // Return the PARC signer. + PARCSigner *getParcSigner() const; + + // Return the PARC key store containing the signer key. + PARCKeyStore *getParcKeyStore() const; + + protected: + PARCSigner *signer_; + PARCKeyId *key_id_; +}; + +class AsymmetricSigner : public Signer { + // This class uses asymmetric verification to sign packets. The public key + // must be given from a PARCKeyStore. + public: + AsymmetricSigner() = default; + AsymmetricSigner(PARCSigner *signer) : Signer(signer){}; + + // Construct an AsymmetricSigner from a key store and a given crypto suite. + AsymmetricSigner(CryptoSuite suite, PARCKeyStore *key_store); +}; + +class SymmetricSigner : public Signer { + // This class uses symmetric verification to sign packets. The symmetric + // key is derived from a passphrase. + public: + SymmetricSigner() = default; + SymmetricSigner(PARCSigner *signer) : Signer(signer){}; + + // Construct an SymmetricSigner from a key store and a given crypto suite. + SymmetricSigner(CryptoSuite suite, PARCKeyStore *key_store); + + // Construct an AsymmetricSigner from a passphrase and a given crypto suite. + SymmetricSigner(CryptoSuite suite, const std::string &passphrase); +}; + +} // namespace auth +} // namespace transport diff --git a/libtransport/includes/hicn/transport/auth/verifier.h b/libtransport/includes/hicn/transport/auth/verifier.h new file mode 100644 index 000000000..e6e561918 --- /dev/null +++ b/libtransport/includes/hicn/transport/auth/verifier.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +extern "C" { +#include +#include +#include +#include +#include +} + +namespace transport { +namespace auth { + +class Verifier { + // The base class from which all verifier classes derive. + public: + // The VerificationFailedCallback will be called by the transport if a data + // packet (either a manifest or a content object) cannot be verified. The + // application decides what to do then by returning a VerificationPolicy + // object. + using VerificationFailedCallback = std::function; + + // The list of VerificationPolicy that will trigger the + // VerificationFailedCallback. + static const std::vector DEFAULT_FAILED_POLICIES; + + Verifier(); + + virtual ~Verifier(); + + // Verify a single packet and return whether or not the packet signature is + // valid. + virtual bool verifyPacket(PacketPtr packet); + + // Verify a batch of packets. Return a vector with the same size as the packet + // list, element i of that vector will contain the VerificationPolicy for + // packet i. + virtual std::vector verifyPackets( + const std::vector &packets); + VerificationPolicy verifyPackets(PacketPtr packet) { + return verifyPackets(std::vector{packet}).front(); + } + + // Verify that a batch of packets are valid using a map from packet suffixes + // to hashes. A packet is considered valid if its hash correspond to the hash + // present in the map. Return a vector with the same size as the packet list, + // element i of that vector will contain the VerificationPolicy for packet i. + virtual std::vector verifyPackets( + const std::vector &packets, + const std::unordered_map &suffix_map); + VerificationPolicy verifyPackets( + PacketPtr packet, + const std::unordered_map &suffix_map) { + return verifyPackets(std::vector{packet}, suffix_map).front(); + } + + // Add a general PARC key which can be used to verify packet signatures. + void addKey(PARCKey *key); + + // Set the hasher object used to compute packet hashes. + void setHasher(PARCCryptoHasher *hasher); + + // Set the callback for the case packet verification fails. + void setVerificationFailedCallback( + VerificationFailedCallback verification_failed_cb, + const std::vector &failed_policies = + DEFAULT_FAILED_POLICIES); + + // Retrieve the VerificationFailedCallback function. + void getVerificationFailedCallback( + VerificationFailedCallback **verification_failed_cb); + + static size_t getSignatureSize(const PacketPtr); + + protected: + PARCCryptoHasher *hasher_; + PARCVerifier *verifier_; + VerificationFailedCallback verification_failed_cb_; + std::vector failed_policies_; + + // Internally compute a packet hash using the hasher object. + virtual CryptoHash computeHash(PacketPtr packet); + + // Call VerificationFailedCallback if it is set and update the packet policy. + void callVerificationFailedCallback(PacketPtr packet, + VerificationPolicy &policy); +}; + +class VoidVerifier : public Verifier { + // This class is the default socket verifier. It ignores completely the packet + // signature and always returns true. + public: + bool verifyPacket(PacketPtr packet) override; + + std::vector verifyPackets( + const std::vector &packets) override; + + std::vector verifyPackets( + const std::vector &packets, + const std::unordered_map &suffix_map) override; +}; + +class AsymmetricVerifier : public Verifier { + // This class uses asymmetric verification to validate packets. The public key + // can be set directly or extracted from a certificate. + public: + AsymmetricVerifier() = default; + + // Add a public key to the verifier. + AsymmetricVerifier(PARCKey *pub_key); + + // Construct an AsymmetricVerifier from a certificate file. + AsymmetricVerifier(const std::string &cert_path); + + // Extract the public key of a certificate file. + void setCertificate(const std::string &cert_path); +}; + +class SymmetricVerifier : public Verifier { + // This class uses symmetric verification to validate packets. The symmetric + // key is derived from a passphrase. + public: + SymmetricVerifier() = default; + + // Construct a SymmetricVerifier from a passphrase. + SymmetricVerifier(const std::string &passphrase); + + ~SymmetricVerifier(); + + // Create and set a symmetric key from a passphrase. + void setPassphrase(const std::string &passphrase); + + // Construct a signer object. Passphrase must be set beforehand. + void setSigner(const PARCCryptoSuite &suite); + + virtual std::vector verifyPackets( + const std::vector &packets) override; + + protected: + PARCBuffer *passphrase_; + PARCSigner *signer_; +}; + +} // namespace auth +} // namespace transport diff --git a/libtransport/includes/hicn/transport/core/CMakeLists.txt b/libtransport/includes/hicn/transport/core/CMakeLists.txt index cb10745ff..2553b7dcd 100644 --- a/libtransport/includes/hicn/transport/core/CMakeLists.txt +++ b/libtransport/includes/hicn/transport/core/CMakeLists.txt @@ -20,6 +20,10 @@ list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/packet.h ${CMAKE_CURRENT_SOURCE_DIR}/payload_type.h ${CMAKE_CURRENT_SOURCE_DIR}/prefix.h + ${CMAKE_CURRENT_SOURCE_DIR}/io_module.h + ${CMAKE_CURRENT_SOURCE_DIR}/connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/endpoint.h + ${CMAKE_CURRENT_SOURCE_DIR}/global_object_pool.h ) set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/libtransport/includes/hicn/transport/core/connector.h b/libtransport/includes/hicn/transport/core/connector.h new file mode 100644 index 000000000..dcf38cdc8 --- /dev/null +++ b/libtransport/includes/hicn/transport/core/connector.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace transport { + +namespace core { + +class Connector : public std::enable_shared_from_this { + public: + enum class Type : uint8_t { + SOCKET_CONNECTOR, + MEMIF_CONNECTOR, + LOOPBACK_CONNECTOR, + }; + + enum class State : std::uint8_t { + CLOSED, + CONNECTING, + CONNECTED, + }; + + enum class Role : std::uint8_t { CONSUMER, PRODUCER }; + + public: + static constexpr std::size_t queue_size = 4096; + static constexpr std::uint32_t invalid_connector = ~0; + +#ifdef LINUX + static constexpr std::uint16_t max_burst = 256; +#endif + + using Ptr = std::shared_ptr; + using PacketQueue = std::deque; + using PacketReceivedCallback = std::function; + using PacketSentCallback = + std::function; + using OnCloseCallback = std::function; + using OnReconnectCallback = std::function; + using Id = std::uint64_t; + + template + Connector(ReceiveCallback &&receive_callback, SentCallback &&packet_sent, + OnClose &&close_callback, OnReconnect &&on_reconnect) + : receive_callback_(std::forward(receive_callback)), + sent_callback_(std::forward(packet_sent)), + on_close_callback_(std::forward(close_callback)), + on_reconnect_callback_(std::forward(on_reconnect)), + state_(State::CLOSED), + connector_id_(invalid_connector) {} + + virtual ~Connector(){}; + + template + void setReceiveCallback(ReceiveCallback &&callback) { + receive_callback_ = std::forward(callback); + } + + template + void setSentCallback(SentCallback &&callback) { + sent_callback_ = std::forward(callback); + } + + template + void setOnCloseCallback(OnClose &&callback) { + on_close_callback_ = std::forward(callback); + } + + template + void setReconnectCallback(const OnReconnect &&callback) { + on_reconnect_callback_ = std::forward(callback); + } + + const PacketReceivedCallback &getReceiveCallback() const { + return receive_callback_; + } + + const PacketSentCallback &getSentCallback() { return sent_callback_; } + + const OnCloseCallback &getOnCloseCallback() { return on_close_callback_; } + + const OnReconnectCallback &getOnReconnectCallback() { + return on_reconnect_callback_; + } + + virtual void send(Packet &packet) = 0; + + virtual void send(const uint8_t *packet, std::size_t len) = 0; + + virtual void close() = 0; + + virtual State state() { return state_; }; + + virtual bool isConnected() { return state_ == State::CONNECTED; } + + void setConnectorId(Id connector_id) { connector_id_ = connector_id; } + + Id getConnectorId() { return connector_id_; } + + void setConnectorName(std::string connector_name) { + connector_name_ = connector_name; + } + + std::string getConnectorName() { return connector_name_; } + + Endpoint getLocalEndpoint() { return local_endpoint_; } + + Endpoint getRemoteEndpoint() { return remote_endpoint_; } + + void setRole(Role r) { role_ = r; } + + Role getRole() { return role_; } + + static utils::MemBuf::Ptr getPacketFromBuffer(uint8_t *buffer, + std::size_t size) { + utils::MemBuf::Ptr ret; + + auto format = Packet::getFormatFromBuffer(buffer, size); + + if (TRANSPORT_EXPECT_TRUE(format != HF_UNSPEC && !_is_icmp(format))) { + if (Packet::isInterest(buffer)) { + ret = core::PacketManager<>::getInstance() + .getPacketFromExistingBuffer(buffer, size); + } else { + ret = core::PacketManager<>::getInstance() + .getPacketFromExistingBuffer(buffer, size); + } + } else { + ret = core::PacketManager<>::getInstance().getMemBuf(buffer, size); + } + + return ret; + } + + static std::pair getRawBuffer() { + return core::PacketManager<>::getInstance().getRawBuffer(); + } + + protected: + inline void sendSuccess(const utils::MemBuf &packet) { + stats_.tx_packets_.fetch_add(1, std::memory_order_relaxed); + stats_.tx_bytes_.fetch_add(packet.length(), std::memory_order_relaxed); + } + + inline void receiveSuccess(const utils::MemBuf &packet) { + stats_.rx_packets_.fetch_add(1, std::memory_order_relaxed); + stats_.rx_bytes_.fetch_add(packet.length(), std::memory_order_relaxed); + } + + inline void sendFailed() { + stats_.drops_.fetch_add(1, std::memory_order_relaxed); + } + + protected: + PacketQueue output_buffer_; + + // Connector events + PacketReceivedCallback receive_callback_; + PacketSentCallback sent_callback_; + OnCloseCallback on_close_callback_; + OnReconnectCallback on_reconnect_callback_; + + // Connector state + std::atomic state_; + Id connector_id_; + + // Endpoints + Endpoint local_endpoint_; + Endpoint remote_endpoint_; + + // Connector name + std::string connector_name_; + + // Connector role + Role role_; + + // Stats + AtomicConnectorStats stats_; +}; + +} // namespace core +} // namespace transport diff --git a/libtransport/includes/hicn/transport/core/connector_stats.h b/libtransport/includes/hicn/transport/core/connector_stats.h new file mode 100644 index 000000000..1985331e9 --- /dev/null +++ b/libtransport/includes/hicn/transport/core/connector_stats.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Cisco and/or its affiliates. + */ + +#pragma once + +#include +#include +#include +#include + +namespace transport { +namespace core { + +struct AtomicConnectorStats { + AtomicConnectorStats() + : tx_packets_(0), tx_bytes_(0), rx_packets_(0), rx_bytes_(0), drops_(0) {} + std::atomic tx_packets_; + std::atomic tx_bytes_; + std::atomic rx_packets_; + std::atomic rx_bytes_; + std::atomic drops_; +}; + +struct ConnectorStats { + ConnectorStats() + : tx_packets_(0), tx_bytes_(0), rx_packets_(0), rx_bytes_(0), drops_(0) {} + std::uint64_t tx_packets_; + std::uint64_t tx_bytes_; + std::uint64_t rx_packets_; + std::uint64_t rx_bytes_; + std::uint64_t drops_; +}; + +using TableEntry = std::tuple; +using StatisticTable = std::vector; + +} // namespace core +} // namespace transport \ No newline at end of file diff --git a/libtransport/includes/hicn/transport/core/content_object.h b/libtransport/includes/hicn/transport/core/content_object.h index 822790e56..805bc814c 100644 --- a/libtransport/includes/hicn/transport/core/content_object.h +++ b/libtransport/includes/hicn/transport/core/content_object.h @@ -17,6 +17,7 @@ #include #include +#include namespace transport { @@ -27,24 +28,53 @@ namespace core { class ContentObject : public Packet { public: - using Ptr = utils::ObjectPool::Ptr; + using Ptr = std::shared_ptr; using HICNContentObject = hicn_header_t; - ContentObject(Packet::Format format = HF_INET6_TCP); + ContentObject(Packet::Format format = HF_INET6_TCP, + std::size_t additional_header_size = 0); - ContentObject(const Name &name, Packet::Format format = HF_INET6_TCP); + ContentObject(const Name &name, Packet::Format format = HF_INET6_TCP, + std::size_t additional_header_size = 0); - ContentObject(const Name &name, hicn_format_t format, const uint8_t *payload, + ContentObject(const Name &name, hicn_format_t format, + std::size_t additional_header_size, 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; + template + ContentObject(CopyBufferOp op, Args &&...args) + : Packet(op, std::forward(args)...) { + if (hicn_data_get_name(format_, packet_start_, name_.getStructReference()) < + 0) { + throw errors::MalformedPacketException(); + } + } + + template + ContentObject(WrapBufferOp op, Args &&...args) + : Packet(op, std::forward(args)...) { + if (hicn_data_get_name(format_, packet_start_, name_.getStructReference()) < + 0) { + throw errors::MalformedPacketException(); + } + } + + template + ContentObject(CreateOp op, Args &&...args) + : Packet(op, std::forward(args)...) { + if (hicn_data_get_name(format_, packet_start_, name_.getStructReference()) < + 0) { + throw errors::MalformedPacketException(); + } + } + + ContentObject(const ContentObject &content_object); + + ContentObject &operator=(const ContentObject &other); ContentObject(ContentObject &&content_object); - ~ContentObject() override; + ~ContentObject(); const Name &getName() const override; @@ -66,6 +96,8 @@ class ContentObject : public Packet { uint32_t getLifetime() const override; + auto shared_from_this() { return utils::shared_from(this); } + private: void resetForHash() override; }; diff --git a/libtransport/includes/hicn/transport/core/endpoint.h b/libtransport/includes/hicn/transport/core/endpoint.h new file mode 100644 index 000000000..4a19744a7 --- /dev/null +++ b/libtransport/includes/hicn/transport/core/endpoint.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#ifndef ASIO_STANDALONE +#define ASIO_STANDALONE +#endif +#include + +namespace transport { + +namespace core { + +const uint16_t INVALID_PORT = 0xffff; + +class Endpoint { + public: + Endpoint() : address_(), port_(INVALID_PORT) {} + + Endpoint(const Endpoint &other) + : address_(other.address_), port_(other.port_) {} + + Endpoint(Endpoint &&other) + : address_(std::move(other.address_)), port_(other.port_) {} + + Endpoint(std::string ip_address, uint32_t port) + : address_(asio::ip::address::from_string(ip_address)), port_(port) {} + + Endpoint(asio::ip::udp::endpoint endpoint) + : address_(endpoint.address()), port_(endpoint.port()) {} + + ~Endpoint() = default; + + Endpoint &operator=(const Endpoint &other) { + address_ = other.address_; + port_ = other.port_; + return *this; + } + + Endpoint &operator=(Endpoint &&other) { + address_ = std::move(other.address_); + port_ = std::move(other.port_); + return *this; + } + +#if 0 + template + Endpoint(Ip &&ip_address, Port &&port) + : address_(std::forward(ip_address)), + port_(std::forward(port)) {} +#endif + + asio::ip::address getAddress() { return address_; } + uint16_t getPort() { return port_; } + + void setAddress(uint32_t address) { + address_ = asio::ip::address(asio::ip::address_v4(address)); + } + + void setPort(uint16_t port) { port_ = port; } + + private: + asio::ip::address address_; + uint16_t port_; +}; +} // namespace core +} // namespace transport diff --git a/libtransport/includes/hicn/transport/core/global_object_pool.h b/libtransport/includes/hicn/transport/core/global_object_pool.h new file mode 100644 index 000000000..e0b6e373f --- /dev/null +++ b/libtransport/includes/hicn/transport/core/global_object_pool.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace transport { + +namespace core { + +template +class PacketManager + : public utils::Singleton> { + friend class utils::Singleton>; + + public: + using MemoryPool = utils::FixedBlockAllocator; + using RawBuffer = std::pair; + + struct PacketStorage { + std::array packet_and_shared_ptr; + std::max_align_t align; + }; + + utils::MemBuf::Ptr getMemBuf() { + utils::MemBuf *memory = nullptr; + + memory = reinterpret_cast(memory_pool_.allocateBlock()); + + utils::STLAllocator allocator(memory, + &memory_pool_); + auto offset = offsetof(PacketStorage, align); + auto ret = std::allocate_shared( + allocator, utils::MemBuf::WRAP_BUFFER, (uint8_t *)memory + offset, 0, + chunk_size - offset); + ret->clear(); + + return ret; + } + + utils::MemBuf::Ptr getMemBuf(uint8_t *buffer, std::size_t length) { + auto offset = offsetof(PacketStorage, align); + auto memory = buffer - offset; + utils::STLAllocator allocator( + (utils::MemBuf *)memory, &memory_pool_); + auto ret = std::allocate_shared( + allocator, utils::MemBuf::WRAP_BUFFER, (uint8_t *)buffer, length, + chunk_size - offset); + + return ret; + } + + template < + typename PacketType, typename... Args, + typename = std::enable_if_t::value>> + typename PacketType::Ptr getPacket(Args &&...args) { + PacketType *memory = nullptr; + + memory = reinterpret_cast(memory_pool_.allocateBlock()); + utils::STLAllocator allocator(memory, + &memory_pool_); + auto offset = offsetof(PacketStorage, align); + auto ret = std::allocate_shared( + allocator, PacketType::CREATE, (uint8_t *)memory + offset, 0, + chunk_size - offset, std::forward(args)...); + + return ret; + } + + std::pair getRawBuffer() { + uint8_t *memory = nullptr; + memory = reinterpret_cast(memory_pool_.allocateBlock()); + + auto offset = offsetof(PacketStorage, align); + memory += offset; + + return std::make_pair(memory, chunk_size - offset); + } + + template + typename PacketType::Ptr getPacketFromExistingBuffer(uint8_t *buffer, + std::size_t length, + Args &&...args) { + auto offset = offsetof(PacketStorage, align); + auto memory = reinterpret_cast(buffer - offset); + utils::STLAllocator allocator(memory, + &memory_pool_); + auto ret = std::allocate_shared( + allocator, PacketType::WRAP_BUFFER, (uint8_t *)buffer, length, + chunk_size - offset, std::forward(args)...); + + return ret; + } + + private: + PacketManager(std::size_t size = packet_pool_size) + : memory_pool_(MemoryPool::getInstance()), size_(0) {} + MemoryPool &memory_pool_; + std::atomic size_; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/includes/hicn/transport/core/interest.h b/libtransport/includes/hicn/transport/core/interest.h index c572afbff..b41b0c94a 100644 --- a/libtransport/includes/hicn/transport/core/interest.h +++ b/libtransport/includes/hicn/transport/core/interest.h @@ -17,6 +17,9 @@ #include #include +#include + +#include namespace transport { @@ -24,25 +27,57 @@ namespace core { class Interest : public Packet /*, public std::enable_shared_from_this*/ { - public: - using Ptr = utils::ObjectPool::Ptr; - - Interest(Packet::Format format = HF_INET6_TCP); - - Interest(const Name &interest_name, Packet::Format format = HF_INET6_TCP); + private: + struct InterestManifestHeader { + /* This can be 16 bits, but we use 32 bits for alignment */ + uint32_t n_suffixes; + /* Followed by the list of prefixes to ask */ + /* ... */ + }; - Interest(const uint8_t *buffer, std::size_t size); - Interest(MemBufPtr &&buffer); + public: + using Ptr = std::shared_ptr; + + Interest(Packet::Format format = HF_INET6_TCP, + std::size_t additional_header_size = 0); + + Interest(const Name &interest_name, Packet::Format format = HF_INET6_TCP, + std::size_t additional_header_size = 0); + + Interest(MemBuf &&buffer); + + template + Interest(CopyBufferOp op, Args &&...args) + : Packet(op, std::forward(args)...) { + if (hicn_interest_get_name(format_, packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + } + + template + Interest(WrapBufferOp op, Args &&...args) + : Packet(op, std::forward(args)...) { + if (hicn_interest_get_name(format_, packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + } + + template + Interest(CreateOp op, Args &&...args) + : Packet(op, std::forward(args)...) {} + + /* Move constructor */ + Interest(Interest &&other_interest); - /* - * Enforce zero-copy. - */ - Interest(const Interest &other_interest) = delete; - Interest &operator=(const Interest &other_interest) = delete; + /* Copy constructor */ + Interest(const Interest &other_interest); - Interest(Interest &&other_interest); + /* Assginemnt operator */ + Interest &operator=(const Interest &other); - ~Interest() override; + ~Interest(); const Name &getName() const override; @@ -60,8 +95,21 @@ class Interest uint32_t getLifetime() const override; + bool hasManifest(); + + void appendSuffix(std::uint32_t suffix); + + void encodeSuffixes(); + + uint32_t *firstSuffix(); + + uint32_t numberOfSuffixes(); + + auto shared_from_this() { return utils::shared_from(this); } + private: void resetForHash() override; + std::set suffix_set_; }; } // end namespace core diff --git a/libtransport/includes/hicn/transport/core/io_module.h b/libtransport/includes/hicn/transport/core/io_module.h new file mode 100644 index 000000000..d4c3bb03a --- /dev/null +++ b/libtransport/includes/hicn/transport/core/io_module.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include +#include +#include +#include + +#include + +#ifndef ASIO_STANDALONE +#define ASIO_STANDALONE +#endif +#include + +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; + +class Connector; + +class IoModule { + protected: + IoModule() + : inet_address_({}), + inet6_address_({}), + mtu_(1500), + output_interface_(""), + content_store_reserved_(5000) { + inet_address_.v4.as_u32 = htonl(0x7f00001); + inet6_address_.v6.as_u8[15] = 0x01; + } + + public: + static IoModule *load(const char *); + static bool unload(IoModule *); + + public: + virtual ~IoModule(); + + virtual void connect(bool is_consumer = true) = 0; + + virtual bool isConnected() = 0; + + virtual void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::OnReconnectCallback &&reconnect_callback, + asio::io_service &io_service, + const std::string &app_name = "Libtransport") = 0; + + virtual void registerRoute(const Prefix &prefix) = 0; + + virtual std::uint32_t getMtu() = 0; + + virtual bool isControlMessage(const uint8_t *message) = 0; + + virtual void processControlMessageReply(utils::MemBuf &packet_buffer) = 0; + + virtual void closeConnection() = 0; + + virtual void send(Packet &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_); + } + } + + virtual void send(const uint8_t *packet, std::size_t len) = 0; + + void setContentStoreSize(uint32_t cs_size) { + content_store_reserved_ = cs_size; + } + + uint32_t getContentStoreSize() const { return content_store_reserved_; } + + void setOutputInterface(const std::string &interface) { + output_interface_ = interface; + } + + const std::string &getOutputInterface() { return output_interface_; } + +#ifndef ANDROID + private: + void *handle_; +#endif + + protected: + ip_address_t inet_address_; + ip_address_t inet6_address_; + uint16_t mtu_; + std::string output_interface_; + uint32_t content_store_reserved_; + Counters counters_; +}; + +extern "C" IoModule *createModule(); + +} // namespace core +} // namespace transport diff --git a/libtransport/includes/hicn/transport/core/name.h b/libtransport/includes/hicn/transport/core/name.h index ea72797ad..033582289 100644 --- a/libtransport/includes/hicn/transport/core/name.h +++ b/libtransport/includes/hicn/transport/core/name.h @@ -77,6 +77,8 @@ class Name { operator bool() const; + bool isValid() const; + std::string toString() const; bool equals(const Name &name, bool consider_segment = true) const; @@ -125,14 +127,14 @@ struct compare2 {}; template <> struct compare2 { - size_t operator()(const transport::core::Name &name1, const transport::core::Name &name2) const; + size_t operator()(const transport::core::Name &name1, + const transport::core::Name &name2) const; }; } // end namespace core } // end namespace transport - namespace std { template <> struct hash { diff --git a/libtransport/includes/hicn/transport/core/packet.h b/libtransport/includes/hicn/transport/core/packet.h index 91f957964..68daea841 100644 --- a/libtransport/includes/hicn/transport/core/packet.h +++ b/libtransport/includes/hicn/transport/core/packet.h @@ -19,21 +19,15 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include #include #include #include -namespace utils { -class Signer; -class Verifier; -} // namespace utils - namespace transport { - namespace core { /* @@ -45,11 +39,13 @@ namespace core { * \_______________________________________| */ -class Packet : public std::enable_shared_from_this { - friend class utils::Signer; - friend class utils::Verifier; +class Packet : public utils::MemBuf, + public std::enable_shared_from_this { + friend class auth::Signer; + friend class auth::Verifier; public: + using Ptr = std::shared_ptr; using MemBufPtr = std::shared_ptr; using Format = hicn_format_t; static constexpr size_t default_mtu = 1500; @@ -59,24 +55,29 @@ class Packet : public std::enable_shared_from_this { * 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); + Packet(Format format = HF_INET6_TCP, std::size_t additional_header_size = 0); /** * 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; + /* Copy buffer */ + Packet(CopyBufferOp, const uint8_t *buffer, std::size_t size); + /* Wrap buffer */ + Packet(WrapBufferOp, uint8_t *buffer, std::size_t length, std::size_t size); + /* Create new using pre-allocated buffer */ + Packet(CreateOp, uint8_t *buffer, std::size_t length, std::size_t size, + Format format = HF_INET6_TCP, std::size_t additional_header_size = 0); + /* Move MemBuf */ + Packet(MemBuf &&buffer); + + Packet(Packet &&other); /* - * Move constructor. + * Copy constructor and assignemnt operators. */ - Packet(Packet &&other); + Packet(const Packet &other); + Packet &operator=(const Packet &other); friend bool operator==(const Packet &l_packet, const Packet &r_packet); @@ -98,36 +99,35 @@ class Packet : public std::enable_shared_from_this { static bool isInterest(const uint8_t *buffer); + bool isInterest(); + static Format getFormatFromBuffer(const uint8_t *buffer, std::size_t length) { Format format = HF_UNSPEC; - - if (TRANSPORT_EXPECT_FALSE( - hicn_packet_get_format((const hicn_header_t *)buffer, &format) < - 0)) { - TRANSPORT_LOGE( - "Error while getting format from packet buffer. Packet will be " - "discarded."); - hicn_packet_dump(buffer, length); - } - + hicn_packet_get_format((const hicn_header_t *)buffer, &format); return format; } - TRANSPORT_ALWAYS_INLINE void replace(MemBufPtr &&buffer) { - packet_ = std::move(buffer); - packet_start_ = reinterpret_cast(packet_->writableData()); - header_head_ = packet_.get(); - payload_head_ = nullptr; - format_ = getFormatFromBuffer(reinterpret_cast(packet_start_), - packet_->length()); + void reset() { + clear(); + packet_start_ = reinterpret_cast(writableData()); + header_offset_ = 0; + format_ = HF_UNSPEC; + payload_type_ = PayloadType::UNSPECIFIED; name_.clear(); + + if (isChained()) { + separateChain(next(), prev()); + } } + void setFormat(Packet::Format format = HF_INET6_TCP, + std::size_t additional_header_size = 0); + std::size_t payloadSize() const; std::size_t headerSize() const; - const std::shared_ptr acquireMemBufReference() const; + std::shared_ptr acquireMemBufReference(); virtual const Name &getName() const = 0; @@ -145,25 +145,8 @@ class Packet : public std::enable_shared_from_this { Packet &appendPayload(std::unique_ptr &&payload); - Packet &appendHeader(std::unique_ptr &&header); - - Packet &appendHeader(const uint8_t *buffer, std::size_t length); - std::unique_ptr getPayload() const; - std::pair getPayloadReference() const { - int signature_size = 0; - - if (_is_ah(format_)) { - signature_size = (uint32_t)getSignatureSize(); - } - - auto header_size = getHeaderSizeFromFormat(format_, signature_size); - auto payload_length = payloadSize(); - - return std::make_pair(packet_->data() + header_size, payload_length); - } - Packet &updateLength(std::size_t length = 0); PayloadType getPayloadType() const; @@ -174,35 +157,38 @@ class Packet : public std::enable_shared_from_this { void dump() const; + static void dump(uint8_t *buffer, std::size_t length); + virtual void setLocator(const ip_address_t &locator) = 0; virtual ip_address_t getLocator() const = 0; - void setSignatureTimestamp(const uint64_t ×tamp); + /** + * @brief Set signature timestamp, in milliseconds. + */ + void setSignatureTimestamp(const uint64_t ×tamp_milliseconds); uint64_t getSignatureTimestamp() const; - void setValidationAlgorithm(const utils::CryptoSuite &validation_algorithm); + void setValidationAlgorithm(const auth::CryptoSuite &validation_algorithm); - utils::CryptoSuite getValidationAlgorithm() const; + auth::CryptoSuite getValidationAlgorithm() const; - void setKeyId(const utils::KeyId &key_id); + void setKeyId(const auth::KeyId &key_id); - utils::KeyId getKeyId() const; + auth::KeyId getKeyId() const; - virtual utils::CryptoHash computeDigest( - utils::CryptoHashType algorithm) const; + virtual auth::CryptoHash computeDigest(auth::CryptoHashType algorithm) const; void 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); + uint16_t partial_csum = + csum(data() + HICN_V6_TCP_HDRLEN, length() - HICN_V6_TCP_HDRLEN, 0); + + for (utils::MemBuf *current = next(); current != this; + current = current->next()) { + partial_csum = csum(current->data(), current->length(), ~partial_csum); } + if (hicn_packet_compute_header_checksum(format_, packet_start_, partial_csum) < 0) { throw errors::MalformedPacketException(); @@ -234,12 +220,12 @@ class Packet : public std::enable_shared_from_this { Packet &setTTL(uint8_t hops); uint8_t getTTL() const; - void separateHeaderPayload(); - void resetPayload(); - private: virtual void resetForHash() = 0; void setSignatureSize(std::size_t size_bytes); + void prependPayload(const uint8_t **buffer, std::size_t *size); + + bool authenticationHeader() const { return _is_ah(format_); } std::size_t getSignatureSize() const { size_t size_bytes; @@ -256,12 +242,11 @@ class Packet : public std::enable_shared_from_this { uint8_t *getSignature() const; protected: - Name name_; - MemBufPtr packet_; hicn_header_t *packet_start_; - utils::MemBuf *header_head_; - utils::MemBuf *payload_head_; + std::size_t header_offset_; mutable Format format_; + Name name_; + mutable PayloadType payload_type_; static const core::Name base_name; }; diff --git a/libtransport/includes/hicn/transport/core/payload_type.h b/libtransport/includes/hicn/transport/core/payload_type.h index fa79db35a..8c918f792 100644 --- a/libtransport/includes/hicn/transport/core/payload_type.h +++ b/libtransport/includes/hicn/transport/core/payload_type.h @@ -20,8 +20,9 @@ namespace transport { namespace core { enum class PayloadType : uint16_t { - CONTENT_OBJECT = HPT_DATA, + DATA = HPT_DATA, MANIFEST = HPT_MANIFEST, + UNSPECIFIED = HPT_UNSPEC }; } // end namespace core diff --git a/libtransport/includes/hicn/transport/core/prefix.h b/libtransport/includes/hicn/transport/core/prefix.h index c3805f13f..7ef667bc8 100644 --- a/libtransport/includes/hicn/transport/core/prefix.h +++ b/libtransport/includes/hicn/transport/core/prefix.h @@ -35,9 +35,9 @@ class Prefix { Prefix(const core::Name &content_name, uint16_t prefix_length); - std::unique_ptr toSockaddr(); + std::unique_ptr toSockaddr() const; - uint16_t getPrefixLength(); + uint16_t getPrefixLength() const; Prefix &setPrefixLength(uint16_t prefix_length); @@ -58,13 +58,13 @@ class Prefix { Prefix &setNetwork(std::string &network); - int getAddressFamily(); + int getAddressFamily() const; Prefix &setAddressFamily(int address_family); Name makeRandomName() const; - ip_prefix_t &toIpPrefixStruct(); + const ip_prefix_t &toIpPrefixStruct() const; private: static bool checkPrefixLengthAndAddressFamily(uint16_t prefix_length, diff --git a/libtransport/includes/hicn/transport/errors/errors.h b/libtransport/includes/hicn/transport/errors/errors.h index 512e35736..b659820fa 100644 --- a/libtransport/includes/hicn/transport/errors/errors.h +++ b/libtransport/includes/hicn/transport/errors/errors.h @@ -15,10 +15,13 @@ #pragma once +#include #include +#include #include #include #include #include #include -#include \ No newline at end of file +#include +#include diff --git a/libtransport/includes/hicn/transport/http/client_connection.h b/libtransport/includes/hicn/transport/http/client_connection.h index 262756a09..7e78e9c59 100644 --- a/libtransport/includes/hicn/transport/http/client_connection.h +++ b/libtransport/includes/hicn/transport/http/client_connection.h @@ -68,7 +68,7 @@ class HTTPClientConnection { HTTPClientConnection &setTimeout(const std::chrono::seconds &timeout); - HTTPClientConnection &setCertificate(const std::string &cert_path); + HTTPClientConnection &setVerifier(std::shared_ptr verifier); private: class Implementation; @@ -77,4 +77,4 @@ class HTTPClientConnection { } // end namespace http -} // end namespace transport \ No newline at end of file +} // end namespace transport diff --git a/libtransport/includes/hicn/transport/interfaces/CMakeLists.txt b/libtransport/includes/hicn/transport/interfaces/CMakeLists.txt index 7370ad1b0..08f880930 100644 --- a/libtransport/includes/hicn/transport/interfaces/CMakeLists.txt +++ b/libtransport/includes/hicn/transport/interfaces/CMakeLists.txt @@ -16,12 +16,11 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/socket_consumer.h ${CMAKE_CURRENT_SOURCE_DIR}/socket_producer.h - ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_producer.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 ${CMAKE_CURRENT_SOURCE_DIR}/callbacks.h - ${CMAKE_CURRENT_SOURCE_DIR}/verification_policy.h + ${CMAKE_CURRENT_SOURCE_DIR}/global_conf_interface.h ${CMAKE_CURRENT_SOURCE_DIR}/statistics.h ${CMAKE_CURRENT_SOURCE_DIR}/portal.h ) diff --git a/libtransport/includes/hicn/transport/interfaces/callbacks.h b/libtransport/includes/hicn/transport/interfaces/callbacks.h index 6ae07797e..95b4d1977 100644 --- a/libtransport/includes/hicn/transport/interfaces/callbacks.h +++ b/libtransport/includes/hicn/transport/interfaces/callbacks.h @@ -16,7 +16,7 @@ #pragma once #include -#include +#include #include #include @@ -74,25 +74,6 @@ using ProducerContentCallback = std::function; -/** - * The ConsumerContentObjectVerificationCallback will be called by the transport - * if an application is willing to verify each content object. Note that a - * better alternative is to instrument the transport to perform the verification - * autonomously, without requiring the intervention of the application. - */ -using ConsumerContentObjectVerificationCallback = - std::function; - -/** - * The ConsumerContentObjectVerificationFailedCallback will be caled by the - * transport if a data packet (either manifest or content object) cannot be - * verified. The application here decides what to do by returning a - * VerificationFailedPolicy object. - */ -using ConsumerContentObjectVerificationFailedCallback = - std::function; - /** * The ProducerContentObjectCallback will be called in different parts of the * consumer socket processing pipeline, with a ProducerSocket and an diff --git a/libtransport/includes/hicn/transport/interfaces/global_conf_interface.h b/libtransport/includes/hicn/transport/interfaces/global_conf_interface.h new file mode 100644 index 000000000..a9fe6fac6 --- /dev/null +++ b/libtransport/includes/hicn/transport/interfaces/global_conf_interface.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +/** + * Global configuration interface. + */ + +namespace transport { +namespace interface { +namespace global_config { + +static const constexpr char io_module_section[] = "io_module"; +void parseConfigurationFile(const std::string& path = ""); + +class ConfigurationObject { + public: + /** + * Set configuration. + */ + void set(); + + /** + * Get configuration. + */ + void get(); + + /** + * Get configuration key + */ + virtual std::string getKey() const = 0; +}; + +class IoModuleConfiguration : public ConfigurationObject { + public: + std::string getKey() const override { return io_module_section; } + + std::string name; + std::vector search_path; +}; + +} // namespace global_config +} // namespace interface +} // namespace transport \ No newline at end of file diff --git a/libtransport/includes/hicn/transport/interfaces/p2psecure_socket_producer.h b/libtransport/includes/hicn/transport/interfaces/p2psecure_socket_producer.h index 6f0d48bb9..7b284c520 100644 --- a/libtransport/includes/hicn/transport/interfaces/p2psecure_socket_producer.h +++ b/libtransport/includes/hicn/transport/interfaces/p2psecure_socket_producer.h @@ -16,8 +16,7 @@ #pragma once #include - -#include +#include namespace transport { @@ -27,7 +26,7 @@ class P2PSecureProducerSocket : public ProducerSocket { public: P2PSecureProducerSocket(); P2PSecureProducerSocket(bool rtc, - const std::shared_ptr &identity); + const std::shared_ptr &identity); ~P2PSecureProducerSocket() = default; }; diff --git a/libtransport/includes/hicn/transport/interfaces/portal.h b/libtransport/includes/hicn/transport/interfaces/portal.h index 724cd7592..22c8591f4 100644 --- a/libtransport/includes/hicn/transport/interfaces/portal.h +++ b/libtransport/includes/hicn/transport/interfaces/portal.h @@ -23,7 +23,6 @@ #define ASIO_STANDALONE #endif #include - #include #define UNSET_CALLBACK 0 @@ -71,8 +70,7 @@ class Portal { */ class ConsumerCallback { public: - virtual void onContentObject(core::Interest::Ptr &&i, - core::ContentObject::Ptr &&c) = 0; + virtual void onContentObject(core::Interest &i, core::ContentObject &c) = 0; virtual void onTimeout(core::Interest::Ptr &&i) = 0; virtual void onError(std::error_code ec) = 0; }; @@ -83,12 +81,12 @@ class Portal { */ class ProducerCallback { public: - virtual void onInterest(core::Interest::Ptr &&i) = 0; + virtual void onInterest(core::Interest &i) = 0; virtual void onError(std::error_code ec) = 0; }; using OnContentObjectCallback = - std::function; + std::function; using OnInterestTimeoutCallback = std::function; Portal(); diff --git a/libtransport/includes/hicn/transport/interfaces/socket_consumer.h b/libtransport/includes/hicn/transport/interfaces/socket_consumer.h index 2447f9b5b..621e7ce6f 100644 --- a/libtransport/includes/hicn/transport/interfaces/socket_consumer.h +++ b/libtransport/includes/hicn/transport/interfaces/socket_consumer.h @@ -21,7 +21,7 @@ #include #include #include -#include +#include #ifndef ASIO_STANDALONE #define ASIO_STANDALONE @@ -207,14 +207,6 @@ class ConsumerSocket { int consume(const Name &name); int asyncConsume(const Name &name); - /** - * Verify the packets containing a key after the origin of the key has been - * validated by the client. - * - * @return true if all packets are valid, false otherwise - */ - bool verifyKeyPackets(); - /** * Stops the consumer socket. If several downloads are queued (using * asyncConsume), this call stops just the current one. @@ -251,14 +243,6 @@ class ConsumerSocket { int setSocketOption(int socket_option_key, ConsumerContentObjectCallback socket_option_value); - int setSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationFailedCallback socket_option_value); - - int setSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationCallback socket_option_value); - int setSocketOption(int socket_option_key, ConsumerInterestCallback socket_option_value); @@ -267,7 +251,7 @@ class ConsumerSocket { int setSocketOption( int socket_option_key, - const std::shared_ptr &socket_option_value); + const std::shared_ptr &socket_option_value); int setSocketOption(int socket_option_key, const std::string &socket_option_value); @@ -286,21 +270,13 @@ class ConsumerSocket { int getSocketOption(int socket_option_key, ConsumerContentObjectCallback **socket_option_value); - int getSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationFailedCallback **socket_option_value); - - int getSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationCallback **socket_option_value); - int getSocketOption(int socket_option_key, ConsumerInterestCallback **socket_option_value); int getSocketOption(int socket_option_key, IcnObserver **socket_option_value); int getSocketOption(int socket_option_key, - std::shared_ptr &socket_option_value); + std::shared_ptr &socket_option_value); int getSocketOption(int socket_option_key, std::string &socket_option_value); diff --git a/libtransport/includes/hicn/transport/interfaces/socket_options_default_values.h b/libtransport/includes/hicn/transport/interfaces/socket_options_default_values.h index bcf103b8c..f4945ac8a 100644 --- a/libtransport/includes/hicn/transport/interfaces/socket_options_default_values.h +++ b/libtransport/includes/hicn/transport/interfaces/socket_options_default_values.h @@ -16,6 +16,7 @@ #pragma once #include + #include #include diff --git a/libtransport/includes/hicn/transport/interfaces/socket_options_keys.h b/libtransport/includes/hicn/transport/interfaces/socket_options_keys.h index f50e919b4..00cd44075 100644 --- a/libtransport/includes/hicn/transport/interfaces/socket_options_keys.h +++ b/libtransport/includes/hicn/transport/interfaces/socket_options_keys.h @@ -31,6 +31,11 @@ typedef enum { RTC = 2, } TransportProtocolAlgorithms; +typedef enum { + BYTE_STREAM = 10, + RTC_PROD = 11, +} ProductionProtocolAlgorithms; + typedef enum { INPUT_BUFFER_SIZE = 101, OUTPUT_BUFFER_SIZE = 102, @@ -40,7 +45,6 @@ typedef enum { DATA_PACKET_SIZE = 106, INTEREST_LIFETIME = 107, CONTENT_OBJECT_EXPIRY_TIME = 108, - KEY_CONTENT = 110, MIN_WINDOW_SIZE = 111, MAX_WINDOW_SIZE = 112, CURRENT_WINDOW_SIZE = 113, @@ -50,12 +54,10 @@ typedef enum { RUNNING = 117, APPLICATION_BUFFER = 118, HASH_ALGORITHM = 119, - CRYPTO_SUITE = 120, SIGNER = 121, VERIFIER = 122, - CERTIFICATE = 123, - VERIFY_SIGNATURE = 124, STATS_INTERVAL = 125, + SUFFIX_STRATEGY = 126 } GeneralTransportOptions; typedef enum { @@ -98,6 +100,7 @@ typedef enum { CONTENT_OBJECT_READY = 510, CONTENT_OBJECT_OUTPUT = 511, CONTENT_PRODUCED = 512, + CONTENT_OBJECT_TO_SIGN = 513 } ProducerCallbacksOptions; typedef enum { OUTPUT_INTERFACE = 601 } DataLinkOptions; diff --git a/libtransport/includes/hicn/transport/interfaces/socket_producer.h b/libtransport/includes/hicn/transport/interfaces/socket_producer.h index e269fb83d..302b03f3f 100644 --- a/libtransport/includes/hicn/transport/interfaces/socket_producer.h +++ b/libtransport/includes/hicn/transport/interfaces/socket_producer.h @@ -21,7 +21,7 @@ #include #include #include -#include +#include #ifndef ASIO_STANDALONE #define ASIO_STANDALONE @@ -40,7 +40,10 @@ using namespace core; class ProducerSocket { public: - explicit ProducerSocket(int protocol = 0); + explicit ProducerSocket( + int protocol = ProductionProtocolAlgorithms::BYTE_STREAM); + + explicit ProducerSocket(int protocol, asio::io_service &io_service); virtual ~ProducerSocket(); @@ -48,22 +51,21 @@ class ProducerSocket { bool isRunning(); - uint32_t produce(Name content_name, const uint8_t *buffer, size_t buffer_size, - bool is_last = true, uint32_t start_offset = 0) { - return produce(content_name, utils::MemBuf::copyBuffer(buffer, buffer_size), - is_last, start_offset); - } + void registerPrefix(const Prefix &producer_namespace); - uint32_t produce(Name content_name, std::unique_ptr &&buffer, - bool is_last = true, uint32_t start_offset = 0); + uint32_t produceStream(const 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); + uint32_t produceStream(const Name &content_name, + std::unique_ptr &&buffer, + bool is_last = true, uint32_t start_offset = 0); - void produce(const uint8_t *buffer, size_t buffer_size) { - produce(utils::MemBuf::copyBuffer(buffer, buffer_size)); - } + uint32_t produceDatagram(const Name &content_name, const uint8_t *buffer, + size_t buffer_size); - void produce(std::unique_ptr &&buffer); + uint32_t produceDatagram(const Name &content_name, + std::unique_ptr &&buffer); void asyncProduce(const Name &suffix, const uint8_t *buf, size_t buffer_size, bool is_last = true, uint32_t *start_offset = nullptr); @@ -72,9 +74,7 @@ class ProducerSocket { bool is_last, uint32_t offset, uint32_t **last_segment = nullptr); - void asyncProduce(ContentObject &content_object); - - void registerPrefix(const Prefix &producer_namespace); + void produce(ContentObject &content_object); void serveForever(); @@ -104,14 +104,13 @@ class ProducerSocket { ProducerContentCallback socket_option_value); int setSocketOption(int socket_option_key, - utils::CryptoHashType socket_option_value); + auth::CryptoHashType socket_option_value); int setSocketOption(int socket_option_key, - utils::CryptoSuite socket_option_value); + auth::CryptoSuite socket_option_value); - int setSocketOption( - int socket_option_key, - const std::shared_ptr &socket_option_value); + int setSocketOption(int socket_option_key, + const std::shared_ptr &socket_option_value); int setSocketOption(int socket_option_key, const std::string &socket_option_value); @@ -133,13 +132,13 @@ class ProducerSocket { ProducerInterestCallback **socket_option_value); int getSocketOption(int socket_option_key, - utils::CryptoHashType &socket_option_value); + auth::CryptoHashType &socket_option_value); int getSocketOption(int socket_option_key, - utils::CryptoSuite &socket_option_value); + auth::CryptoSuite &socket_option_value); int getSocketOption(int socket_option_key, - std::shared_ptr &socket_option_value); + std::shared_ptr &socket_option_value); int getSocketOption(int socket_option_key, std::string &socket_option_value); diff --git a/libtransport/includes/hicn/transport/interfaces/statistics.h b/libtransport/includes/hicn/transport/interfaces/statistics.h index 26831fbf1..92c58da23 100644 --- a/libtransport/includes/hicn/transport/interfaces/statistics.h +++ b/libtransport/includes/hicn/transport/interfaces/statistics.h @@ -31,6 +31,8 @@ class IcnObserver { virtual void notifyDownloadTime(double downloadTime) = 0; }; +class ProductionStatistics {}; + class TransportStatistics { static constexpr double default_alpha = 0.7; @@ -43,7 +45,15 @@ class TransportStatistics { interest_tx_(0), alpha_(alpha), loss_ratio_(0.0), - queuing_delay_(0.0) {} + queuing_delay_(0.0), + interest_FEC_tx_(0), + bytes_FEC_received_(0), + lost_data_(0), + recovered_data_(0), + status_(-1), + // avg_data_rtt_(0), + avg_pending_pkt_(0.0), + received_nacks_(0) {} TRANSPORT_ALWAYS_INLINE void updateRetxCount(uint64_t retx) { retx_count_ += retx; @@ -74,6 +84,32 @@ class TransportStatistics { queuing_delay_ = queuing_delay; } + TRANSPORT_ALWAYS_INLINE void updateInterestFecTx(uint64_t int_tx) { + interest_FEC_tx_ += int_tx; + } + + TRANSPORT_ALWAYS_INLINE void updateBytesFecRecv(uint64_t bytes) { + bytes_FEC_received_ += bytes; + } + + TRANSPORT_ALWAYS_INLINE void updateLostData(uint64_t pkt) { + lost_data_ += pkt; + } + + TRANSPORT_ALWAYS_INLINE void updateRecoveredData(uint64_t bytes) { + recovered_data_ += bytes; + } + + TRANSPORT_ALWAYS_INLINE void updateCCState(int status) { status_ = status; } + + TRANSPORT_ALWAYS_INLINE void updateAveragePendingPktCount(double pkt) { + avg_pending_pkt_ = (alpha_ * avg_pending_pkt_) + ((1. - alpha_) * pkt); + } + + TRANSPORT_ALWAYS_INLINE void updateReceivedNacks(uint32_t nacks) { + received_nacks_ += nacks; + } + TRANSPORT_ALWAYS_INLINE uint64_t getRetxCount() const { return retx_count_; } TRANSPORT_ALWAYS_INLINE uint64_t getBytesRecv() const { @@ -96,6 +132,32 @@ class TransportStatistics { return queuing_delay_; } + TRANSPORT_ALWAYS_INLINE uint64_t getInterestFecTxCount() const { + return interest_FEC_tx_; + } + + TRANSPORT_ALWAYS_INLINE uint64_t getBytesFecRecv() const { + return bytes_FEC_received_; + } + + TRANSPORT_ALWAYS_INLINE uint64_t getLostData() const { return lost_data_; } + + TRANSPORT_ALWAYS_INLINE uint64_t getBytesRecoveredData() const { + return recovered_data_; + } + + TRANSPORT_ALWAYS_INLINE int getCCStatus() const { return status_; } + + TRANSPORT_ALWAYS_INLINE double getAveragePendingPktCount() const { + return avg_pending_pkt_; + } + + TRANSPORT_ALWAYS_INLINE uint32_t getReceivedNacks() const { + return received_nacks_; + } + + TRANSPORT_ALWAYS_INLINE void setAlpha(double val) { alpha_ = val; } + TRANSPORT_ALWAYS_INLINE void reset() { retx_count_ = 0; bytes_received_ = 0; @@ -103,6 +165,14 @@ class TransportStatistics { avg_window_size_ = 0; interest_tx_ = 0; loss_ratio_ = 0; + interest_FEC_tx_ = 0; + bytes_FEC_received_ = 0; + lost_data_ = 0; + recovered_data_ = 0; + status_ = 0; + // avg_data_rtt_ = 0; + avg_pending_pkt_ = 0; + received_nacks_ = 0; } private: @@ -114,6 +184,13 @@ class TransportStatistics { double alpha_; double loss_ratio_; double queuing_delay_; + uint64_t interest_FEC_tx_; + uint64_t bytes_FEC_received_; + uint64_t lost_data_; + uint64_t recovered_data_; + int status_; // transport status (e.g. sync status, congestion etc.) + double avg_pending_pkt_; + uint32_t received_nacks_; }; } // namespace interface diff --git a/libtransport/includes/hicn/transport/portability/c_portability.h b/libtransport/includes/hicn/transport/portability/c_portability.h index 71e976a81..9fe9ef90a 100644 --- a/libtransport/includes/hicn/transport/portability/c_portability.h +++ b/libtransport/includes/hicn/transport/portability/c_portability.h @@ -33,4 +33,4 @@ #define TRANSPORT_ALWAYS_INLINE inline __attribute__((__always_inline__)) #else #define TRANSPORT_ALWAYS_INLINE inline -#endif \ No newline at end of file +#endif diff --git a/libtransport/includes/hicn/transport/portability/platform.h b/libtransport/includes/hicn/transport/portability/platform.h new file mode 100644 index 000000000..282d27740 --- /dev/null +++ b/libtransport/includes/hicn/transport/portability/platform.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +/* + * Extract the "MACINTOSH" flag from the compiler. + */ +#if defined(__APPLE__) +#define UNIX +#define MACINTOSH +#endif + +/* + * Extract the "SUNOS" flag from the compiler. + */ +#if defined(sun) +#define UNIX +#define SUNOS +#endif + +/* + * Extract the "LINUX" flag from compiler. + */ +#ifdef __linux__ +#define UNIX +#define LINUX +#endif + +/* + * Extract the "ANDROID" flag from compiler. + */ +#ifdef __ANDROID__ +#define UNIX +#define LINUX +#ifndef ANDROID +#define ANDROID +#endif +#endif + +/* + * Extract the "BSD" flag from compiler. + */ +#if defined(BSD) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__OpenBSD__) +#define OS_BSD +#define UNIX +#endif + +/* + * Extract the "MSDOS" flag from the compiler. + */ +#ifdef __MSDOS__ +#define MSDOS +#undef UNIX +#endif + +/* + * Extract the "WINDOWS" flag from the compiler. + */ +#if defined(_Windows) || defined(__WINDOWS__) || defined(__WIN32__) || \ + defined(WIN32) || defined(__WINNT__) || defined(__NT__) || \ + defined(_WIN32) || defined(_WIN64) +#define WINDOWS +#ifdef _MSC_VER +#define MSV +#if defined(DEBUG) || defined(DEBUGTRACE) +#ifdef NDEBUG +#undef NDEBUG +#endif +#else +#ifndef NDEBUG +#define NDEBUG +#endif +#endif +#else +#undef MSV +#endif +#undef UNIX +#undef MSDOS +#endif + +/* + * Remove the WINDOWS flag when using MACINTOSH. + */ +#ifdef MACINTOSH +#undef WINDOWS +#endif + +/* + * Assume UNIX if not Windows, Macintosh or MSDOS. + */ +#if !defined(WINDOWS) && !defined(MACINTOSH) && !defined(MSDOS) +#define UNIX +#endif diff --git a/libtransport/includes/hicn/transport/portability/portability.h b/libtransport/includes/hicn/transport/portability/portability.h index 1d97a346e..539ce2d5a 100644 --- a/libtransport/includes/hicn/transport/portability/portability.h +++ b/libtransport/includes/hicn/transport/portability/portability.h @@ -22,8 +22,8 @@ #endif #include - #include + #include namespace portability { diff --git a/libtransport/includes/hicn/transport/portability/win_portability.h b/libtransport/includes/hicn/transport/portability/win_portability.h index 65d949291..bfbe431d1 100644 --- a/libtransport/includes/hicn/transport/portability/win_portability.h +++ b/libtransport/includes/hicn/transport/portability/win_portability.h @@ -31,6 +31,7 @@ #include #include #include + #include #define __ORDER_LITTLE_ENDIAN__ 0x41424344UL diff --git a/libtransport/includes/hicn/transport/utils/CMakeLists.txt b/libtransport/includes/hicn/transport/utils/CMakeLists.txt index 11a9b0f25..7094601f4 100644 --- a/libtransport/includes/hicn/transport/utils/CMakeLists.txt +++ b/libtransport/includes/hicn/transport/utils/CMakeLists.txt @@ -31,6 +31,11 @@ list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/fixed_block_allocator.h ${CMAKE_CURRENT_SOURCE_DIR}/event_thread.h ${CMAKE_CURRENT_SOURCE_DIR}/string_utils.h + ${CMAKE_CURRENT_SOURCE_DIR}/file.h + ${CMAKE_CURRENT_SOURCE_DIR}/shared_ptr_utils.h + ${CMAKE_CURRENT_SOURCE_DIR}/move_wrapper.h + ${CMAKE_CURRENT_SOURCE_DIR}/noncopyable.h + ${CMAKE_CURRENT_SOURCE_DIR}/singleton.h ) if(NOT WIN32) diff --git a/libtransport/includes/hicn/transport/utils/conversions.h b/libtransport/includes/hicn/transport/utils/conversions.h index 24b529206..52d3e3168 100644 --- a/libtransport/includes/hicn/transport/utils/conversions.h +++ b/libtransport/includes/hicn/transport/utils/conversions.h @@ -16,8 +16,8 @@ #pragma once #include - #include + #include #include diff --git a/libtransport/includes/hicn/transport/utils/enum_iterator.h b/libtransport/includes/hicn/transport/utils/enum_iterator.h new file mode 100644 index 000000000..5e108b088 --- /dev/null +++ b/libtransport/includes/hicn/transport/utils/enum_iterator.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +template +class Iterator { + typedef typename std::underlying_type::type val_t; + int val; + + public: + Iterator(const C& f) : val(static_cast(f)) {} + + Iterator() : val(static_cast(begin_val)) {} + + Iterator operator++() { + ++val; + return *this; + } + + C operator*() { return static_cast(val); } + + Iterator begin() { return *this; } // default ctor is good + + Iterator end() { + static const Iterator endIter = ++Iterator(end_val); // cache it + return endIter; + } + + bool operator!=(const Iterator& i) { return val != i.val; } +}; \ No newline at end of file diff --git a/libtransport/includes/hicn/transport/utils/event_thread.h b/libtransport/includes/hicn/transport/utils/event_thread.h index 702c98f8d..bb6ab90ef 100644 --- a/libtransport/includes/hicn/transport/utils/event_thread.h +++ b/libtransport/includes/hicn/transport/utils/event_thread.h @@ -17,7 +17,11 @@ #include #include +#include +#ifndef ASIO_STANDALONE +#define ASIO_STANDALONE +#endif #include #include #include @@ -25,36 +29,58 @@ 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) + EventThread(asio::io_service& io_service, bool detached = false) : internal_io_service_(nullptr), - io_service_(io_service), + io_service_(std::ref(io_service)), work_(std::make_unique(io_service_)), - thread_(nullptr) { + thread_(nullptr), + detached_(detached) { run(); } - explicit EventThread() + EventThread(bool detached = false) : internal_io_service_(std::make_unique()), - io_service_(*internal_io_service_), + io_service_(std::ref(*internal_io_service_)), work_(std::make_unique(io_service_)), - thread_(nullptr) { + thread_(nullptr), + detached_(detached) { run(); } + EventThread(const EventThread&) = delete; + EventThread& operator=(const EventThread&) = delete; + + EventThread(EventThread&& other) + : internal_io_service_(std::move(other.internal_io_service_)), + io_service_(std::move(other.io_service_)), + work_(std::move(other.work_)), + thread_(std::move(other.thread_)), + detached_(std::move(other.detached_)) {} + + EventThread& operator=(EventThread&& other) { + internal_io_service_ = std::move(other.internal_io_service_); + io_service_ = std::move(other.io_service_); + work_ = std::move(other.work_); + thread_ = std::move(other.thread_); + detached_ = other.detached_; + + return *this; + } + ~EventThread() { stop(); } void run() { if (stopped()) { - io_service_.reset(); + io_service_.get().stopped(); } - thread_ = std::make_unique([this]() { io_service_.run(); }); + thread_ = + std::make_unique([this]() { io_service_.get().run(); }); + + if (detached_) { + thread_->detach(); + } } std::thread::id getThreadId() const { @@ -67,14 +93,12 @@ class EventThread { template void add(Func&& f) { - // If the function f - // TODO USe post in mac os, asio->post in xenial - io_service_.post(std::forward(f)); + io_service_.get().post(std::forward(f)); } template void tryRunHandlerNow(Func&& f) { - io_service_.dispatch(std::forward(f)); + io_service_.get().dispatch(std::forward(f)); } void stop() { @@ -87,15 +111,16 @@ class EventThread { thread_.reset(); } - bool stopped() { return io_service_.stopped(); } + bool stopped() { return io_service_.get().stopped(); } asio::io_service& getIoService() { return io_service_; } private: std::unique_ptr internal_io_service_; - asio::io_service& io_service_; + std::reference_wrapper io_service_; std::unique_ptr work_; std::unique_ptr thread_; + bool detached_; }; } // namespace utils \ No newline at end of file diff --git a/libtransport/includes/hicn/transport/utils/file.h b/libtransport/includes/hicn/transport/utils/file.h new file mode 100644 index 000000000..4c73f33e8 --- /dev/null +++ b/libtransport/includes/hicn/transport/utils/file.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +namespace utils { + +class File { + public: + static inline bool exists(const std::string& name) { + struct stat buffer; + return (stat(name.c_str(), &buffer) == 0); + } +}; + +} // namespace utils diff --git a/libtransport/includes/hicn/transport/utils/fixed_block_allocator.h b/libtransport/includes/hicn/transport/utils/fixed_block_allocator.h index 1ade1516e..b1df36493 100644 --- a/libtransport/includes/hicn/transport/utils/fixed_block_allocator.h +++ b/libtransport/includes/hicn/transport/utils/fixed_block_allocator.h @@ -5,104 +5,99 @@ #pragma once #include +#include #include - #include + +#include #include #include -#include namespace utils { -template -class FixedBlockAllocator { - FixedBlockAllocator(std::size_t size = DEFAULT_SIZE, - std::size_t objects = OBJECTS) - : block_size_(size < sizeof(void*) ? sizeof(long*) : size), - object_size_(size), - max_objects_(objects), - p_head_(NULL), - pool_index_(0), - block_count_(0), - blocks_in_use_(0), - allocations_(0), - deallocations_(0) { - p_pool_ = (uint8_t*)new uint8_t[block_size_ * max_objects_]; - } +template +class FixedBlockAllocator + : public utils::Singleton> { + friend class utils::Singleton>; public: - static FixedBlockAllocator* getInstance() { - if (!instance_) { - instance_ = std::unique_ptr( - new FixedBlockAllocator(DEFAULT_SIZE, OBJECTS)); + ~FixedBlockAllocator() { + for (auto& p : p_pools_) { + delete[] p; } - - return instance_.get(); } - ~FixedBlockAllocator() { delete[] p_pool_; } - - TRANSPORT_ALWAYS_INLINE void* allocateBlock(size_t size = DEFAULT_SIZE) { - assert(size <= DEFAULT_SIZE); + void* allocateBlock(size_t size = SIZE) { + assert(size <= SIZE); uint32_t index; + SpinLock::Acquire locked(lock_); void* p_block = pop(); if (!p_block) { - if (pool_index_ < max_objects_) { - { - SpinLock::Acquire locked(lock_); - index = pool_index_++; - } - p_block = (void*)(p_pool_ + (index * block_size_)); - } else { - // TODO Consider increasing pool here instead of throwing an exception - throw std::runtime_error("No more memory available from packet pool!"); + if (TRANSPORT_EXPECT_FALSE(current_pool_index_ >= max_objects_)) { + // Allocate new memory block + TRANSPORT_LOGV("Allocating new block of %zu size", SIZE * OBJECTS); + p_pools_.emplace_front( + new typename std::aligned_storage::type[max_objects_]); + // reset current_pool_index_ + current_pool_index_ = 0; } - } - blocks_in_use_++; - allocations_++; + auto& latest = p_pools_.front(); + index = current_pool_index_++; + blocks_in_use_++; + allocations_++; + p_block = (void*)&latest[index]; + } return p_block; } - TRANSPORT_ALWAYS_INLINE void deallocateBlock(void* pBlock) { + void deallocateBlock(void* pBlock) { + SpinLock::Acquire locked(lock_); push(pBlock); - { - SpinLock::Acquire locked(lock_); - blocks_in_use_--; - deallocations_++; - } + blocks_in_use_--; + deallocations_++; } - TRANSPORT_ALWAYS_INLINE std::size_t blockSize() { return block_size_; } + public: + std::size_t blockSize() { return block_size_; } - TRANSPORT_ALWAYS_INLINE uint32_t blockCount() { return block_count_; } + uint32_t blockCount() { return block_count_; } - TRANSPORT_ALWAYS_INLINE uint32_t blocksInUse() { return blocks_in_use_; } + uint32_t blocksInUse() { return blocks_in_use_; } - TRANSPORT_ALWAYS_INLINE uint32_t allocations() { return allocations_; } + uint32_t allocations() { return allocations_; } - TRANSPORT_ALWAYS_INLINE uint32_t deallocations() { return deallocations_; } + uint32_t deallocations() { return deallocations_; } private: - TRANSPORT_ALWAYS_INLINE void push(void* p_memory) { + FixedBlockAllocator() + : block_size_(SIZE), + object_size_(SIZE), + max_objects_(OBJECTS), + p_head_(NULL), + current_pool_index_(0), + block_count_(0), + blocks_in_use_(0), + allocations_(0), + deallocations_(0) { + static_assert(SIZE >= sizeof(long*), "SIZE must be at least 8 bytes"); + p_pools_.emplace_front( + new typename std::aligned_storage::type[max_objects_]); + } + + void push(void* p_memory) { Block* p_block = (Block*)p_memory; - { - SpinLock::Acquire locked(lock_); - p_block->p_next = p_head_; - p_head_ = p_block; - } + p_block->p_next = p_head_; + p_head_ = p_block; } - TRANSPORT_ALWAYS_INLINE void* pop() { + void* pop() { Block* p_block = nullptr; - { - SpinLock::Acquire locked(lock_); - if (p_head_) { - p_block = p_head_; - p_head_ = p_head_->p_next; - } + if (p_head_) { + p_block = p_head_; + p_head_ = p_head_->p_next; } return (void*)p_block; @@ -119,8 +114,8 @@ class FixedBlockAllocator { const std::size_t max_objects_; Block* p_head_; - uint8_t* p_pool_; - uint32_t pool_index_; + uint32_t current_pool_index_; + std::list::type*> p_pools_; uint32_t block_count_; uint32_t blocks_in_use_; uint32_t allocations_; @@ -133,4 +128,88 @@ template std::unique_ptr> FixedBlockAllocator::instance_ = nullptr; +/** + * STL Allocator trait to be used with allocate_shared. + */ +template +class STLAllocator { + /** + * If STLAllocator is rebound to another type (!= T) using copy constructor, + * we may need to access private members of the source allocator to copy + * memory and pool. + */ + template + friend class STLAllocator; + + public: + using size_type = std::size_t; + using difference_type = ptrdiff_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using value_type = T; + + STLAllocator(pointer memory, Pool* memory_pool) + : memory_(memory), pool_(memory_pool) { + TRANSPORT_LOGV("Creating allocator. This: %p, memory: %p, memory_pool: %p", + this, memory, memory_pool); + } + + ~STLAllocator() {} + + template + STLAllocator(const STLAllocator& other) { + memory_ = other.memory_; + pool_ = other.pool_; + } + + template + struct rebind { + typedef STLAllocator other; + }; + + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const { return &x; } + + pointer allocate(size_type n, pointer hint = 0) { + TRANSPORT_LOGV( + "Allocating memory (%zu). This: %p, memory: %p, memory_pool: %p", n, + this, memory_, pool_); + return static_cast(memory_); + } + + void deallocate(pointer p, size_type n) { + TRANSPORT_LOGV("Deallocating memory. This: %p, memory: %p, memory_pool: %p", + this, memory_, pool_); + pool_->deallocateBlock(memory_); + } + + template + void construct(pointer p, Args&&... args) { + new (static_cast(p)) T(std::forward(args)...); + } + + void destroy(pointer p) { + TRANSPORT_LOGV("Destroying object. This: %p, memory: %p, memory_pool: %p", + this, memory_, pool_); + p->~T(); + } + + private: + void* memory_; + Pool* pool_; +}; + +template +inline bool operator==(const STLAllocator&, const STLAllocator&) { + return true; +} + +template +inline bool operator!=(const STLAllocator& a, + const STLAllocator& b) { + return !(a == b); +} + } // namespace utils \ No newline at end of file diff --git a/libtransport/includes/hicn/transport/utils/linux.h b/libtransport/includes/hicn/transport/utils/linux.h index 5820528e1..105196fd2 100644 --- a/libtransport/includes/hicn/transport/utils/linux.h +++ b/libtransport/includes/hicn/transport/utils/linux.h @@ -17,14 +17,14 @@ #ifdef __linux__ +#include #include #include - -#include #include #include #include #include + #include #define LINK_LOCAL_PREFIX 0xfe80 diff --git a/libtransport/includes/hicn/transport/utils/membuf.h b/libtransport/includes/hicn/transport/utils/membuf.h index 9fc37dd25..0db87e9dd 100644 --- a/libtransport/includes/hicn/transport/utils/membuf.h +++ b/libtransport/includes/hicn/transport/utils/membuf.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -35,8 +36,6 @@ #include #include -#include - #ifndef _WIN32 TRANSPORT_GNU_DISABLE_WARNING("-Wshadow") #endif @@ -50,6 +49,8 @@ class MemBuf { enum TakeOwnershipOp { TAKE_OWNERSHIP }; enum CopyBufferOp { COPY_BUFFER }; + using Ptr = std::shared_ptr; + typedef void (*FreeFunction)(void* buf, void* userData); static std::unique_ptr create(std::size_t capacity); @@ -106,13 +107,14 @@ class MemBuf { FreeFunction freeFn = nullptr, void* userData = nullptr, bool freeOnError = true); - static std::unique_ptr wrapBuffer(const void* buf, + static std::unique_ptr wrapBuffer(const void* buf, std::size_t length, std::size_t capacity); - static MemBuf wrapBufferAsValue(const void* buf, + static MemBuf wrapBufferAsValue(const void* buf, std::size_t length, std::size_t capacity) noexcept; - MemBuf(WrapBufferOp op, const void* buf, std::size_t capacity) noexcept; + MemBuf(WrapBufferOp op, const void* buf, std::size_t length, + std::size_t capacity) noexcept; /** * Convenience function to create a new MemBuf object that copies data from a @@ -147,6 +149,8 @@ class MemBuf { std::size_t length() const { return length_; } + void setLength(std::size_t length) { length_ = length; } + std::size_t headroom() const { return std::size_t(data_ - buffer()); } std::size_t tailroom() const { return std::size_t(bufferEnd() - tail()); } @@ -689,6 +693,18 @@ class MemBuf { void* userData = nullptr, bool freeOnError = true); + /** + * Ensure the current MemBuf can hold at least capacity bytes and its + * memory is contiguous + */ + bool ensureCapacity(std::size_t capacity); + + /** + * Ensure packet buffer can hold at least 1500 bytes in contiguous memory and + * fill unused memory with placeholder + */ + bool ensureCapacityAndFillUnused(std::size_t capacity, uint8_t placeholder); + /* * Overridden operator new and delete. * These perform specialized memory management to help support @@ -700,6 +716,12 @@ class MemBuf { void operator delete(void* ptr); void operator delete(void* ptr, void* placement); + /** + * Override operator == and != + */ + bool operator ==(const MemBuf &other); + bool operator !=(const MemBuf &other); + // /** // * Iteration support: a chain of MemBufs may be iterated through using // * STL-style iterators over const ByteRanges. Iterators are only diff --git a/libtransport/includes/hicn/transport/utils/move_wrapper.h b/libtransport/includes/hicn/transport/utils/move_wrapper.h new file mode 100644 index 000000000..3aba345d6 --- /dev/null +++ b/libtransport/includes/hicn/transport/utils/move_wrapper.h @@ -0,0 +1,39 @@ +/* + * 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 + +namespace utils { + +template +struct MoveWrapper : F { + MoveWrapper(F&& f) : F(std::move(f)) {} + + MoveWrapper(MoveWrapper&&) = default; + MoveWrapper& operator=(MoveWrapper&&) = default; + + MoveWrapper(const MoveWrapper&); + MoveWrapper& operator=(const MoveWrapper&); +}; + +template +auto moveHandler(T&& t) -> MoveWrapper::type> { + return std::move(t); +} +} // namespace utils \ No newline at end of file diff --git a/libtransport/includes/hicn/transport/utils/noncopyable.h b/libtransport/includes/hicn/transport/utils/noncopyable.h new file mode 100644 index 000000000..83923e647 --- /dev/null +++ b/libtransport/includes/hicn/transport/utils/noncopyable.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 utils { + +class NonCopyable { + protected: + NonCopyable() = default; + ~NonCopyable() = default; + + NonCopyable(const NonCopyable&) = delete; + NonCopyable& operator=(const NonCopyable&) = delete; +}; + +} // namespace utils diff --git a/libtransport/includes/hicn/transport/utils/object_pool.h b/libtransport/includes/hicn/transport/utils/object_pool.h index f78bd2aa2..a5e8b2eef 100644 --- a/libtransport/includes/hicn/transport/utils/object_pool.h +++ b/libtransport/includes/hicn/transport/utils/object_pool.h @@ -17,6 +17,7 @@ // TODO #include +#include #include #include @@ -33,6 +34,7 @@ class ObjectPool { void operator()(T *t) { if (pool_) { + TRANSPORT_LOGV("Back in pool"); pool_->add(t); } else { delete t; @@ -44,10 +46,13 @@ class ObjectPool { }; public: - using Ptr = std::unique_ptr; + using Ptr = std::shared_ptr; ObjectPool() : destructor_(false) {} + // No copies + ObjectPool(const ObjectPool &other) = delete; + ~ObjectPool() { destructor_ = true; for (auto &ptr : object_pool_) { @@ -55,12 +60,17 @@ class ObjectPool { } } + bool empty() { + utils::SpinLock::Acquire locked(object_pool_lock_); + return object_pool_.empty(); + } + std::pair get() { + utils::SpinLock::Acquire locked(object_pool_lock_); if (object_pool_.empty()) { - return std::make_pair(false, makePtr(nullptr)); + return std::make_pair(false, nullptr); } - utils::SpinLock::Acquire locked(object_pool_lock_); auto ret = std::move(object_pool_.front()); object_pool_.pop_front(); return std::make_pair(true, std::move(ret)); @@ -70,7 +80,7 @@ class ObjectPool { utils::SpinLock::Acquire locked(object_pool_lock_); if (TRANSPORT_EXPECT_TRUE(!destructor_)) { - object_pool_.emplace_back(makePtr(object)); + object_pool_.emplace_front(makePtr(object)); } else { delete object; } @@ -79,12 +89,9 @@ class ObjectPool { Ptr makePtr(T *object) { return Ptr(object, ObjectDeleter(this)); } private: - // No copies - ObjectPool(const ObjectPool &other) = delete; - utils::SpinLock object_pool_lock_; std::deque object_pool_; - bool destructor_; + std::atomic destructor_; }; -} // namespace utils \ No newline at end of file +} // namespace utils diff --git a/libtransport/includes/hicn/transport/utils/shared_ptr_utils.h b/libtransport/includes/hicn/transport/utils/shared_ptr_utils.h new file mode 100644 index 000000000..3387997b5 --- /dev/null +++ b/libtransport/includes/hicn/transport/utils/shared_ptr_utils.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + */ + +#pragma once + +#include + +namespace utils { + +template +inline std::shared_ptr shared_from_base( + std::enable_shared_from_this* base) { + return base->shared_from_this(); +} + +template +inline std::shared_ptr shared_from_base( + std::enable_shared_from_this const* base) { + return base->shared_from_this(); +} + +template +inline std::shared_ptr shared_from(That* that) { + return std::static_pointer_cast(shared_from_base(that)); +} + +} // namespace utils \ No newline at end of file diff --git a/libtransport/includes/hicn/transport/utils/singleton.h b/libtransport/includes/hicn/transport/utils/singleton.h new file mode 100644 index 000000000..7fd8b912f --- /dev/null +++ b/libtransport/includes/hicn/transport/utils/singleton.h @@ -0,0 +1,39 @@ +/* + * 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 + +namespace utils { + +template +class Singleton { + public: + static T& getInstance() { + static T instance; + return instance; + } + + protected: + Singleton() {} + ~Singleton() {} + + public: + Singleton(Singleton const&) = delete; + Singleton& operator=(Singleton const&) = delete; +}; + +} // namespace utils \ No newline at end of file diff --git a/libtransport/includes/hicn/transport/utils/string_utils.h b/libtransport/includes/hicn/transport/utils/string_utils.h index bfda816f1..313c28cc6 100644 --- a/libtransport/includes/hicn/transport/utils/string_utils.h +++ b/libtransport/includes/hicn/transport/utils/string_utils.h @@ -72,4 +72,4 @@ static inline std::string trim_copy(std::string s) { return s; } -} \ No newline at end of file +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/CMakeLists.txt b/libtransport/src/CMakeLists.txt index 33497e0f4..0fa9bbe3c 100644 --- a/libtransport/src/CMakeLists.txt +++ b/libtransport/src/CMakeLists.txt @@ -20,7 +20,7 @@ set(ASIO_STANDALONE 1) add_subdirectory(core) add_subdirectory(interfaces) add_subdirectory(protocols) -add_subdirectory(security) +add_subdirectory(auth) add_subdirectory(implementation) add_subdirectory(utils) add_subdirectory(http) @@ -34,7 +34,16 @@ install( COMPONENT lib${LIBTRANSPORT}-dev ) -set (COMPILER_DEFINITIONS "-DTRANSPORT_LOG_DEF_LEVEL=TRANSPORT_LOG_${TRANSPORT_LOG_LEVEL}") +install( + FILES "transport.config" + DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn + COMPONENT lib${LIBTRANSPORT} +) + +list(APPEND COMPILER_DEFINITIONS + "-DTRANSPORT_LOG_DEF_LEVEL=TRANSPORT_LOG_${TRANSPORT_LOG_LEVEL}" + "-DASIO_STANDALONE" +) list(INSERT LIBTRANSPORT_INTERNAL_INCLUDE_DIRS 0 ${CMAKE_CURRENT_SOURCE_DIR}/ @@ -55,8 +64,10 @@ else () set(CMAKE_SHARED_LINKER_FLAGS "/NODEFAULTLIB:\"MSVCRTD\"" ) endif () endif () -if (${CMAKE_SYSTEM_NAME} STREQUAL "Android") + +if (${CMAKE_SYSTEM_NAME} MATCHES "Android") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -isystem -lm") + add_subdirectory(io_modules) endif() if (DISABLE_SHARED_LIBRARIES) @@ -68,8 +79,9 @@ if (DISABLE_SHARED_LIBRARIES) DEPENDS ${DEPENDENCIES} COMPONENT lib${LIBTRANSPORT} INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} - INSTALL_ROOT_DIR hicn/transport + HEADER_ROOT_DIR hicn/transport DEFINITIONS ${COMPILER_DEFINITIONS} + VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION} ) else () build_library(${LIBTRANSPORT} @@ -80,11 +92,17 @@ else () DEPENDS ${DEPENDENCIES} COMPONENT lib${LIBTRANSPORT} INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} - INSTALL_ROOT_DIR hicn/transport + HEADER_ROOT_DIR hicn/transport DEFINITIONS ${COMPILER_DEFINITIONS} + VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION} ) endif () +# io modules +if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Android") + add_subdirectory(io_modules) +endif() + if (${BUILD_TESTS}) add_subdirectory(test) endif() diff --git a/libtransport/src/auth/CMakeLists.txt b/libtransport/src/auth/CMakeLists.txt new file mode 100644 index 000000000..0e7b5832b --- /dev/null +++ b/libtransport/src/auth/CMakeLists.txt @@ -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. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/signer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/verifier.cc + ${CMAKE_CURRENT_SOURCE_DIR}/identity.cc +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) diff --git a/libtransport/src/auth/identity.cc b/libtransport/src/auth/identity.cc new file mode 100644 index 000000000..bd787b9b6 --- /dev/null +++ b/libtransport/src/auth/identity.cc @@ -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. + */ + +#include + +using namespace std; + +namespace transport { +namespace auth { + +Identity::Identity(const string &keystore_path, const string &keystore_pwd, + CryptoSuite suite, unsigned int signature_len, + unsigned int validity_days, const string &subject_name) + : identity_(nullptr), signer_(nullptr) { + parcSecurity_Init(); + + bool success = parcPkcs12KeyStore_CreateFile( + keystore_path.c_str(), keystore_pwd.c_str(), subject_name.c_str(), + parcCryptoSuite_GetSigningAlgorithm(static_cast(suite)), + signature_len, validity_days); + + parcAssertTrue( + success, + "parcPkcs12KeyStore_CreateFile('%s', '%s', '%s', %d, %d, %d) failed.", + keystore_path.c_str(), keystore_pwd.c_str(), subject_name.c_str(), + static_cast(suite), static_cast(signature_len), validity_days); + + PARCIdentityFile *identity_file = + parcIdentityFile_Create(keystore_path.c_str(), keystore_pwd.c_str()); + + identity_ = + parcIdentity_Create(identity_file, PARCIdentityFileAsPARCIdentity); + + PARCSigner *signer = parcIdentity_CreateSigner( + identity_, + parcCryptoSuite_GetCryptoHash(static_cast(suite))); + + signer_ = make_shared(signer); + + parcSigner_Release(&signer); + parcIdentityFile_Release(&identity_file); +} + +Identity::Identity(string &keystore_path, string &keystore_pwd, + CryptoHashType hash_type) + : identity_(nullptr), signer_(nullptr) { + parcSecurity_Init(); + + PARCIdentityFile *identity_file = + parcIdentityFile_Create(keystore_path.c_str(), keystore_pwd.c_str()); + + identity_ = + parcIdentity_Create(identity_file, PARCIdentityFileAsPARCIdentity); + + PARCSigner *signer = parcIdentity_CreateSigner( + identity_, static_cast(hash_type)); + + signer_ = make_shared(signer); + + parcSigner_Release(&signer); + parcIdentityFile_Release(&identity_file); +} + +Identity::Identity(const Identity &other) + : identity_(nullptr), signer_(other.signer_) { + parcSecurity_Init(); + identity_ = parcIdentity_Acquire(other.identity_); +} + +Identity::Identity(Identity &&other) + : identity_(nullptr), signer_(move(other.signer_)) { + parcSecurity_Init(); + identity_ = parcIdentity_Acquire(other.identity_); + parcIdentity_Release(&other.identity_); +} + +Identity::~Identity() { + if (identity_) parcIdentity_Release(&identity_); + parcSecurity_Fini(); +} + +shared_ptr Identity::getSigner() const { return signer_; } + +string Identity::getFilename() const { + return string(parcIdentity_GetFileName(identity_)); +} + +string Identity::getPassword() const { + return string(parcIdentity_GetPassWord(identity_)); +} + +Identity Identity::generateIdentity(const string &subject_name) { + string keystore_name = "keystore"; + string keystore_password = "password"; + size_t key_length = 1024; + unsigned int validity_days = 30; + CryptoSuite suite = CryptoSuite::RSA_SHA256; + + return Identity(keystore_name, keystore_password, suite, + (unsigned int)key_length, validity_days, subject_name); +} + +} // namespace auth +} // namespace transport diff --git a/libtransport/src/auth/signer.cc b/libtransport/src/auth/signer.cc new file mode 100644 index 000000000..281b9c59a --- /dev/null +++ b/libtransport/src/auth/signer.cc @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +extern "C" { +#ifndef _WIN32 +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#endif +#include +} + +#include + +#define ALLOW_UNALIGNED_READS 1 + +using namespace std; + +namespace transport { +namespace auth { + +Signer::Signer() : signer_(nullptr), key_id_(nullptr) { parcSecurity_Init(); } + +Signer::Signer(PARCSigner *signer) : Signer() { setSigner(signer); } + +Signer::~Signer() { + if (signer_) parcSigner_Release(&signer_); + if (key_id_) parcKeyId_Release(&key_id_); + parcSecurity_Fini(); +} + +void Signer::signPacket(PacketPtr packet) { + parcAssertNotNull(signer_, "Expected non-null signer"); + + const utils::MemBuf &header_chain = *packet; + core::Packet::Format format = packet->getFormat(); + auto suite = getCryptoSuite(); + size_t signature_len = getSignatureSize(); + + if (!packet->authenticationHeader()) { + throw errors::MalformedAHPacketException(); + } + + packet->setSignatureSize(signature_len); + + // Copy IP+TCP / ICMP header before zeroing them + hicn_header_t header_copy; + hicn_packet_copy_header(format, packet->packet_start_, &header_copy, false); + packet->resetForHash(); + + // Fill in the HICN_AH header + auto now = chrono::duration_cast( + chrono::system_clock::now().time_since_epoch()) + .count(); + packet->setSignatureTimestamp(now); + packet->setValidationAlgorithm(suite); + + // Set the key ID + KeyId key_id; + key_id.first = static_cast( + parcBuffer_Overlay((PARCBuffer *)parcKeyId_GetKeyId(key_id_), 0)); + packet->setKeyId(key_id); + + // Calculate hash + CryptoHasher hasher(parcSigner_GetCryptoHasher(signer_)); + const utils::MemBuf *current = &header_chain; + + hasher.init(); + + do { + hasher.updateBytes(current->data(), current->length()); + current = current->next(); + } while (current != &header_chain); + + CryptoHash hash = hasher.finalize(); + + // Compute signature + PARCSignature *signature = parcSigner_SignDigestNoAlloc( + signer_, hash.hash_, packet->getSignature(), signature_len); + PARCBuffer *buffer = parcSignature_GetSignature(signature); + size_t bytes_len = parcBuffer_Remaining(buffer); + + if (bytes_len > signature_len) { + throw errors::MalformedAHPacketException(); + } + + // Put signature in AH header + hicn_packet_copy_header(format, &header_copy, packet->packet_start_, false); + + // Release allocated objects + parcSignature_Release(&signature); +} + +void Signer::setSigner(PARCSigner *signer) { + parcAssertNotNull(signer, "Expected non-null signer"); + + if (signer_) parcSigner_Release(&signer_); + if (key_id_) parcKeyId_Release(&key_id_); + + signer_ = parcSigner_Acquire(signer); + key_id_ = parcSigner_CreateKeyId(signer_); +} + +size_t Signer::getSignatureSize() const { + parcAssertNotNull(signer_, "Expected non-null signer"); + return parcSigner_GetSignatureSize(signer_); +} + +CryptoSuite Signer::getCryptoSuite() const { + parcAssertNotNull(signer_, "Expected non-null signer"); + return static_cast(parcSigner_GetCryptoSuite(signer_)); +} + +CryptoHashType Signer::getCryptoHashType() const { + parcAssertNotNull(signer_, "Expected non-null signer"); + return static_cast(parcSigner_GetCryptoHashType(signer_)); +} + +PARCSigner *Signer::getParcSigner() const { return signer_; } + +PARCKeyStore *Signer::getParcKeyStore() const { + parcAssertNotNull(signer_, "Expected non-null signer"); + return parcSigner_GetKeyStore(signer_); +} + +AsymmetricSigner::AsymmetricSigner(CryptoSuite suite, PARCKeyStore *key_store) { + parcAssertNotNull(key_store, "Expected non-null key_store"); + + auto crypto_suite = static_cast(suite); + + switch (suite) { + case CryptoSuite::DSA_SHA256: + case CryptoSuite::RSA_SHA256: + case CryptoSuite::RSA_SHA512: + case CryptoSuite::ECDSA_256K1: + break; + default: + throw errors::RuntimeException( + "Invalid crypto suite for asymmetric signer"); + } + + setSigner( + parcSigner_Create(parcPublicKeySigner_Create(key_store, crypto_suite), + PARCPublicKeySignerAsSigner)); +} + +SymmetricSigner::SymmetricSigner(CryptoSuite suite, PARCKeyStore *key_store) { + parcAssertNotNull(key_store, "Expected non-null key_store"); + + auto crypto_suite = static_cast(suite); + + switch (suite) { + case CryptoSuite::HMAC_SHA256: + case CryptoSuite::HMAC_SHA512: + break; + default: + throw errors::RuntimeException( + "Invalid crypto suite for symmetric signer"); + } + + setSigner(parcSigner_Create(parcSymmetricKeySigner_Create( + (PARCSymmetricKeyStore *)key_store, + parcCryptoSuite_GetCryptoHash(crypto_suite)), + PARCSymmetricKeySignerAsSigner)); +} + +SymmetricSigner::SymmetricSigner(CryptoSuite suite, const string &passphrase) { + auto crypto_suite = static_cast(suite); + + switch (suite) { + case CryptoSuite::HMAC_SHA256: + case CryptoSuite::HMAC_SHA512: + break; + default: + throw errors::RuntimeException( + "Invalid crypto suite for symmetric signer"); + } + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_PutString(composer, passphrase.c_str()); + PARCBuffer *key_buf = parcBufferComposer_ProduceBuffer(composer); + parcBufferComposer_Release(&composer); + + PARCSymmetricKeyStore *key_store = parcSymmetricKeyStore_Create(key_buf); + PARCSymmetricKeySigner *key_signer = parcSymmetricKeySigner_Create( + key_store, parcCryptoSuite_GetCryptoHash(crypto_suite)); + + setSigner(parcSigner_Create(key_signer, PARCSymmetricKeySignerAsSigner)); + + parcSymmetricKeySigner_Release(&key_signer); + parcSymmetricKeyStore_Release(&key_store); + parcBuffer_Release(&key_buf); +} + +} // namespace auth +} // namespace transport diff --git a/libtransport/src/auth/verifier.cc b/libtransport/src/auth/verifier.cc new file mode 100644 index 000000000..c6648a763 --- /dev/null +++ b/libtransport/src/auth/verifier.cc @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +extern "C" { +#ifndef _WIN32 +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#endif +#include +} + +#include + +using namespace std; + +namespace transport { +namespace auth { + +const std::vector Verifier::DEFAULT_FAILED_POLICIES = { + VerificationPolicy::DROP, + VerificationPolicy::ABORT, +}; + +Verifier::Verifier() + : hasher_(nullptr), + verifier_(nullptr), + verification_failed_cb_(interface::VOID_HANDLER), + failed_policies_(DEFAULT_FAILED_POLICIES) { + parcSecurity_Init(); + PARCInMemoryVerifier *in_memory_verifier = parcInMemoryVerifier_Create(); + verifier_ = + parcVerifier_Create(in_memory_verifier, PARCInMemoryVerifierAsVerifier); + parcInMemoryVerifier_Release(&in_memory_verifier); +} + +Verifier::~Verifier() { + if (hasher_) parcCryptoHasher_Release(&hasher_); + if (verifier_) parcVerifier_Release(&verifier_); + parcSecurity_Fini(); +} + +bool Verifier::verifyPacket(PacketPtr packet) { + bool valid_packet = false; + core::Packet::Format format = packet->getFormat(); + + if (!packet->authenticationHeader()) { + throw errors::MalformedAHPacketException(); + } + + // Get crypto suite and hash type + auto suite = static_cast(packet->getValidationAlgorithm()); + PARCCryptoHashType hash_type = parcCryptoSuite_GetCryptoHash(suite); + + // Copy IP+TCP / ICMP header before zeroing them + hicn_header_t header_copy; + hicn_packet_copy_header(format, packet->packet_start_, &header_copy, false); + + // Fetch packet signature + uint8_t *packet_signature = packet->getSignature(); + size_t signature_len = Verifier::getSignatureSize(packet); + vector signature_raw(packet_signature, + packet_signature + signature_len); + + // Create a signature buffer from the raw packet signature + PARCBuffer *bits = + parcBuffer_Wrap(signature_raw.data(), signature_len, 0, signature_len); + parcBuffer_Rewind(bits); + + // If the signature algo is ECDSA, the signature might be shorter than the + // signature field + PARCSigningAlgorithm algo = parcCryptoSuite_GetSigningAlgorithm(suite); + if (algo == PARCSigningAlgorithm_ECDSA) { + while (parcBuffer_HasRemaining(bits) && parcBuffer_GetUint8(bits) == 0) + ; + parcBuffer_SetPosition(bits, parcBuffer_Position(bits) - 1); + } + + if (!parcBuffer_HasRemaining(bits)) { + parcBuffer_Release(&bits); + return false; + } + + // Create a signature object from the signature buffer + PARCSignature *signature = parcSignature_Create( + parcCryptoSuite_GetSigningAlgorithm(suite), hash_type, bits); + + // Fetch the key to verify the signature + KeyId key_buffer = packet->getKeyId(); + PARCBuffer *buffer = parcBuffer_Wrap(key_buffer.first, key_buffer.second, 0, + key_buffer.second); + PARCKeyId *key_id = parcKeyId_Create(buffer); + + // Reset fields that are not used to compute signature + packet->resetForHash(); + + // Compute the packet hash + if (!hasher_) + setHasher(parcVerifier_GetCryptoHasher(verifier_, key_id, hash_type)); + CryptoHash local_hash = computeHash(packet); + + // Compare the packet signature to the locally computed one + valid_packet = parcVerifier_VerifyDigestSignature( + verifier_, key_id, local_hash.hash_, suite, signature); + + // Restore the fields that were reset + hicn_packet_copy_header(format, &header_copy, packet->packet_start_, false); + + // Release allocated objects + parcBuffer_Release(&buffer); + parcKeyId_Release(&key_id); + parcSignature_Release(&signature); + parcBuffer_Release(&bits); + + return valid_packet; +} + +vector Verifier::verifyPackets( + const vector &packets) { + vector policies(packets.size(), VerificationPolicy::DROP); + + for (unsigned int i = 0; i < packets.size(); ++i) { + if (verifyPacket(packets[i])) { + policies[i] = VerificationPolicy::ACCEPT; + } + + callVerificationFailedCallback(packets[i], policies[i]); + } + + return policies; +} + +vector Verifier::verifyPackets( + const vector &packets, + const unordered_map &suffix_map) { + vector policies(packets.size(), + VerificationPolicy::UNKNOWN); + + for (unsigned int i = 0; i < packets.size(); ++i) { + uint32_t suffix = packets[i]->getName().getSuffix(); + auto manifest_hash = suffix_map.find(suffix); + + if (manifest_hash != suffix_map.end()) { + CryptoHashType hash_type = manifest_hash->second.first; + CryptoHash packet_hash = packets[i]->computeDigest(hash_type); + + if (!CryptoHash::compareBinaryDigest( + packet_hash.getDigest().data(), + manifest_hash->second.second.data(), hash_type)) { + policies[i] = VerificationPolicy::ABORT; + } else { + policies[i] = VerificationPolicy::ACCEPT; + } + } + + callVerificationFailedCallback(packets[i], policies[i]); + } + + return policies; +} + +void Verifier::addKey(PARCKey *key) { parcVerifier_AddKey(verifier_, key); } + +void Verifier::setHasher(PARCCryptoHasher *hasher) { + parcAssertNotNull(hasher, "Expected non-null hasher"); + if (hasher_) parcCryptoHasher_Release(&hasher_); + hasher_ = parcCryptoHasher_Acquire(hasher); +} + +void Verifier::setVerificationFailedCallback( + VerificationFailedCallback verfication_failed_cb, + const vector &failed_policies) { + verification_failed_cb_ = verfication_failed_cb; + failed_policies_ = failed_policies; +} + +void Verifier::getVerificationFailedCallback( + VerificationFailedCallback **verfication_failed_cb) { + *verfication_failed_cb = &verification_failed_cb_; +} + +size_t Verifier::getSignatureSize(const PacketPtr packet) { + return packet->getSignatureSize(); +} + +CryptoHash Verifier::computeHash(PacketPtr packet) { + parcAssertNotNull(hasher_, "Expected non-null hasher"); + + CryptoHasher crypto_hasher(hasher_); + const utils::MemBuf &header_chain = *packet; + const utils::MemBuf *current = &header_chain; + + crypto_hasher.init(); + + do { + crypto_hasher.updateBytes(current->data(), current->length()); + current = current->next(); + } while (current != &header_chain); + + return crypto_hasher.finalize(); +} + +void Verifier::callVerificationFailedCallback(PacketPtr packet, + VerificationPolicy &policy) { + if (verification_failed_cb_ == interface::VOID_HANDLER) { + return; + } + + if (find(failed_policies_.begin(), failed_policies_.end(), policy) != + failed_policies_.end()) { + policy = verification_failed_cb_( + static_cast(*packet), + make_error_code( + protocol::protocol_error::signature_verification_failed)); + } +} + +bool VoidVerifier::verifyPacket(PacketPtr packet) { return true; } + +vector VoidVerifier::verifyPackets( + const vector &packets) { + return vector(packets.size(), VerificationPolicy::ACCEPT); +} + +vector VoidVerifier::verifyPackets( + const vector &packets, + const unordered_map &suffix_map) { + return vector(packets.size(), VerificationPolicy::ACCEPT); +} + +AsymmetricVerifier::AsymmetricVerifier(PARCKey *pub_key) { addKey(pub_key); } + +AsymmetricVerifier::AsymmetricVerifier(const string &cert_path) { + setCertificate(cert_path); +} + +void AsymmetricVerifier::setCertificate(const string &cert_path) { + PARCCertificateFactory *factory = parcCertificateFactory_Create( + PARCCertificateType_X509, PARCContainerEncoding_PEM); + + struct stat buffer; + if (stat(cert_path.c_str(), &buffer) != 0) { + throw errors::RuntimeException("Certificate does not exist"); + } + + PARCCertificate *certificate = + parcCertificateFactory_CreateCertificateFromFile(factory, + cert_path.c_str(), NULL); + PARCKey *key = parcCertificate_GetPublicKey(certificate); + + addKey(key); + + parcKey_Release(&key); + parcCertificateFactory_Release(&factory); +} + +SymmetricVerifier::SymmetricVerifier(const string &passphrase) + : passphrase_(nullptr), signer_(nullptr) { + setPassphrase(passphrase); +} + +SymmetricVerifier::~SymmetricVerifier() { + if (passphrase_) parcBuffer_Release(&passphrase_); + if (signer_) parcSigner_Release(&signer_); +} + +void SymmetricVerifier::setPassphrase(const string &passphrase) { + if (passphrase_) parcBuffer_Release(&passphrase_); + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_PutString(composer, passphrase.c_str()); + passphrase_ = parcBufferComposer_ProduceBuffer(composer); + parcBufferComposer_Release(&composer); +} + +void SymmetricVerifier::setSigner(const PARCCryptoSuite &suite) { + parcAssertNotNull(passphrase_, "Expected non-null passphrase"); + + if (signer_) parcSigner_Release(&signer_); + + PARCSymmetricKeyStore *key_store = parcSymmetricKeyStore_Create(passphrase_); + PARCSymmetricKeySigner *key_signer = parcSymmetricKeySigner_Create( + key_store, parcCryptoSuite_GetCryptoHash(suite)); + signer_ = parcSigner_Create(key_signer, PARCSymmetricKeySignerAsSigner); + + PARCKeyId *key_id = parcSigner_CreateKeyId(signer_); + PARCKey *key = parcKey_CreateFromSymmetricKey( + key_id, parcSigner_GetSigningAlgorithm(signer_), passphrase_); + + addKey(key); + setHasher(parcSigner_GetCryptoHasher(signer_)); + + parcSymmetricKeyStore_Release(&key_store); + parcSymmetricKeySigner_Release(&key_signer); + parcKeyId_Release(&key_id); + parcKey_Release(&key); +} + +vector SymmetricVerifier::verifyPackets( + const vector &packets) { + vector policies(packets.size(), VerificationPolicy::DROP); + + for (unsigned int i = 0; i < packets.size(); ++i) { + auto suite = + static_cast(packets[i]->getValidationAlgorithm()); + + if (!signer_ || suite != parcSigner_GetCryptoSuite(signer_)) { + setSigner(suite); + } + + if (verifyPacket(packets[i])) { + policies[i] = VerificationPolicy::ACCEPT; + } + + callVerificationFailedCallback(packets[i], policies[i]); + } + + return policies; +} + +} // namespace auth +} // namespace transport diff --git a/libtransport/src/config.h.in b/libtransport/src/config.h.in index 4e9a0f262..ef47affda 100644 --- a/libtransport/src/config.h.in +++ b/libtransport/src/config.h.in @@ -25,10 +25,6 @@ #cmakedefine ASIO_STANDALONE #endif -#ifndef SECURE_HICNTRANSPORT -#cmakedefine SECURE_HICNTRANSPORT -#endif - #define RAAQM_CONFIG_PATH "@raaqm_config_path@" #cmakedefine __vpp__ diff --git a/libtransport/src/core/CMakeLists.txt b/libtransport/src/core/CMakeLists.txt index 5c8ab9270..4e3ac10ec 100644 --- a/libtransport/src/core/CMakeLists.txt +++ b/libtransport/src/core/CMakeLists.txt @@ -21,55 +21,27 @@ list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/manifest_format.h ${CMAKE_CURRENT_SOURCE_DIR}/pending_interest.h ${CMAKE_CURRENT_SOURCE_DIR}/portal.h - ${CMAKE_CURRENT_SOURCE_DIR}/connector.h - ${CMAKE_CURRENT_SOURCE_DIR}/tcp_socket_connector.h - ${CMAKE_CURRENT_SOURCE_DIR}/udp_socket_connector.h - ${CMAKE_CURRENT_SOURCE_DIR}/forwarder_interface.h - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_interface.h - ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_interface.h - ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/errors.h + ${CMAKE_CURRENT_SOURCE_DIR}/global_configuration.h + ${CMAKE_CURRENT_SOURCE_DIR}/local_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/rs.h ) list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/content_object.cc ${CMAKE_CURRENT_SOURCE_DIR}/interest.cc + ${CMAKE_CURRENT_SOURCE_DIR}/errors.cc ${CMAKE_CURRENT_SOURCE_DIR}/packet.cc ${CMAKE_CURRENT_SOURCE_DIR}/name.cc ${CMAKE_CURRENT_SOURCE_DIR}/prefix.cc - ${CMAKE_CURRENT_SOURCE_DIR}/tcp_socket_connector.cc - ${CMAKE_CURRENT_SOURCE_DIR}/udp_socket_connector.cc - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_interface.cc ${CMAKE_CURRENT_SOURCE_DIR}/manifest_format_fixed.cc - ${CMAKE_CURRENT_SOURCE_DIR}/connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/portal.cc + ${CMAKE_CURRENT_SOURCE_DIR}/global_configuration.cc + ${CMAKE_CURRENT_SOURCE_DIR}/io_module.cc + ${CMAKE_CURRENT_SOURCE_DIR}/local_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/fec.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rs.cc ) -if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") - if (BUILD_WITH_VPP OR BUILD_HICNPLUGIN) - list(APPEND HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_interface.h - ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.h - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_vapi.h - ${CMAKE_CURRENT_SOURCE_DIR}/memif_vapi.h - ) - - list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_interface.cc - ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.cc - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_vapi.c - ${CMAKE_CURRENT_SOURCE_DIR}/memif_vapi.c - ) - endif() - - list(APPEND HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_connector.h - ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_interface.h - ) - - list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_connector.cc - ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_interface.cc - ) -endif() - set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/libtransport/src/core/content_object.cc b/libtransport/src/core/content_object.cc index f5cccf404..0c68ef559 100644 --- a/libtransport/src/core/content_object.cc +++ b/libtransport/src/core/content_object.cc @@ -32,8 +32,9 @@ namespace transport { namespace core { -ContentObject::ContentObject(const Name &name, Packet::Format format) - : Packet(format) { +ContentObject::ContentObject(const Name &name, Packet::Format format, + std::size_t additional_header_size) + : Packet(format, additional_header_size) { if (TRANSPORT_EXPECT_FALSE( hicn_data_set_name(format, packet_start_, &name.name_) < 0)) { throw errors::RuntimeException("Error filling the packet name."); @@ -47,41 +48,32 @@ ContentObject::ContentObject(const Name &name, Packet::Format format) } #ifdef __ANDROID__ -ContentObject::ContentObject(hicn_format_t format) - : ContentObject(Name("0::0|0"), format) {} +ContentObject::ContentObject(hicn_format_t format, + std::size_t additional_header_size) + : ContentObject(Name("0::0|0"), format, additional_header_size) {} #else -ContentObject::ContentObject(hicn_format_t format) - : ContentObject(Packet::base_name, format) {} +ContentObject::ContentObject(hicn_format_t format, + std::size_t additional_header_size) + : ContentObject(Packet::base_name, format, additional_header_size) {} #endif ContentObject::ContentObject(const Name &name, hicn_format_t format, + std::size_t additional_header_size, const uint8_t *payload, std::size_t size) - : ContentObject(name, format) { + : ContentObject(name, format, additional_header_size) { appendPayload(payload, size); } -ContentObject::ContentObject(const uint8_t *buffer, std::size_t size) - : Packet(buffer, size) { - if (hicn_data_get_name(format_, packet_start_, name_.getStructReference()) < - 0) { - throw errors::RuntimeException("Error getting name from content object."); - } +ContentObject::ContentObject(ContentObject &&other) : Packet(std::move(other)) { + name_ = std::move(other.name_); } -ContentObject::ContentObject(MemBufPtr &&buffer) : Packet(std::move(buffer)) { - if (hicn_data_get_name(format_, packet_start_, name_.getStructReference()) < - 0) { - throw errors::RuntimeException("Error getting name from content object."); - } +ContentObject::ContentObject(const ContentObject &other) : Packet(other) { + name_ = other.name_; } -ContentObject::ContentObject(ContentObject &&other) : Packet(std::move(other)) { - name_ = std::move(other.name_); - - if (hicn_data_get_name(format_, packet_start_, name_.getStructReference()) < - 0) { - throw errors::MalformedPacketException(); - } +ContentObject &ContentObject::operator=(const ContentObject &other) { + return (ContentObject &)Packet::operator=(other); } ContentObject::~ContentObject() {} @@ -132,10 +124,11 @@ uint32_t ContentObject::getPathLabel() const { "Error retrieving the path label from content object"); } - return path_label; + return ntohl(path_label); } ContentObject &ContentObject::setPathLabel(uint32_t path_label) { + path_label = htonl(path_label); if (hicn_data_set_path_label((hicn_header_t *)packet_start_, path_label) < 0) { throw errors::RuntimeException( diff --git a/libtransport/src/core/errors.cc b/libtransport/src/core/errors.cc new file mode 100644 index 000000000..82647a60b --- /dev/null +++ b/libtransport/src/core/errors.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace transport { +namespace core { + +const std::error_category& core_category() { + static core_category_impl instance; + + return instance; +} + +const char* core_category_impl::name() const throw() { + return "transport::protocol::error"; +} + +std::string core_category_impl::message(int ev) const { + switch (static_cast(ev)) { + case core_error::success: { + return "Success"; + } + case core_error::configuration_parse_failed: { + return "Error parsing configuration."; + } + case core_error::configuration_not_applied: { + return "Configuration was not applied due to wrong parameters."; + } + default: { + return "Unknown core error"; + } + } +} + +} // namespace core +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/core/errors.h b/libtransport/src/core/errors.h new file mode 100644 index 000000000..a46f1dbcd --- /dev/null +++ b/libtransport/src/core/errors.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace transport { +namespace core { + +/** + * @brief Get the default server error category. + * @return The default server error category instance. + * + * @warning The first call to this function is thread-safe only starting with + * C++11. + */ +const std::error_category& core_category(); + +/** + * The list of errors. + */ +enum class core_error { + success = 0, + configuration_parse_failed, + configuration_not_applied +}; + +/** + * @brief Create an error_code instance for the given error. + * @param error The error. + * @return The error_code instance. + */ +inline std::error_code make_error_code(core_error error) { + return std::error_code(static_cast(error), core_category()); +} + +/** + * @brief Create an error_condition instance for the given error. + * @param error The error. + * @return The error_condition instance. + */ +inline std::error_condition make_error_condition(core_error error) { + return std::error_condition(static_cast(error), core_category()); +} + +/** + * @brief A server error category. + */ +class core_category_impl : public std::error_category { + public: + /** + * @brief Get the name of the category. + * @return The name of the category. + */ + virtual const char* name() const throw(); + + /** + * @brief Get the error message for a given error. + * @param ev The error numeric value. + * @return The message associated to the error. + */ + virtual std::string message(int ev) const; +}; +} // namespace core +} // namespace transport + +namespace std { +// namespace system { +template <> +struct is_error_code_enum<::transport::core::core_error> + : public std::true_type {}; +// } // namespace system +} // namespace std \ No newline at end of file diff --git a/libtransport/src/core/facade.h b/libtransport/src/core/facade.h index 04f643f63..199081271 100644 --- a/libtransport/src/core/facade.h +++ b/libtransport/src/core/facade.h @@ -15,36 +15,14 @@ #pragma once -#include -#include #include #include #include -#ifdef __linux__ -#ifndef __ANDROID__ -#include -#ifdef __vpp__ -#include -#endif -#endif -#endif - namespace transport { namespace core { -using HicnForwarderPortal = Portal; - -#ifdef __linux__ -#ifndef __ANDROID__ -using RawSocketPortal = Portal; -#endif -#ifdef __vpp__ -using VPPForwarderPortal = Portal; -#endif -#endif - using ContentObjectManifest = core::ManifestInline; using InterestManifest = core::ManifestInline; diff --git a/libtransport/src/core/fec.cc b/libtransport/src/core/fec.cc new file mode 100644 index 000000000..134198b9e --- /dev/null +++ b/libtransport/src/core/fec.cc @@ -0,0 +1,878 @@ +/* + * fec.c -- forward error correction based on Vandermonde matrices + * 980624 + * (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it) + * + * Portions derived from code by Phil Karn (karn@ka9q.ampr.org), + * Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari + * Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* + * The following parameter defines how many bits are used for + * field elements. The code supports any value from 2 to 16 + * but fastest operation is achieved with 8 bit elements + * This is the only parameter you may want to change. + */ +#ifndef GF_BITS +#define GF_BITS 8 /* code over GF(2**GF_BITS) - change to suit */ +#endif + +#include +#include +#include +#include +#include "fec.h" + +/** + * XXX This disable a warning raising only in some platforms. + * TODO Check if this warning is a mistake or it is a real bug: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83404 + * https://gcc.gnu.org/bugzilla//show_bug.cgi?id=88059 + */ +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wstringop-overflow" +#endif + +/* + * compatibility stuff + */ +#ifdef MSDOS /* but also for others, e.g. sun... */ +#define NEED_BCOPY +#define bcmp(a,b,n) memcmp(a,b,n) +#endif + +#ifdef ANDROID +#define bcmp(a,b,n) memcmp(a,b,n) +#endif + +#ifdef NEED_BCOPY +#define bcopy(s, d, siz) memcpy((d), (s), (siz)) +#define bzero(d, siz) memset((d), '\0', (siz)) +#endif + +/* + * stuff used for testing purposes only + */ + +#ifdef TEST +#define DEB(x) +#define DDB(x) x +#define DEBUG 0 /* minimal debugging */ +#ifdef MSDOS +#include +struct timeval { + unsigned long ticks; +}; +#define gettimeofday(x, dummy) { (x)->ticks = clock() ; } +#define DIFF_T(a,b) (1+ 1000000*(a.ticks - b.ticks) / CLOCKS_PER_SEC ) +typedef unsigned long u_long ; +typedef unsigned short u_short ; +#else /* typically, unix systems */ +#include +#define DIFF_T(a,b) \ + (1+ 1000000*(a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec) ) +#endif + +#define TICK(t) \ + {struct timeval x ; \ + gettimeofday(&x, NULL) ; \ + t = x.tv_usec + 1000000* (x.tv_sec & 0xff ) ; \ + } +#define TOCK(t) \ + { u_long t1 ; TICK(t1) ; \ + if (t1 < t) t = 256000000 + t1 - t ; \ + else t = t1 - t ; \ + if (t == 0) t = 1 ;} + +u_long ticks[10]; /* vars for timekeeping */ +#else +#define DEB(x) +#define DDB(x) +#define TICK(x) +#define TOCK(x) +#endif /* TEST */ + +/* + * You should not need to change anything beyond this point. + * The first part of the file implements linear algebra in GF. + * + * gf is the type used to store an element of the Galois Field. + * Must constain at least GF_BITS bits. + * + * Note: unsigned char will work up to GF(256) but int seems to run + * faster on the Pentium. We use int whenever have to deal with an + * index, since they are generally faster. + */ +#if (GF_BITS < 2 && GF_BITS >16) +#error "GF_BITS must be 2 .. 16" +#endif + + +#define GF_SIZE ((1 << GF_BITS) - 1) /* powers of \alpha */ + +/* + * Primitive polynomials - see Lin & Costello, Appendix A, + * and Lee & Messerschmitt, p. 453. + */ +static const char *allPp[] = { /* GF_BITS polynomial */ + NULL, /* 0 no code */ + NULL, /* 1 no code */ + "111", /* 2 1+x+x^2 */ + "1101", /* 3 1+x+x^3 */ + "11001", /* 4 1+x+x^4 */ + "101001", /* 5 1+x^2+x^5 */ + "1100001", /* 6 1+x+x^6 */ + "10010001", /* 7 1 + x^3 + x^7 */ + "101110001", /* 8 1+x^2+x^3+x^4+x^8 */ + "1000100001", /* 9 1+x^4+x^9 */ + "10010000001", /* 10 1+x^3+x^10 */ + "101000000001", /* 11 1+x^2+x^11 */ + "1100101000001", /* 12 1+x+x^4+x^6+x^12 */ + "11011000000001", /* 13 1+x+x^3+x^4+x^13 */ + "110000100010001", /* 14 1+x+x^6+x^10+x^14 */ + "1100000000000001", /* 15 1+x+x^15 */ + "11010000000010001" /* 16 1+x+x^3+x^12+x^16 */ +}; + + +/* + * To speed up computations, we have tables for logarithm, exponent + * and inverse of a number. If GF_BITS <= 8, we use a table for + * multiplication as well (it takes 64K, no big deal even on a PDA, + * especially because it can be pre-initialized an put into a ROM!), + * otherwhise we use a table of logarithms. + * In any case the macro gf_mul(x,y) takes care of multiplications. + */ + +static gf gf_exp[2*GF_SIZE]; /* index->poly form conversion table */ +static int gf_log[GF_SIZE + 1]; /* Poly->index form conversion table */ +static gf inverse[GF_SIZE+1]; /* inverse of field elem. */ + /* inv[\alpha**i]=\alpha**(GF_SIZE-i-1) */ + +/* + * modnn(x) computes x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1, + * without a slow divide. + */ +static inline gf +modnn(int x) +{ + while (x >= GF_SIZE) { + x -= GF_SIZE; + x = (x >> GF_BITS) + (x & GF_SIZE); + } + return x; +} + +#define SWAP(a,b,t) {t tmp; tmp=a; a=b; b=tmp;} + +/* + * gf_mul(x,y) multiplies two numbers. If GF_BITS<=8, it is much + * faster to use a multiplication table. + * + * USE_GF_MULC, GF_MULC0(c) and GF_ADDMULC(x) can be used when multiplying + * many numbers by the same constant. In this case the first + * call sets the constant, and others perform the multiplications. + * A value related to the multiplication is held in a local variable + * declared with USE_GF_MULC . See usage in addmul1(). + */ +#if (GF_BITS <= 8) +static gf gf_mul_table[GF_SIZE + 1][GF_SIZE + 1]; + +#define gf_mul(x,y) gf_mul_table[x][y] + +#define USE_GF_MULC gf * __gf_mulc_ +#define GF_MULC0(c) __gf_mulc_ = gf_mul_table[c] +#define GF_ADDMULC(dst, x) dst ^= __gf_mulc_[x] + +static void +init_mul_table() +{ + int i, j; + for (i=0; i< GF_SIZE+1; i++) + for (j=0; j< GF_SIZE+1; j++) + gf_mul_table[i][j] = gf_exp[modnn(gf_log[i] + gf_log[j]) ] ; + + for (j=0; j< GF_SIZE+1; j++) + gf_mul_table[0][j] = gf_mul_table[j][0] = 0; +} +#else /* GF_BITS > 8 */ +static inline gf +gf_mul(x,y) +{ + if ( (x) == 0 || (y)==0 ) return 0; + + return gf_exp[gf_log[x] + gf_log[y] ] ; +} +#define init_mul_table() + +#define USE_GF_MULC register gf * __gf_mulc_ +#define GF_MULC0(c) __gf_mulc_ = &gf_exp[ gf_log[c] ] +#define GF_ADDMULC(dst, x) { if (x) dst ^= __gf_mulc_[ gf_log[x] ] ; } +#endif + +/* + * Generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m] + * Lookup tables: + * index->polynomial form gf_exp[] contains j= \alpha^i; + * polynomial form -> index form gf_log[ j = \alpha^i ] = i + * \alpha=x is the primitive element of GF(2^m) + * + * For efficiency, gf_exp[] has size 2*GF_SIZE, so that a simple + * multiplication of two numbers can be resolved without calling modnn + */ + +/* + * i use malloc so many times, it is easier to put checks all in + * one place. + */ +static void * +my_malloc(int sz, const char *err_string) +{ + void *p = malloc( sz ); + if (p == NULL) { + fprintf(stderr, "-- malloc failure allocating %s\n", err_string); + exit(1) ; + } + return p ; +} + +#define NEW_GF_MATRIX(rows, cols) \ + (gf *)my_malloc(rows * cols * sizeof(gf), " ## __LINE__ ## " ) + +/* + * initialize the data structures used for computations in GF. + */ +static void +generate_gf(void) +{ + int i; + gf mask; + const char *Pp = allPp[GF_BITS] ; + + mask = 1; /* x ** 0 = 1 */ + gf_exp[GF_BITS] = 0; /* will be updated at the end of the 1st loop */ + /* + * first, generate the (polynomial representation of) powers of \alpha, + * which are stored in gf_exp[i] = \alpha ** i . + * At the same time build gf_log[gf_exp[i]] = i . + * The first GF_BITS powers are simply bits shifted to the left. + */ + for (i = 0; i < GF_BITS; i++, mask <<= 1 ) { + gf_exp[i] = mask; + gf_log[gf_exp[i]] = i; + /* + * If Pp[i] == 1 then \alpha ** i occurs in poly-repr + * gf_exp[GF_BITS] = \alpha ** GF_BITS + */ + if ( Pp[i] == '1' ) + gf_exp[GF_BITS] ^= mask; + } + /* + * now gf_exp[GF_BITS] = \alpha ** GF_BITS is complete, so can als + * compute its inverse. + */ + gf_log[gf_exp[GF_BITS]] = GF_BITS; + /* + * Poly-repr of \alpha ** (i+1) is given by poly-repr of + * \alpha ** i shifted left one-bit and accounting for any + * \alpha ** GF_BITS term that may occur when poly-repr of + * \alpha ** i is shifted. + */ + mask = 1 << (GF_BITS - 1 ) ; + for (i = GF_BITS + 1; i < GF_SIZE; i++) { + if (gf_exp[i - 1] >= mask) + gf_exp[i] = gf_exp[GF_BITS] ^ ((gf_exp[i - 1] ^ mask) << 1); + else + gf_exp[i] = gf_exp[i - 1] << 1; + gf_log[gf_exp[i]] = i; + } + /* + * log(0) is not defined, so use a special value + */ + gf_log[0] = GF_SIZE ; + /* set the extended gf_exp values for fast multiply */ + for (i = 0 ; i < GF_SIZE ; i++) + gf_exp[i + GF_SIZE] = gf_exp[i] ; + + /* + * again special cases. 0 has no inverse. This used to + * be initialized to GF_SIZE, but it should make no difference + * since noone is supposed to read from here. + */ + inverse[0] = 0 ; + inverse[1] = 1; + for (i=2; i<=GF_SIZE; i++) + inverse[i] = gf_exp[GF_SIZE-gf_log[i]]; +} + +/* + * Various linear algebra operations that i use often. + */ + +/* + * addmul() computes dst[] = dst[] + c * src[] + * This is used often, so better optimize it! Currently the loop is + * unrolled 16 times, a good value for 486 and pentium-class machines. + * The case c=0 is also optimized, whereas c=1 is not. These + * calls are unfrequent in my typical apps so I did not bother. + * + * Note that gcc on + */ +#define addmul(dst, src, c, sz) \ + if (c != 0) addmul1(dst, src, c, sz) + +#define UNROLL 16 /* 1, 4, 8, 16 */ +static void +addmul1(gf *dst1, gf *src1, gf c, int sz) +{ + USE_GF_MULC ; + gf *dst = dst1, *src = src1 ; + gf *lim = &dst[sz - UNROLL + 1] ; + + GF_MULC0(c) ; + +#if (UNROLL > 1) /* unrolling by 8/16 is quite effective on the pentium */ + for (; dst < lim ; dst += UNROLL, src += UNROLL ) { + GF_ADDMULC( dst[0] , src[0] ); + GF_ADDMULC( dst[1] , src[1] ); + GF_ADDMULC( dst[2] , src[2] ); + GF_ADDMULC( dst[3] , src[3] ); +#if (UNROLL > 4) + GF_ADDMULC( dst[4] , src[4] ); + GF_ADDMULC( dst[5] , src[5] ); + GF_ADDMULC( dst[6] , src[6] ); + GF_ADDMULC( dst[7] , src[7] ); +#endif +#if (UNROLL > 8) + GF_ADDMULC( dst[8] , src[8] ); + GF_ADDMULC( dst[9] , src[9] ); + GF_ADDMULC( dst[10] , src[10] ); + GF_ADDMULC( dst[11] , src[11] ); + GF_ADDMULC( dst[12] , src[12] ); + GF_ADDMULC( dst[13] , src[13] ); + GF_ADDMULC( dst[14] , src[14] ); + GF_ADDMULC( dst[15] , src[15] ); +#endif + } +#endif + lim += UNROLL - 1 ; + for (; dst < lim; dst++, src++ ) /* final components */ + GF_ADDMULC( *dst , *src ); +} + +/* + * computes C = AB where A is n*k, B is k*m, C is n*m + */ +static void +matmul(gf *a, gf *b, gf *c, int n, int k, int m) +{ + int row, col, i ; + + for (row = 0; row < n ; row++) { + for (col = 0; col < m ; col++) { + gf *pa = &a[ row * k ]; + gf *pb = &b[ col ]; + gf acc = 0 ; + for (i = 0; i < k ; i++, pa++, pb += m ) + acc ^= gf_mul( *pa, *pb ) ; + c[ row * m + col ] = acc ; + } + } +} + +#ifdef DEBUGG +/* + * returns 1 if the square matrix is identiy + * (only for test) + */ +static int +is_identity(gf *m, int k) +{ + int row, col ; + for (row=0; row 1) { + fprintf(stderr, "singular matrix\n"); + goto fail ; + } + } + } + } + if (icol == -1) { + fprintf(stderr, "XXX pivot not found!\n"); + goto fail ; + } +found_piv: + ++(ipiv[icol]) ; + /* + * swap rows irow and icol, so afterwards the diagonal + * element will be correct. Rarely done, not worth + * optimizing. + */ + if (irow != icol) { + for (ix = 0 ; ix < k ; ix++ ) { + SWAP( src[irow*k + ix], src[icol*k + ix], gf) ; + } + } + indxr[col] = irow ; + indxc[col] = icol ; + pivot_row = &src[icol*k] ; + c = pivot_row[icol] ; + if (c == 0) { + fprintf(stderr, "singular matrix 2\n"); + goto fail ; + } + if (c != 1 ) { /* otherwhise this is a NOP */ + /* + * this is done often , but optimizing is not so + * fruitful, at least in the obvious ways (unrolling) + */ + DEB( pivswaps++ ; ) + c = inverse[ c ] ; + pivot_row[icol] = 1 ; + for (ix = 0 ; ix < k ; ix++ ) + pivot_row[ix] = gf_mul(c, pivot_row[ix] ); + } + /* + * from all rows, remove multiples of the selected row + * to zero the relevant entry (in fact, the entry is not zero + * because we know it must be zero). + * (Here, if we know that the pivot_row is the identity, + * we can optimize the addmul). + */ + id_row[icol] = 1; + if (bcmp(pivot_row, id_row, k*sizeof(gf)) != 0) { + for (p = src, ix = 0 ; ix < k ; ix++, p += k ) { + if (ix != icol) { + c = p[icol] ; + p[icol] = 0 ; + addmul(p, pivot_row, c, k ); + } + } + } + id_row[icol] = 0; + } /* done all columns */ + for (col = k-1 ; col >= 0 ; col-- ) { + if (indxr[col] <0 || indxr[col] >= k) + fprintf(stderr, "AARGH, indxr[col] %d\n", indxr[col]); + else if (indxc[col] <0 || indxc[col] >= k) + fprintf(stderr, "AARGH, indxc[col] %d\n", indxc[col]); + else + if (indxr[col] != indxc[col] ) { + for (row = 0 ; row < k ; row++ ) { + SWAP( src[row*k + indxr[col]], src[row*k + indxc[col]], gf) ; + } + } + } + error = 0 ; +fail: + free(indxc); + free(indxr); + free(ipiv); + free(id_row); + free(temp_row); + return error ; +} + +/* + * fast code for inverting a vandermonde matrix. + * XXX NOTE: It assumes that the matrix + * is not singular and _IS_ a vandermonde matrix. Only uses + * the second column of the matrix, containing the p_i's. + * + * Algorithm borrowed from "Numerical recipes in C" -- sec.2.8, but + * largely revised for my purposes. + * p = coefficients of the matrix (p_i) + * q = values of the polynomial (known) + */ + +int +invert_vdm(gf *src, int k) +{ + int i, j, row, col ; + gf *b, *c, *p; + gf t, xx ; + + if (k == 1) /* degenerate case, matrix must be p^0 = 1 */ + return 0 ; + /* + * c holds the coefficient of P(x) = Prod (x - p_i), i=0..k-1 + * b holds the coefficient for the matrix inversion + */ + c = NEW_GF_MATRIX(1, k); + b = NEW_GF_MATRIX(1, k); + + p = NEW_GF_MATRIX(1, k); + + for ( j=1, i = 0 ; i < k ; i++, j+=k ) { + c[i] = 0 ; + p[i] = src[j] ; /* p[i] */ + } + /* + * construct coeffs. recursively. We know c[k] = 1 (implicit) + * and start P_0 = x - p_0, then at each stage multiply by + * x - p_i generating P_i = x P_{i-1} - p_i P_{i-1} + * After k steps we are done. + */ + c[k-1] = p[0] ; /* really -p(0), but x = -x in GF(2^m) */ + for (i = 1 ; i < k ; i++ ) { + gf p_i = p[i] ; /* see above comment */ + for (j = k-1 - ( i - 1 ) ; j < k-1 ; j++ ) + c[j] ^= gf_mul( p_i, c[j+1] ) ; + c[k-1] ^= p_i ; + } + + for (row = 0 ; row < k ; row++ ) { + /* + * synthetic division etc. + */ + xx = p[row] ; + t = 1 ; + b[k-1] = 1 ; /* this is in fact c[k] */ + for (i = k-2 ; i >= 0 ; i-- ) { + b[i] = c[i+1] ^ gf_mul(xx, b[i+1]) ; + t = gf_mul(xx, t) ^ b[i] ; + } + for (col = 0 ; col < k ; col++ ) + src[col*k + row] = gf_mul(inverse[t], b[col] ); + } + free(c) ; + free(b) ; + free(p) ; + return 0 ; +} + +static int fec_initialized = 0 ; +static void +init_fec() +{ + TICK(ticks[0]); + generate_gf(); + TOCK(ticks[0]); + DDB(fprintf(stderr, "generate_gf took %ldus\n", ticks[0]);) + TICK(ticks[0]); + init_mul_table(); + TOCK(ticks[0]); + DDB(fprintf(stderr, "init_mul_table took %ldus\n", ticks[0]);) + fec_initialized = 1 ; +} + +/* + * This section contains the proper FEC encoding/decoding routines. + * The encoding matrix is computed starting with a Vandermonde matrix, + * and then transforming it into a systematic matrix. + */ + +#define FEC_MAGIC 0xFECC0DEC + +void +fec_free(struct fec_parms *p) +{ + if (p==NULL || + p->magic != ( ( (FEC_MAGIC ^ p->k) ^ p->n) ^ (unsigned long)(p->enc_matrix)) ) { + fprintf(stderr, "bad parameters to fec_free\n"); + return ; + } + free(p->enc_matrix); + free(p); +} + +/* + * create a new encoder, returning a descriptor. This contains k,n and + * the encoding matrix. + */ +struct fec_parms * +fec_new(int k, int n) +{ + int row, col ; + gf *p, *tmp_m ; + + struct fec_parms *retval ; + + if (fec_initialized == 0) + init_fec(); + + if (k > GF_SIZE + 1 || n > GF_SIZE + 1 || k > n ) { + fprintf(stderr, "Invalid parameters k %d n %d GF_SIZE %d\n", + k, n, GF_SIZE ); + return NULL ; + } + retval = (struct fec_parms *)my_malloc(sizeof(struct fec_parms), "new_code"); + retval->k = k ; + retval->n = n ; + retval->enc_matrix = NEW_GF_MATRIX(n, k); + retval->magic = ( ( FEC_MAGIC ^ k) ^ n) ^ (unsigned long)(retval->enc_matrix) ; + tmp_m = NEW_GF_MATRIX(n, k); + /* + * fill the matrix with powers of field elements, starting from 0. + * The first row is special, cannot be computed with exp. table. + */ + tmp_m[0] = 1 ; + for (col = 1; col < k ; col++) + tmp_m[col] = 0 ; + for (p = tmp_m + k, row = 0; row < n-1 ; row++, p += k) { + for ( col = 0 ; col < k ; col ++ ) + p[col] = gf_exp[modnn(row*col)]; + } + + /* + * quick code to build systematic matrix: invert the top + * k*k vandermonde matrix, multiply right the bottom n-k rows + * by the inverse, and construct the identity matrix at the top. + */ + TICK(ticks[3]); + invert_vdm(tmp_m, k); /* much faster than invert_mat */ + matmul(tmp_m + k*k, tmp_m, retval->enc_matrix + k*k, n - k, k, k); + /* + * the upper matrix is I so do not bother with a slow multiply + */ + bzero(retval->enc_matrix, k*k*sizeof(gf) ); + for (p = retval->enc_matrix, col = 0 ; col < k ; col++, p += k+1 ) + *p = 1 ; + free(tmp_m); + TOCK(ticks[3]); + + DDB(fprintf(stderr, "--- %ld us to build encoding matrix\n", + ticks[3]);) + DEB(pr_matrix(retval->enc_matrix, n, k, "encoding_matrix");) + return retval ; +} + +/* + * fec_encode accepts as input pointers to n data packets of size sz, + * and produces as output a packet pointed to by fec, computed + * with index "index". + */ +void +fec_encode(struct fec_parms *code, gf *src[], gf *fec, int index, int sz) +{ + int i, k = code->k ; + gf *p ; + + if (GF_BITS > 8) + sz /= 2 ; + + if (index < k) + bcopy(src[index], fec, sz*sizeof(gf) ) ; + else if (index < code->n) { + p = &(code->enc_matrix[index*k] ); + bzero(fec, sz*sizeof(gf)); + for (i = 0; i < k ; i++) + addmul(fec, src[i], p[i], sz ) ; + } else + fprintf(stderr, "Invalid index %d (max %d)\n", + index, code->n - 1 ); +} + +/* + * shuffle move src packets in their position + */ +static int +shuffle(gf *pkt[], int index[], int k) +{ + int i; + + for ( i = 0 ; i < k ; ) { + if (index[i] >= k || index[i] == i) + i++ ; + else { + /* + * put pkt in the right position (first check for conflicts). + */ + int c = index[i] ; + + if (index[c] == c) { + DEB(fprintf(stderr, "\nshuffle, error at %d\n", i);) + return 1 ; + } + SWAP(index[i], index[c], int) ; + SWAP(pkt[i], pkt[c], gf *) ; + } + } + DEB( /* just test that it works... */ + for ( i = 0 ; i < k ; i++ ) { + if (index[i] < k && index[i] != i) { + fprintf(stderr, "shuffle: after\n"); + for (i=0; ik ; + gf *p, *matrix = NEW_GF_MATRIX(k, k); + + TICK(ticks[9]); + for (i = 0, p = matrix ; i < k ; i++, p += k ) { +#if 1 /* this is simply an optimization, not very useful indeed */ + if (index[i] < k) { + bzero(p, k*sizeof(gf) ); + p[i] = 1 ; + } else +#endif + if (index[i] < code->n ) + bcopy( &(code->enc_matrix[index[i]*k]), p, k*sizeof(gf) ); + else { + fprintf(stderr, "decode: invalid index %d (max %d)\n", + index[i], code->n - 1 ); + free(matrix) ; + return NULL ; + } + } + TICK(ticks[9]); + if (invert_mat(matrix, k)) { + free(matrix); + matrix = NULL ; + } + TOCK(ticks[9]); + return matrix ; +} + +/* + * fec_decode receives as input a vector of packets, the indexes of + * packets, and produces the correct vector as output. + * + * Input: + * code: pointer to code descriptor + * pkt: pointers to received packets. They are modified + * to store the output packets (in place) + * index: pointer to packet indexes (modified) + * sz: size of each packet + */ +int +fec_decode(struct fec_parms *code, gf *pkt[], int index[], int sz) +{ + gf *m_dec ; + gf **new_pkt ; + int row, col , k = code->k ; + + if (GF_BITS > 8) + sz /= 2 ; + + if (shuffle(pkt, index, k)) /* error if true */ + return 1 ; + m_dec = build_decode_matrix(code, pkt, index); + + if (m_dec == NULL) + return 1 ; /* error */ + /* + * do the actual decoding + */ + new_pkt = (gf**)my_malloc (k * sizeof (gf * ), "new pkt pointers" ); + for (row = 0 ; row < k ; row++ ) { + if (index[row] >= k) { + new_pkt[row] = (gf*) my_malloc (sz * sizeof (gf), "new pkt buffer" ); + bzero(new_pkt[row], sz * sizeof(gf) ) ; + for (col = 0 ; col < k ; col++ ) + addmul(new_pkt[row], pkt[col], m_dec[row*k + col], sz) ; + } + } + /* + * move pkts to their final destination + */ + for (row = 0 ; row < k ; row++ ) { + if (index[row] >= k) { + bcopy(new_pkt[row], pkt[row], sz*sizeof(gf)); + free(new_pkt[row]); + } + } + free(new_pkt); + free(m_dec); + + return 0; +} diff --git a/libtransport/src/core/fec.h b/libtransport/src/core/fec.h new file mode 100644 index 000000000..8234057a7 --- /dev/null +++ b/libtransport/src/core/fec.h @@ -0,0 +1,65 @@ +/* + * fec.c -- forward error correction based on Vandermonde matrices + * 980614 + * (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it) + * + * Portions derived from code by Phil Karn (karn@ka9q.ampr.org), + * Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari + * Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* + * The following parameter defines how many bits are used for + * field elements. The code supports any value from 2 to 16 + * but fastest operation is achieved with 8 bit elements + * This is the only parameter you may want to change. + */ +#ifndef GF_BITS +#define GF_BITS 8 /* code over GF(2**GF_BITS) - change to suit */ +#endif + +#if (GF_BITS <= 8) +typedef unsigned char gf; +#else +typedef unsigned short gf; +#endif + +#define GF_SIZE ((1 << GF_BITS) - 1) /* powers of \alpha */ + +struct fec_parms { + unsigned long magic ; + int k, n ; /* parameters of the code */ + gf *enc_matrix ; +}; + +void fec_free(struct fec_parms *p) ; +struct fec_parms *fec_new(int k, int n) ; + +void fec_encode(struct fec_parms *code, gf *src[], gf *fec, int index, int sz); +int fec_decode(struct fec_parms *code, gf *pkt[], int index[], int sz); + +/* end of file */ diff --git a/libtransport/src/core/global_configuration.cc b/libtransport/src/core/global_configuration.cc new file mode 100644 index 000000000..e0b6c040a --- /dev/null +++ b/libtransport/src/core/global_configuration.cc @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include + +namespace transport { +namespace core { + +GlobalConfiguration::GlobalConfiguration() {} + +bool GlobalConfiguration::parseTransportConfig(const std::string& path) { + using namespace libconfig; + Config cfg; + + try { + cfg.readFile(path.c_str()); + } catch (const FileIOException& fioex) { + TRANSPORT_LOGE("I/O error while reading file."); + return false; + } catch (const ParseException& pex) { + TRANSPORT_LOGE("Parse error at %s:%d - %s", pex.getFile(), pex.getLine(), + pex.getError()); + return false; + } + + Setting& root = cfg.getRoot(); + + /** + * Iterate over sections. Best thing to do here would be to have other + * components of the program registering a callback here, to parse their + * section of the configuration file. + */ + for (auto section = root.begin(); section != root.end(); section++) { + std::string name = section->getName(); + std::error_code ec; + TRANSPORT_LOGD("Parsing Section: %s", name.c_str()); + + auto it = configuration_parsers_.find(name); + if (it != configuration_parsers_.end() && !it->second.first) { + TRANSPORT_LOGD("Found valid configuration parser"); + it->second.second(*section, ec); + it->second.first = true; + } + } + + return true; +} + +void GlobalConfiguration::parseConfiguration(const std::string& path) { + // Check if an environment variable with the configuration path exists. COnf + // variable comes first. + std::unique_lock lck(cp_mtx_); + if (const char* env_c = std::getenv(GlobalConfiguration::conf_file)) { + parseTransportConfig(env_c); + } else if (!path.empty()) { + conf_file_path_ = path; + parseTransportConfig(conf_file_path_); + } else { + TRANSPORT_LOGD( + "Called parseConfiguration but no configuration file was provided."); + } +} + +void GlobalConfiguration::registerConfigurationSetter( + const std::string& key, const SetCallback& set_callback) { + std::unique_lock lck(cp_mtx_); + if (configuration_setters_.find(key) != configuration_setters_.end()) { + TRANSPORT_LOGW( + "Trying to register configuration setter %s twice. Ignoring second " + "registration attempt.", + key.c_str()); + } else { + configuration_setters_.emplace(key, set_callback); + } +} + +void GlobalConfiguration::registerConfigurationGetter( + const std::string& key, const GetCallback& get_callback) { + std::unique_lock lck(cp_mtx_); + if (configuration_getters_.find(key) != configuration_getters_.end()) { + TRANSPORT_LOGW( + "Trying to register configuration getter %s twice. Ignoring second " + "registration attempt.", + key.c_str()); + } else { + configuration_getters_.emplace(key, get_callback); + } +} + +void GlobalConfiguration::registerConfigurationParser( + const std::string& key, const ParserCallback& parser) { + std::unique_lock lck(cp_mtx_); + if (configuration_parsers_.find(key) != configuration_parsers_.end()) { + TRANSPORT_LOGW( + "Trying to register configuration key %s twice. Ignoring second " + "registration attempt.", + key.c_str()); + } else { + configuration_parsers_.emplace(key, std::make_pair(false, parser)); + + // Trigger a parsing of the configuration. + if (!conf_file_path_.empty()) { + parseTransportConfig(conf_file_path_); + } + } +} + +void GlobalConfiguration::unregisterConfigurationParser( + const std::string& key) { + std::unique_lock lck(cp_mtx_); + auto it = configuration_parsers_.find(key); + if (it != configuration_parsers_.end()) { + configuration_parsers_.erase(it); + } +} + +void GlobalConfiguration::unregisterConfigurationSetter( + const std::string& key) { + std::unique_lock lck(cp_mtx_); + auto it = configuration_setters_.find(key); + if (it != configuration_setters_.end()) { + configuration_setters_.erase(it); + } +} + +void GlobalConfiguration::unregisterConfigurationGetter( + const std::string& key) { + std::unique_lock lck(cp_mtx_); + auto it = configuration_getters_.find(key); + if (it != configuration_getters_.end()) { + configuration_getters_.erase(it); + } +} + +void GlobalConfiguration::getConfiguration( + interface::global_config::ConfigurationObject& configuration_object, + std::error_code& ec) { + auto it = configuration_getters_.find(configuration_object.getKey()); + + if (it != configuration_getters_.end()) { + it->second(configuration_object, ec); + } +} + +void GlobalConfiguration::setConfiguration( + const interface::global_config::ConfigurationObject& configuration_object, + std::error_code& ec) { + auto it = configuration_setters_.find(configuration_object.getKey()); + + if (it != configuration_setters_.end()) { + it->second(configuration_object, ec); + } +} + +} // namespace core +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/core/global_configuration.h b/libtransport/src/core/global_configuration.h new file mode 100644 index 000000000..dcc8d94e3 --- /dev/null +++ b/libtransport/src/core/global_configuration.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace libconfig { +class Setting; +} + +namespace transport { +namespace core { + +/** + * Class holding workflow for global configuration. + * This class does not contains the actual configuration, which is rather stored + * inside the modules to be configured. This class contains the handlers to call + * for getting/setting the configurations and to parse the corresponding + * sections of the configuration file. Each class register 3 callbacks: one to + * parse conf section and 2 to set/get the configuration through programming + * interface. + */ +class GlobalConfiguration : public utils::Singleton { + static const constexpr char *conf_file = "TRANSPORT_CONFIG"; + friend class utils::Singleton; + + public: + /** + * This callback will be called by GlobalConfiguration in + * + */ + using ParserCallback = std::function; + using GetCallback = + std::function; + + using SetCallback = std::function; + + ~GlobalConfiguration() = default; + + public: + void parseConfiguration(const std::string &path); + + void registerConfigurationParser(const std::string &key, + const ParserCallback &parser); + + void registerConfigurationSetter(const std::string &key, + const SetCallback &set_callback); + void registerConfigurationGetter(const std::string &key, + const GetCallback &get_callback); + + void unregisterConfigurationParser(const std::string &key); + + void unregisterConfigurationSetter(const std::string &key); + + void unregisterConfigurationGetter(const std::string &key); + + void getConfiguration( + interface::global_config::ConfigurationObject &configuration_object, + std::error_code &ec); + void setConfiguration( + const interface::global_config::ConfigurationObject &configuration_object, + std::error_code &ec); + + private: + GlobalConfiguration(); + std::string conf_file_path_; + bool parseTransportConfig(const std::string &path); + + private: + std::mutex cp_mtx_; + using ParserPair = std::pair; + std::map configuration_parsers_; + std::map configuration_getters_; + std::map configuration_setters_; +}; + +} // namespace core +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/core/interest.cc b/libtransport/src/core/interest.cc index 9ee662615..06cbe9f81 100644 --- a/libtransport/src/core/interest.cc +++ b/libtransport/src/core/interest.cc @@ -31,8 +31,9 @@ namespace transport { namespace core { -Interest::Interest(const Name &interest_name, Packet::Format format) - : Packet(format) { +Interest::Interest(const Name &interest_name, Packet::Format format, + std::size_t additional_header_size) + : Packet(format, additional_header_size) { if (hicn_interest_set_name(format_, packet_start_, interest_name.getConstStructReference()) < 0) { throw errors::MalformedPacketException(); @@ -45,20 +46,14 @@ Interest::Interest(const Name &interest_name, Packet::Format format) } #ifdef __ANDROID__ -Interest::Interest(hicn_format_t format) : Interest(Name("0::0|0"), format) {} +Interest::Interest(hicn_format_t format, std::size_t additional_header_size) + : Interest(Name("0::0|0"), format, additional_header_size) {} #else -Interest::Interest(hicn_format_t format) : Interest(base_name, format) {} +Interest::Interest(hicn_format_t format, std::size_t additional_header_size) + : Interest(base_name, format, additional_header_size) {} #endif -Interest::Interest(const uint8_t *buffer, std::size_t size) - : Packet(buffer, size) { - if (hicn_interest_get_name(format_, packet_start_, - name_.getStructReference()) < 0) { - throw errors::MalformedPacketException(); - } -} - -Interest::Interest(MemBufPtr &&buffer) : Packet(std::move(buffer)) { +Interest::Interest(MemBuf &&buffer) : Packet(std::move(buffer)) { if (hicn_interest_get_name(format_, packet_start_, name_.getStructReference()) < 0) { throw errors::MalformedPacketException(); @@ -70,6 +65,14 @@ Interest::Interest(Interest &&other_interest) name_ = std::move(other_interest.name_); } +Interest::Interest(const Interest &other_interest) : Packet(other_interest) { + name_ = other_interest.name_; +} + +Interest &Interest::operator=(const Interest &other) { + return (Interest &)Packet::operator=(other); +} + Interest::~Interest() {} const Name &Interest::getName() const { @@ -152,6 +155,59 @@ void Interest::resetForHash() { } } +bool Interest::hasManifest() { + return (getPayloadType() == PayloadType::MANIFEST); +} + +void Interest::appendSuffix(std::uint32_t suffix) { + if (TRANSPORT_EXPECT_FALSE(suffix_set_.empty())) { + setPayloadType(PayloadType::MANIFEST); + } + + suffix_set_.emplace(suffix); +} + +void Interest::encodeSuffixes() { + if (!hasManifest()) { + return; + } + + // We assume interest does not hold signature for the moment. + auto int_manifest_header = + (InterestManifestHeader *)(writableData() + headerSize()); + int_manifest_header->n_suffixes = suffix_set_.size(); + std::size_t additional_length = + int_manifest_header->n_suffixes * sizeof(uint32_t); + + uint32_t *suffix = (uint32_t *)(int_manifest_header + 1); + for (auto it = suffix_set_.begin(); it != suffix_set_.end(); it++, suffix++) { + *suffix = *it; + } + + updateLength(additional_length); +} + +uint32_t *Interest::firstSuffix() { + if (!hasManifest()) { + return nullptr; + } + + auto ret = (InterestManifestHeader *)(writableData() + headerSize()); + ret += 1; + + return (uint32_t *)ret; +} + +uint32_t Interest::numberOfSuffixes() { + if (!hasManifest()) { + return 0; + } + + auto header = (InterestManifestHeader *)(writableData() + headerSize()); + + return header->n_suffixes; +} + } // end namespace core } // end namespace transport diff --git a/libtransport/src/core/io_module.cc b/libtransport/src/core/io_module.cc new file mode 100644 index 000000000..fef0c1504 --- /dev/null +++ b/libtransport/src/core/io_module.cc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include + +#ifdef ANDROID +#include +#endif + +#include + +namespace transport { +namespace core { + +IoModule::~IoModule() {} + +IoModule *IoModule::load(const char *module_name) { +#ifdef ANDROID + return new HicnForwarderModule(); +#else + void *handle = 0; + IoModule *module = 0; + IoModule *(*creator)(void) = 0; + const char *error = 0; + + // open module + handle = dlopen(module_name, RTLD_NOW); + if (!handle) { + if ((error = dlerror()) != 0) { + TRANSPORT_LOGE("%s", error); + } + return 0; + } + + // link factory method + creator = (IoModule * (*)(void)) dlsym(handle, "create_module"); + if (!creator) { + if ((error = dlerror()) != 0) { + TRANSPORT_LOGE("%s", error); + return 0; + } + } + + // create object and return it + module = (*creator)(); + module->handle_ = handle; + + return module; +#endif +} + +bool IoModule::unload(IoModule *module) { + if (!module) { + return false; + } + +#ifdef ANDROID + delete module; +#else + // destroy object and close module + void *handle = module->handle_; + delete module; + dlclose(handle); +#endif + + return true; +} + +} // namespace core +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/core/local_connector.cc b/libtransport/src/core/local_connector.cc new file mode 100644 index 000000000..f0e36a3d7 --- /dev/null +++ b/libtransport/src/core/local_connector.cc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +namespace transport { +namespace core { + +LocalConnector::~LocalConnector() {} + +void LocalConnector::close() { state_ = State::CLOSED; } + +void LocalConnector::send(Packet &packet) { + if (!isConnected()) { + return; + } + + TRANSPORT_LOGD("Sending packet to local socket."); + io_service_.get().post([this, p{packet.shared_from_this()}]() mutable { + receive_callback_(this, *p, std::make_error_code(std::errc(0))); + }); +} + +void LocalConnector::send(const uint8_t *packet, std::size_t len) { + throw errors::NotImplementedException(); +} + +} // namespace core +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/core/local_connector.h b/libtransport/src/core/local_connector.h new file mode 100644 index 000000000..b0daa4f53 --- /dev/null +++ b/libtransport/src/core/local_connector.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#ifndef ASIO_STANDALONE +#define ASIO_STANDALONE +#endif +#include + +namespace transport { +namespace core { + +class LocalConnector : public Connector { + public: + template + LocalConnector(asio::io_service &io_service, + ReceiveCallback &&receive_callback, SentCallback &&packet_sent, + OnClose &&close_callback, OnReconnect &&on_reconnect) + : Connector(receive_callback, packet_sent, close_callback, on_reconnect), + io_service_(io_service), + io_service_work_(io_service_.get()) { + state_ = State::CONNECTED; + } + + ~LocalConnector() override; + + void send(Packet &packet) override; + + void send(const uint8_t *packet, std::size_t len) override; + + void close() override; + + auto shared_from_this() { return utils::shared_from(this); } + + private: + std::reference_wrapper io_service_; + asio::io_service::work io_service_work_; + std::string name_; +}; + +} // namespace core +} // namespace transport diff --git a/libtransport/src/core/manifest.h b/libtransport/src/core/manifest.h index eadfed752..9b25ebd67 100644 --- a/libtransport/src/core/manifest.h +++ b/libtransport/src/core/manifest.h @@ -15,11 +15,10 @@ #pragma once +#include #include #include -#include - #include namespace transport { @@ -36,18 +35,20 @@ class Manifest : public Base { "Base must inherit from packet!"); public: + // core::ContentObjectManifest::Ptr + using Encoder = typename FormatTraits::Encoder; using Decoder = typename FormatTraits::Decoder; Manifest(std::size_t signature_size = 0) - : Base(HF_INET6_TCP_AH), + : Base(HF_INET6_TCP_AH, signature_size), encoder_(*this, signature_size), decoder_(*this) { Base::setPayloadType(PayloadType::MANIFEST); } Manifest(const core::Name &name, std::size_t signature_size = 0) - : Base(name, HF_INET6_TCP_AH), + : Base(name, HF_INET6_TCP_AH, signature_size), encoder_(*this, signature_size), decoder_(*this) { Base::setPayloadType(PayloadType::MANIFEST); @@ -55,7 +56,9 @@ class Manifest : public Base { template Manifest(T &&base) - : Base(std::forward(base)), encoder_(*this), decoder_(*this) { + : Base(std::forward(base)), + encoder_(*this, 0, false), + decoder_(*this) { Base::setPayloadType(PayloadType::MANIFEST); } @@ -96,13 +99,13 @@ class Manifest : public Base { return *this; } - Manifest &setHashAlgorithm(utils::CryptoHashType hash_algorithm) { + Manifest &setHashAlgorithm(auth::CryptoHashType hash_algorithm) { hash_algorithm_ = hash_algorithm; encoder_.setHashAlgorithm(hash_algorithm_); return *this; } - utils::CryptoHashType getHashAlgorithm() { return hash_algorithm_; } + auth::CryptoHashType getHashAlgorithm() { return hash_algorithm_; } ManifestType getManifestType() const { return manifest_type_; } @@ -138,7 +141,7 @@ class Manifest : public Base { protected: ManifestType manifest_type_; - utils::CryptoHashType hash_algorithm_; + auth::CryptoHashType hash_algorithm_; bool is_last_; Encoder encoder_; diff --git a/libtransport/src/core/manifest_format.h b/libtransport/src/core/manifest_format.h index 36d23f99b..b759942cb 100644 --- a/libtransport/src/core/manifest_format.h +++ b/libtransport/src/core/manifest_format.h @@ -15,8 +15,8 @@ #pragma once +#include #include -#include #include #include @@ -63,8 +63,10 @@ template struct format_traits { using Encoder = typename T::Encoder; using Decoder = typename T::Decoder; + using Hash = typename T::Hash; using HashType = typename T::HashType; - using HashList = typename T::HashList; + using Suffix = typename T::Suffix; + using SuffixList = typename T::SuffixList; }; class Packet; @@ -86,7 +88,7 @@ class ManifestEncoder { return static_cast(*this).setManifestTypeImpl(type); } - ManifestEncoder &setHashAlgorithm(utils::CryptoHashType hash) { + ManifestEncoder &setHashAlgorithm(auth::CryptoHashType hash) { return static_cast(*this).setHashAlgorithmImpl(hash); } @@ -160,7 +162,7 @@ class ManifestDecoder { return static_cast(*this).getManifestTypeImpl(); } - utils::CryptoHashType getHashAlgorithm() const { + auth::CryptoHashType getHashAlgorithm() const { return static_cast(*this).getHashAlgorithmImpl(); } diff --git a/libtransport/src/core/manifest_format_fixed.cc b/libtransport/src/core/manifest_format_fixed.cc index ca80c38b1..55280b460 100644 --- a/libtransport/src/core/manifest_format_fixed.cc +++ b/libtransport/src/core/manifest_format_fixed.cc @@ -13,49 +13,50 @@ * limitations under the License. */ +#include #include #include -#include - namespace transport { namespace core { // TODO use preallocated pool of membufs FixedManifestEncoder::FixedManifestEncoder(Packet &packet, - std::size_t signature_size) + std::size_t signature_size, + bool clear) : packet_(packet), - max_size_(Packet::default_mtu - packet_.headerSize() - signature_size), - manifest_( - utils::MemBuf::create(Packet::default_mtu - packet_.headerSize())), - manifest_header_( - reinterpret_cast(manifest_->writableData())), - manifest_entries_(reinterpret_cast( - manifest_->writableData() + sizeof(ManifestHeader))), + max_size_(Packet::default_mtu - packet_.headerSize()), + manifest_header_(reinterpret_cast( + packet_.writableData() + packet_.headerSize())), + manifest_entries_( + reinterpret_cast(manifest_header_ + 1)), current_entry_(0), signature_size_(signature_size) { - *manifest_header_ = {0}; + if (clear) { + *manifest_header_ = {0}; + } } FixedManifestEncoder::~FixedManifestEncoder() {} FixedManifestEncoder &FixedManifestEncoder::encodeImpl() { - manifest_->append(sizeof(ManifestHeader) + - manifest_header_->number_of_entries * - sizeof(ManifestEntry)); - packet_.appendPayload(std::move(manifest_)); + packet_.append(sizeof(ManifestHeader) + + manifest_header_->number_of_entries * sizeof(ManifestEntry)); + packet_.updateLength(); return *this; } FixedManifestEncoder &FixedManifestEncoder::clearImpl() { - manifest_ = utils::MemBuf::create(Packet::default_mtu - packet_.headerSize() - - signature_size_); + packet_.trimEnd(sizeof(ManifestHeader) + + manifest_header_->number_of_entries * sizeof(ManifestEntry)); + current_entry_ = 0; + *manifest_header_ = {0}; return *this; } FixedManifestEncoder &FixedManifestEncoder::setHashAlgorithmImpl( - utils::CryptoHashType algorithm) { + auth::CryptoHashType algorithm) { manifest_header_->hash_algorithm = static_cast(algorithm); return *this; } @@ -83,7 +84,7 @@ FixedManifestEncoder &FixedManifestEncoder::setBaseNameImpl( } FixedManifestEncoder &FixedManifestEncoder::addSuffixAndHashImpl( - uint32_t suffix, const utils::CryptoHash &hash) { + uint32_t suffix, const auth::CryptoHash &hash) { auto _hash = hash.getDigest(); addSuffixHashBytes(suffix, _hash.data(), _hash.length()); return *this; @@ -170,8 +171,8 @@ ManifestType FixedManifestDecoder::getManifestTypeImpl() const { return static_cast(manifest_header_->manifest_type); } -utils::CryptoHashType FixedManifestDecoder::getHashAlgorithmImpl() const { - return static_cast(manifest_header_->hash_algorithm); +auth::CryptoHashType FixedManifestDecoder::getHashAlgorithmImpl() const { + return static_cast(manifest_header_->hash_algorithm); } NextSegmentCalculationStrategy diff --git a/libtransport/src/core/manifest_format_fixed.h b/libtransport/src/core/manifest_format_fixed.h index 1d7cd7d32..56ad4ef6d 100644 --- a/libtransport/src/core/manifest_format_fixed.h +++ b/libtransport/src/core/manifest_format_fixed.h @@ -15,9 +15,8 @@ #pragma once -#include - #include +#include #include @@ -53,8 +52,10 @@ class Packet; struct Fixed { using Encoder = FixedManifestEncoder; using Decoder = FixedManifestDecoder; - using HashType = utils::CryptoHash; - using SuffixList = std::list>; + using Hash = auth::CryptoHash; + using HashType = auth::CryptoHashType; + using Suffix = uint32_t; + using SuffixList = std::list>; }; struct Flags { @@ -84,7 +85,8 @@ static const constexpr std::uint8_t manifest_version = 1; class FixedManifestEncoder : public ManifestEncoder { public: - FixedManifestEncoder(Packet &packet, std::size_t signature_size = 0); + FixedManifestEncoder(Packet &packet, std::size_t signature_size = 0, + bool clear = true); ~FixedManifestEncoder(); @@ -94,7 +96,7 @@ class FixedManifestEncoder : public ManifestEncoder { FixedManifestEncoder &setManifestTypeImpl(ManifestType manifest_type); - FixedManifestEncoder &setHashAlgorithmImpl(utils::CryptoHashType algorithm); + FixedManifestEncoder &setHashAlgorithmImpl(Fixed::HashType algorithm); FixedManifestEncoder &setNextSegmentCalculationStrategyImpl( NextSegmentCalculationStrategy strategy); @@ -102,7 +104,7 @@ class FixedManifestEncoder : public ManifestEncoder { FixedManifestEncoder &setBaseNameImpl(const core::Name &base_name); FixedManifestEncoder &addSuffixAndHashImpl(uint32_t suffix, - const utils::CryptoHash &hash); + const Fixed::Hash &hash); FixedManifestEncoder &setIsFinalManifestImpl(bool is_last); @@ -125,7 +127,6 @@ class FixedManifestEncoder : public ManifestEncoder { Packet &packet_; std::size_t max_size_; - std::unique_ptr manifest_; ManifestHeader *manifest_header_; ManifestEntry *manifest_entries_; std::size_t current_entry_; @@ -144,7 +145,7 @@ class FixedManifestDecoder : public ManifestDecoder { ManifestType getManifestTypeImpl() const; - utils::CryptoHashType getHashAlgorithmImpl() const; + Fixed::HashType getHashAlgorithmImpl() const; NextSegmentCalculationStrategy getNextSegmentCalculationStrategyImpl() const; diff --git a/libtransport/src/core/manifest_inline.h b/libtransport/src/core/manifest_inline.h index dedf82b45..fcb1d214f 100644 --- a/libtransport/src/core/manifest_inline.h +++ b/libtransport/src/core/manifest_inline.h @@ -30,8 +30,12 @@ class ManifestInline : public Manifest> { using ManifestBase = Manifest>; + + using Hash = typename FormatTraits::Hash; using HashType = typename FormatTraits::HashType; + using Suffix = typename FormatTraits::Suffix; using SuffixList = typename FormatTraits::SuffixList; + using HashEntry = std::pair>; public: ManifestInline() : ManifestBase() {} @@ -44,7 +48,7 @@ class ManifestInline static TRANSPORT_ALWAYS_INLINE ManifestInline *createManifest( const core::Name &manifest_name, ManifestVersion version, - ManifestType type, utils::CryptoHashType algorithm, bool is_last, + ManifestType type, auth::CryptoHashType algorithm, bool is_last, const Name &base_name, NextSegmentCalculationStrategy strategy, std::size_t signature_size) { auto manifest = new ManifestInline(manifest_name, signature_size); @@ -84,7 +88,7 @@ class ManifestInline const Name &getBaseName() { return base_name_; } - ManifestInline &addSuffixHash(uint32_t suffix, const HashType &hash) { + ManifestInline &addSuffixHash(Suffix suffix, const Hash &hash) { ManifestBase::encoder_.addSuffixAndHash(suffix, hash); return *this; } @@ -104,12 +108,35 @@ class ManifestInline return next_segment_strategy_; } + // Convert several manifests into a single map from suffixes to packet hashes. + // All manifests must have been decoded beforehand. + static std::unordered_map getSuffixMap( + const std::vector &manifests) { + std::unordered_map suffix_map; + + for (auto manifest_ptr : manifests) { + HashType hash_algorithm = manifest_ptr->getHashAlgorithm(); + SuffixList suffix_list = manifest_ptr->getSuffixList(); + + for (auto it = suffix_list.begin(); it != suffix_list.end(); ++it) { + std::vector hash( + it->second, it->second + auth::hash_size_map[hash_algorithm]); + suffix_map[it->first] = {hash_algorithm, hash}; + } + } + + return suffix_map; + } + static std::unordered_map getSuffixMap( + ManifestInline *manifest) { + return getSuffixMap(std::vector{manifest}); + } + 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 +} // namespace core +} // namespace transport diff --git a/libtransport/src/core/name.cc b/libtransport/src/core/name.cc index 811e93b87..795c8a697 100644 --- a/libtransport/src/core/name.cc +++ b/libtransport/src/core/name.cc @@ -13,14 +13,13 @@ * limitations under the License. */ +#include #include #include #include #include #include -#include - namespace transport { namespace core { @@ -98,7 +97,12 @@ bool Name::operator!=(const Name &name) const { } Name::operator bool() const { - return bool(hicn_name_empty((hicn_name_t *)&name_)); + auto ret = isValid(); + return ret; +} + +bool Name::isValid() const { + return bool(!hicn_name_empty((hicn_name_t *)&name_)); } bool Name::equals(const Name &name, bool consider_segment) const { diff --git a/libtransport/src/core/packet.cc b/libtransport/src/core/packet.cc index cd2c5aa69..6f237729a 100644 --- a/libtransport/src/core/packet.cc +++ b/libtransport/src/core/packet.cc @@ -31,59 +31,94 @@ namespace core { const core::Name Packet::base_name("0::0|0"); -Packet::Packet(Format format) - : packet_(utils::MemBuf::create(getHeaderSizeFromFormat(format, 256)) - .release()), - packet_start_(reinterpret_cast(packet_->writableData())), - header_head_(packet_.get()), - payload_head_(nullptr), - format_(format) { - if (hicn_packet_init_header(format, packet_start_) < 0) { - throw errors::RuntimeException("Unexpected error initializing the packet."); - } - - packet_->append(getHeaderSizeFromFormat(format_)); -} - -Packet::Packet(MemBufPtr &&buffer) - : packet_(std::move(buffer)), - packet_start_(reinterpret_cast(packet_->writableData())), - header_head_(packet_.get()), - payload_head_(nullptr), - format_(getFormatFromBuffer(packet_->writableData(), packet_->length())) { -} - -Packet::Packet(const uint8_t *buffer, std::size_t size) - : Packet(MemBufPtr(utils::MemBuf::copyBuffer(buffer, size).release())) {} +Packet::Packet(Format format, std::size_t additional_header_size) + : utils::MemBuf(utils::MemBuf(CREATE, 2048)), + packet_start_(reinterpret_cast(writableData())), + header_offset_(0), + format_(format), + payload_type_(PayloadType::UNSPECIFIED) { + setFormat(format_, additional_header_size); +} + +Packet::Packet(MemBuf &&buffer) + : utils::MemBuf(std::move(buffer)), + packet_start_(reinterpret_cast(writableData())), + header_offset_(0), + format_(getFormatFromBuffer(data(), length())), + payload_type_(PayloadType::UNSPECIFIED) {} + +Packet::Packet(CopyBufferOp, const uint8_t *buffer, std::size_t size) + : utils::MemBuf(COPY_BUFFER, buffer, size), + packet_start_(reinterpret_cast(writableData())), + header_offset_(0), + format_(getFormatFromBuffer(data(), length())), + payload_type_(PayloadType::UNSPECIFIED) {} + +Packet::Packet(WrapBufferOp, uint8_t *buffer, std::size_t length, + std::size_t size) + : utils::MemBuf(WRAP_BUFFER, buffer, length, size), + packet_start_(reinterpret_cast(writableData())), + header_offset_(0), + format_(getFormatFromBuffer(this->data(), this->length())), + payload_type_(PayloadType::UNSPECIFIED) {} + +Packet::Packet(CreateOp, uint8_t *buffer, std::size_t length, std::size_t size, + Format format, std::size_t additional_header_size) + : utils::MemBuf(WRAP_BUFFER, buffer, length, size), + packet_start_(reinterpret_cast(writableData())), + header_offset_(0), + format_(format), + payload_type_(PayloadType::UNSPECIFIED) { + clear(); + setFormat(format_, additional_header_size); +} + +Packet::Packet(const Packet &other) + : utils::MemBuf(other), + packet_start_(reinterpret_cast(writableData())), + header_offset_(other.header_offset_), + format_(other.format_), + payload_type_(PayloadType::UNSPECIFIED) {} Packet::Packet(Packet &&other) - : packet_(std::move(other.packet_)), + : utils::MemBuf(std::move(other)), packet_start_(other.packet_start_), - header_head_(other.header_head_), - payload_head_(other.payload_head_), - format_(other.format_) { + header_offset_(other.header_offset_), + format_(other.format_), + payload_type_(PayloadType::UNSPECIFIED) { other.packet_start_ = nullptr; - other.header_head_ = nullptr; - other.payload_head_ = nullptr; other.format_ = HF_UNSPEC; + other.header_offset_ = 0; } Packet::~Packet() {} +Packet &Packet::operator=(const Packet &other) { + if (this != &other) { + *this = other; + packet_start_ = reinterpret_cast(writableData()); + } + + return *this; +} + std::size_t Packet::getHeaderSizeFromBuffer(Format format, const uint8_t *buffer) { size_t header_length; + if (hicn_packet_get_header_length(format, (hicn_header_t *)buffer, &header_length) < 0) { throw errors::MalformedPacketException(); } + return header_length; } bool Packet::isInterest(const uint8_t *buffer) { bool is_interest = false; - if (TRANSPORT_EXPECT_FALSE(hicn_packet_test_ece((const hicn_header_t *)buffer, + if (TRANSPORT_EXPECT_FALSE(hicn_packet_test_ece(HF_INET6_TCP, + (const hicn_header_t *)buffer, &is_interest) < 0)) { throw errors::RuntimeException( "Impossible to retrieve ece flag from packet"); @@ -92,6 +127,25 @@ bool Packet::isInterest(const uint8_t *buffer) { return !is_interest; } +void Packet::setFormat(Packet::Format format, + std::size_t additional_header_size) { + format_ = format; + if (hicn_packet_init_header(format_, packet_start_) < 0) { + throw errors::RuntimeException("Unexpected error initializing the packet."); + } + + auto header_size = getHeaderSizeFromFormat(format_); + assert(header_size <= tailroom()); + append(header_size); + + assert(additional_header_size <= tailroom()); + append(additional_header_size); + + header_offset_ = length(); +} + +bool Packet::isInterest() { return Packet::isInterest(data()); } + std::size_t Packet::getPayloadSizeFromBuffer(Format format, const uint8_t *buffer) { std::size_t payload_length; @@ -105,67 +159,58 @@ std::size_t Packet::getPayloadSizeFromBuffer(Format format, } std::size_t Packet::payloadSize() const { - return getPayloadSizeFromBuffer(format_, - reinterpret_cast(packet_start_)); + std::size_t ret = 0; + + if (length()) { + ret = getPayloadSizeFromBuffer(format_, + reinterpret_cast(packet_start_)); + } + + return ret; } std::size_t Packet::headerSize() const { - return getHeaderSizeFromBuffer(format_, - reinterpret_cast(packet_start_)); + if (header_offset_ == 0 && length()) { + const_cast(this)->header_offset_ = getHeaderSizeFromBuffer( + format_, reinterpret_cast(packet_start_)); + } + + return header_offset_; } Packet &Packet::appendPayload(std::unique_ptr &&payload) { - separateHeaderPayload(); - - if (!payload_head_) { - payload_head_ = payload.get(); - } - - header_head_->prependChain(std::move(payload)); + 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 &&header) { - separateHeaderPayload(); + prependPayload(&buffer, &length); - if (!payload_head_) { - header_head_->prependChain(std::move(header)); - } else { - payload_head_->prependChain(std::move(header)); + if (length) { + appendPayload(utils::MemBuf::copyBuffer(buffer, length)); } updateLength(); return *this; } -Packet &Packet::appendHeader(const uint8_t *buffer, std::size_t length) { - return appendHeader(utils::MemBuf::copyBuffer(buffer, length)); -} - std::unique_ptr Packet::getPayload() const { - const_cast(this)->separateHeaderPayload(); - - // Hopefully the payload is contiguous - if (TRANSPORT_EXPECT_FALSE(payload_head_ && - payload_head_->next() != header_head_)) { - payload_head_->gather(payloadSize()); - } - - return payload_head_->cloneOne(); + auto ret = clone(); + ret->trimStart(headerSize()); + return ret; } Packet &Packet::updateLength(std::size_t length) { std::size_t total_length = length; - for (utils::MemBuf *current = payload_head_; - current && current != header_head_; current = current->next()) { + const utils::MemBuf *current = this; + do { total_length += current->length(); - } + current = current->next(); + } while (current != this); + + total_length -= headerSize(); if (hicn_packet_set_payload_length(format_, packet_start_, total_length) < 0) { @@ -176,13 +221,16 @@ Packet &Packet::updateLength(std::size_t length) { } PayloadType Packet::getPayloadType() const { - hicn_payload_type_t ret = HPT_UNSPEC; + if (payload_type_ == PayloadType::UNSPECIFIED) { + hicn_payload_type_t ret; + if (hicn_packet_get_payload_type(packet_start_, &ret) < 0) { + throw errors::RuntimeException("Impossible to retrieve payload type."); + } - if (hicn_packet_get_payload_type(packet_start_, &ret) < 0) { - throw errors::RuntimeException("Impossible to retrieve payload type."); + payload_type_ = (PayloadType)ret; } - return PayloadType(ret); + return payload_type_; } Packet &Packet::setPayloadType(PayloadType payload_type) { @@ -191,60 +239,76 @@ Packet &Packet::setPayloadType(PayloadType payload_type) { throw errors::RuntimeException("Error setting payload type of the packet."); } + payload_type_ = payload_type; + return *this; } Packet::Format Packet::getFormat() const { - if (format_ == HF_UNSPEC) { + /** + * We check packet start because after a movement it will result in a nullptr + */ + if (format_ == HF_UNSPEC && length()) { if (hicn_packet_get_format(packet_start_, &format_) < 0) { - throw errors::MalformedPacketException(); + TRANSPORT_LOGE("Unexpected packet format."); } } return format_; } -const std::shared_ptr Packet::acquireMemBufReference() const { - return packet_; +std::shared_ptr Packet::acquireMemBufReference() { + return std::static_pointer_cast(shared_from_this()); } void Packet::dump() const { - const_cast(this)->separateHeaderPayload(); - TRANSPORT_LOGI("HEADER -- Length: %zu", headerSize()); - hicn_packet_dump((uint8_t *)header_head_->data(), headerSize()); - TRANSPORT_LOGI("PAYLOAD -- Length: %zu", payloadSize()); - for (utils::MemBuf *current = payload_head_; - current && current != header_head_; current = current->next()) { + + const utils::MemBuf *current = this; + do { TRANSPORT_LOGI("MemBuf Length: %zu", current->length()); - hicn_packet_dump((uint8_t *)current->data(), current->length()); - } + dump((uint8_t *)current->data(), current->length()); + current = current->next(); + } while (current != this); +} + +void Packet::dump(uint8_t *buffer, std::size_t length) { + hicn_packet_dump(buffer, length); } void Packet::setSignatureSize(std::size_t size_bytes) { + if (!authenticationHeader()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + int ret = hicn_packet_set_signature_size(format_, packet_start_, size_bytes); if (ret < 0) { - throw errors::RuntimeException("Packet without Authentication Header."); + throw errors::RuntimeException("Error setting signature size."); } - - packet_->append(size_bytes); - updateLength(); } uint8_t *Packet::getSignature() const { + if (!authenticationHeader()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + uint8_t *signature; int ret = hicn_packet_get_signature(format_, packet_start_, &signature); if (ret < 0) { - throw errors::RuntimeException("Packet without Authentication Header."); + throw errors::RuntimeException("Error getting signature."); } return signature; } void Packet::setSignatureTimestamp(const uint64_t ×tamp) { + if (!authenticationHeader()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + int ret = hicn_packet_set_signature_timestamp(format_, packet_start_, timestamp); @@ -254,6 +318,10 @@ void Packet::setSignatureTimestamp(const uint64_t ×tamp) { } uint64_t Packet::getSignatureTimestamp() const { + if (!authenticationHeader()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + uint64_t return_value; int ret = hicn_packet_get_signature_timestamp(format_, packet_start_, &return_value); @@ -266,7 +334,11 @@ uint64_t Packet::getSignatureTimestamp() const { } void Packet::setValidationAlgorithm( - const utils::CryptoSuite &validation_algorithm) { + const auth::CryptoSuite &validation_algorithm) { + if (!authenticationHeader()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + int ret = hicn_packet_set_validation_algorithm(format_, packet_start_, uint8_t(validation_algorithm)); @@ -275,7 +347,11 @@ void Packet::setValidationAlgorithm( } } -utils::CryptoSuite Packet::getValidationAlgorithm() const { +auth::CryptoSuite Packet::getValidationAlgorithm() const { + if (!authenticationHeader()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + uint8_t return_value; int ret = hicn_packet_get_validation_algorithm(format_, packet_start_, &return_value); @@ -284,10 +360,14 @@ utils::CryptoSuite Packet::getValidationAlgorithm() const { throw errors::RuntimeException("Error getting the validation algorithm."); } - return utils::CryptoSuite(return_value); + return auth::CryptoSuite(return_value); } -void Packet::setKeyId(const utils::KeyId &key_id) { +void Packet::setKeyId(const auth::KeyId &key_id) { + if (!authenticationHeader()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + int ret = hicn_packet_set_key_id(format_, packet_start_, key_id.first); if (ret < 0) { @@ -295,8 +375,12 @@ void Packet::setKeyId(const utils::KeyId &key_id) { } } -utils::KeyId Packet::getKeyId() const { - utils::KeyId return_value; +auth::KeyId Packet::getKeyId() const { + if (!authenticationHeader()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + auth::KeyId return_value; int ret = hicn_packet_get_key_id(format_, packet_start_, &return_value.first, &return_value.second); @@ -307,8 +391,8 @@ utils::KeyId Packet::getKeyId() const { return return_value; } -utils::CryptoHash Packet::computeDigest(utils::CryptoHashType algorithm) const { - utils::CryptoHasher hasher(static_cast(algorithm)); +auth::CryptoHash Packet::computeDigest(auth::CryptoHashType algorithm) const { + auth::CryptoHasher hasher(static_cast(algorithm)); hasher.init(); // Copy IP+TCP/ICMP header before zeroing them @@ -318,11 +402,11 @@ utils::CryptoHash Packet::computeDigest(utils::CryptoHashType algorithm) const { const_cast(this)->resetForHash(); - auto current = header_head_; + const utils::MemBuf *current = this; do { hasher.updateBytes(current->data(), current->length()); current = current->next(); - } while (current != header_head_); + } while (current != this); hicn_packet_copy_header(format_, &header_copy, packet_start_, false); @@ -330,15 +414,33 @@ utils::CryptoHash Packet::computeDigest(utils::CryptoHashType algorithm) const { } bool Packet::checkIntegrity() const { - if (hicn_packet_check_integrity(format_, packet_start_) < 0) { + uint16_t partial_csum = + csum(data() + HICN_V6_TCP_HDRLEN, length() - HICN_V6_TCP_HDRLEN, 0); + + for (const utils::MemBuf *current = next(); current != this; + current = current->next()) { + partial_csum = csum(current->data(), current->length(), ~partial_csum); + } + + if (hicn_packet_check_integrity_no_payload(format_, packet_start_, + partial_csum) < 0) { return false; } return true; } +void Packet::prependPayload(const uint8_t **buffer, std::size_t *size) { + auto last = prev(); + auto to_copy = std::min(*size, last->tailroom()); + std::memcpy(last->writableTail(), *buffer, to_copy); + last->append(to_copy); + *size -= to_copy; + *buffer += to_copy; +} + Packet &Packet::setSyn() { - if (hicn_packet_set_syn(packet_start_) < 0) { + if (hicn_packet_set_syn(format_, packet_start_) < 0) { throw errors::RuntimeException("Error setting syn bit in the packet."); } @@ -346,7 +448,7 @@ Packet &Packet::setSyn() { } Packet &Packet::resetSyn() { - if (hicn_packet_reset_syn(packet_start_) < 0) { + if (hicn_packet_reset_syn(format_, packet_start_) < 0) { throw errors::RuntimeException("Error resetting syn bit in the packet."); } @@ -355,7 +457,7 @@ Packet &Packet::resetSyn() { bool Packet::testSyn() const { bool res = false; - if (hicn_packet_test_syn(packet_start_, &res) < 0) { + if (hicn_packet_test_syn(format_, packet_start_, &res) < 0) { throw errors::RuntimeException("Error testing syn bit in the packet."); } @@ -363,7 +465,7 @@ bool Packet::testSyn() const { } Packet &Packet::setAck() { - if (hicn_packet_set_ack(packet_start_) < 0) { + if (hicn_packet_set_ack(format_, packet_start_) < 0) { throw errors::RuntimeException("Error setting ack bit in the packet."); } @@ -371,7 +473,7 @@ Packet &Packet::setAck() { } Packet &Packet::resetAck() { - if (hicn_packet_reset_ack(packet_start_) < 0) { + if (hicn_packet_reset_ack(format_, packet_start_) < 0) { throw errors::RuntimeException("Error resetting ack bit in the packet."); } @@ -380,7 +482,7 @@ Packet &Packet::resetAck() { bool Packet::testAck() const { bool res = false; - if (hicn_packet_test_ack(packet_start_, &res) < 0) { + if (hicn_packet_test_ack(format_, packet_start_, &res) < 0) { throw errors::RuntimeException("Error testing ack bit in the packet."); } @@ -388,7 +490,7 @@ bool Packet::testAck() const { } Packet &Packet::setRst() { - if (hicn_packet_set_rst(packet_start_) < 0) { + if (hicn_packet_set_rst(format_, packet_start_) < 0) { throw errors::RuntimeException("Error setting rst bit in the packet."); } @@ -396,7 +498,7 @@ Packet &Packet::setRst() { } Packet &Packet::resetRst() { - if (hicn_packet_reset_rst(packet_start_) < 0) { + if (hicn_packet_reset_rst(format_, packet_start_) < 0) { throw errors::RuntimeException("Error resetting rst bit in the packet."); } @@ -405,7 +507,7 @@ Packet &Packet::resetRst() { bool Packet::testRst() const { bool res = false; - if (hicn_packet_test_rst(packet_start_, &res) < 0) { + if (hicn_packet_test_rst(format_, packet_start_, &res) < 0) { throw errors::RuntimeException("Error testing rst bit in the packet."); } @@ -413,7 +515,7 @@ bool Packet::testRst() const { } Packet &Packet::setFin() { - if (hicn_packet_set_fin(packet_start_) < 0) { + if (hicn_packet_set_fin(format_, packet_start_) < 0) { throw errors::RuntimeException("Error setting fin bit in the packet."); } @@ -421,7 +523,7 @@ Packet &Packet::setFin() { } Packet &Packet::resetFin() { - if (hicn_packet_reset_fin(packet_start_) < 0) { + if (hicn_packet_reset_fin(format_, packet_start_) < 0) { throw errors::RuntimeException("Error resetting fin bit in the packet."); } @@ -430,7 +532,7 @@ Packet &Packet::resetFin() { bool Packet::testFin() const { bool res = false; - if (hicn_packet_test_fin(packet_start_, &res) < 0) { + if (hicn_packet_test_fin(format_, packet_start_, &res) < 0) { throw errors::RuntimeException("Error testing fin bit in the packet."); } @@ -447,24 +549,29 @@ Packet &Packet::resetFlags() { } std::string Packet::printFlags() const { - std::string flags = ""; + 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(packet_start_, srcPort) < 0) { + if (hicn_packet_set_src_port(format_, packet_start_, srcPort) < 0) { throw errors::RuntimeException("Error setting source port in the packet."); } @@ -472,7 +579,7 @@ Packet &Packet::setSrcPort(uint16_t srcPort) { } Packet &Packet::setDstPort(uint16_t dstPort) { - if (hicn_packet_set_dst_port(packet_start_, dstPort) < 0) { + if (hicn_packet_set_dst_port(format_, packet_start_, dstPort) < 0) { throw errors::RuntimeException( "Error setting destination port in the packet."); } @@ -483,7 +590,7 @@ Packet &Packet::setDstPort(uint16_t dstPort) { uint16_t Packet::getSrcPort() const { uint16_t port = 0; - if (hicn_packet_get_src_port(packet_start_, &port) < 0) { + if (hicn_packet_get_src_port(format_, packet_start_, &port) < 0) { throw errors::RuntimeException("Error reading source port in the packet."); } @@ -493,7 +600,7 @@ uint16_t Packet::getSrcPort() const { uint16_t Packet::getDstPort() const { uint16_t port = 0; - if (hicn_packet_get_dst_port(packet_start_, &port) < 0) { + if (hicn_packet_get_dst_port(format_, packet_start_, &port) < 0) { throw errors::RuntimeException( "Error reading destination port in the packet."); } @@ -518,37 +625,6 @@ uint8_t Packet::getTTL() const { return hops; } -void Packet::separateHeaderPayload() { - if (payload_head_) { - return; - } - - int signature_size = 0; - if (_is_ah(format_)) { - signature_size = (uint32_t)getSignatureSize(); - } - - auto header_size = getHeaderSizeFromFormat(format_, signature_size); - auto payload_length = packet_->length() - header_size; - - packet_->trimEnd(packet_->length()); - - auto payload = packet_->cloneOne(); - payload_head_ = payload.get(); - payload_head_->advance(header_size); - payload_head_->append(payload_length); - packet_->prependChain(std::move(payload)); - packet_->append(header_size); -} - -void Packet::resetPayload() { - if (packet_->isChained()) { - packet_->separateChain(packet_->next(), packet_->prev()); - payload_head_ = nullptr; - updateLength(); - } -} - } // end namespace core } // end namespace transport diff --git a/libtransport/src/core/pending_interest.h b/libtransport/src/core/pending_interest.h index aeff78ea2..ca6411ddf 100644 --- a/libtransport/src/core/pending_interest.h +++ b/libtransport/src/core/pending_interest.h @@ -21,7 +21,6 @@ #include #include #include - #include #include @@ -34,24 +33,21 @@ class HicnForwarderInterface; class VPPForwarderInterface; class RawSocketInterface; -template class Portal; using OnContentObjectCallback = interface::Portal::OnContentObjectCallback; using OnInterestTimeoutCallback = interface::Portal::OnInterestTimeoutCallback; class PendingInterest { - friend class Portal; - friend class Portal; - friend class Portal; + friend class Portal; public: using Ptr = utils::ObjectPool::Ptr; - PendingInterest() - : interest_(nullptr, nullptr), - timer_(), - on_content_object_callback_(), - on_interest_timeout_callback_() {} + // PendingInterest() + // : interest_(nullptr, nullptr), + // timer_(), + // on_content_object_callback_(), + // on_interest_timeout_callback_() {} PendingInterest(Interest::Ptr &&interest, std::unique_ptr &&timer) diff --git a/libtransport/src/core/portal.cc b/libtransport/src/core/portal.cc new file mode 100644 index 000000000..d1d26c5b7 --- /dev/null +++ b/libtransport/src/core/portal.cc @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include + +using namespace transport::interface::global_config; + +namespace transport { +namespace core { + +#ifdef ANDROID +static const constexpr char default_module[] = ""; +#elif defined(MACINTOSH) +static const constexpr char default_module[] = "hicnlight_module.dylib"; +#elif defined(LINUX) +static const constexpr char default_module[] = "hicnlight_module.so"; +#endif + +IoModuleConfiguration Portal::conf_; +std::string Portal::io_module_path_ = defaultIoModule(); + +std::string Portal::defaultIoModule() { + using namespace std::placeholders; + GlobalConfiguration::getInstance().registerConfigurationParser( + io_module_section, + std::bind(&Portal::parseIoModuleConfiguration, _1, _2)); + GlobalConfiguration::getInstance().registerConfigurationGetter( + io_module_section, std::bind(&Portal::getModuleConfiguration, _1, _2)); + GlobalConfiguration::getInstance().registerConfigurationSetter( + io_module_section, std::bind(&Portal::setModuleConfiguration, _1, _2)); + + // return default + conf_.name = default_module; + return default_module; +} + +void Portal::getModuleConfiguration(ConfigurationObject& object, + std::error_code& ec) { + assert(object.getKey() == io_module_section); + + auto conf = dynamic_cast(object); + conf = conf_; + ec = std::error_code(); +} + +std::string getIoModulePath(const std::string& name, + const std::vector& paths, + std::error_code& ec) { +#ifdef LINUX + std::string extension = ".so"; +#elif defined(MACINTOSH) + std::string extension = ".dylib"; +#else +#error "Platform not supported."; +#endif + + std::string complete_path = name; + + if (name.empty()) { + ec = make_error_code(core_error::configuration_parse_failed); + return ""; + } + + complete_path += extension; + + for (auto& p : paths) { + if (p.at(0) != '/') { + TRANSPORT_LOGW("Path %s is not an absolute path. Ignoring it.", + p.c_str()); + continue; + } + + if (utils::File::exists(p + "/" + complete_path)) { + complete_path = p + "/" + complete_path; + break; + } + } + + return complete_path; +} + +void Portal::setModuleConfiguration(const ConfigurationObject& object, + std::error_code& ec) { + assert(object.getKey() == io_module_section); + + const IoModuleConfiguration& conf = + dynamic_cast(object); + auto path = getIoModulePath(conf.name, conf.search_path, ec); + if (!ec) { + conf_ = conf; + io_module_path_ = path; + } +} + +void Portal::parseIoModuleConfiguration(const libconfig::Setting& io_config, + std::error_code& ec) { + using namespace libconfig; + // path property: the list of paths where to look for the module. + std::vector paths; + std::string name; + + if (io_config.exists("path")) { + // get path where looking for modules + const Setting& path_list = io_config.lookup("path"); + auto count = path_list.getLength(); + + for (int i = 0; i < count; i++) { + paths.emplace_back(path_list[i].c_str()); + } + } + + if (io_config.exists("name")) { + io_config.lookupValue("name", name); + } else { + ec = make_error_code(core_error::configuration_parse_failed); + return; + } + + auto path = getIoModulePath(name, paths, ec); + if (!ec) { + conf_.name = name; + conf_.search_path = paths; + io_module_path_ = path; + } +} + +} // namespace core +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/core/portal.h b/libtransport/src/core/portal.h index b63eab3af..59254cf7b 100644 --- a/libtransport/src/core/portal.h +++ b/libtransport/src/core/portal.h @@ -15,24 +15,20 @@ #pragma once -#include #include -#include #include #include #include +#include #include #include #include +#include #include #include #include #include -#ifdef __vpp__ -#include -#endif - #include #include #include @@ -40,17 +36,19 @@ #include #include +namespace libconfig { +class Setting; +} + namespace transport { namespace core { namespace portal_details { -static constexpr uint32_t pool_size = 2048; +static constexpr uint32_t pit_size = 1024; class HandlerMemory { #ifdef __vpp__ - static constexpr std::size_t memory_size = 1024 * 1024; - public: HandlerMemory() {} @@ -58,12 +56,11 @@ class HandlerMemory { HandlerMemory &operator=(const HandlerMemory &) = delete; TRANSPORT_ALWAYS_INLINE void *allocate(std::size_t size) { - return utils::FixedBlockAllocator<128, 4096>::getInstance() - ->allocateBlock(); + return utils::FixedBlockAllocator<128, 8192>::getInstance().allocateBlock(); } TRANSPORT_ALWAYS_INLINE void deallocate(void *pointer) { - utils::FixedBlockAllocator<128, 4096>::getInstance()->deallocateBlock( + utils::FixedBlockAllocator<128, 8192>::getInstance().deallocateBlock( pointer); } #else @@ -159,33 +156,16 @@ class Pool { public: Pool(asio::io_service &io_service) : io_service_(io_service) { increasePendingInterestPool(); - increaseInterestPool(); - increaseContentObjectPool(); } TRANSPORT_ALWAYS_INLINE void increasePendingInterestPool() { // Create pool of pending interests to reuse - for (uint32_t i = 0; i < pool_size; i++) { + for (uint32_t i = 0; i < pit_size; i++) { pending_interests_pool_.add(new PendingInterest( Interest::Ptr(nullptr), std::make_unique(io_service_))); } } - - TRANSPORT_ALWAYS_INLINE void increaseInterestPool() { - // Create pool of interests to reuse - for (uint32_t i = 0; i < pool_size; i++) { - interest_pool_.add(new Interest()); - } - } - - TRANSPORT_ALWAYS_INLINE void increaseContentObjectPool() { - // Create pool of content object to reuse - for (uint32_t i = 0; i < pool_size; i++) { - content_object_pool_.add(new ContentObject()); - } - } - PendingInterest::Ptr getPendingInterest() { auto res = pending_interests_pool_.get(); while (TRANSPORT_EXPECT_FALSE(!res.first)) { @@ -196,35 +176,15 @@ class Pool { return std::move(res.second); } - TRANSPORT_ALWAYS_INLINE ContentObject::Ptr getContentObject() { - auto res = content_object_pool_.get(); - while (TRANSPORT_EXPECT_FALSE(!res.first)) { - increaseContentObjectPool(); - res = content_object_pool_.get(); - } - - return std::move(res.second); - } - - TRANSPORT_ALWAYS_INLINE Interest::Ptr getInterest() { - auto res = interest_pool_.get(); - while (TRANSPORT_EXPECT_FALSE(!res.first)) { - increaseInterestPool(); - res = interest_pool_.get(); - } - - return std::move(res.second); - } - private: utils::ObjectPool pending_interests_pool_; - utils::ObjectPool content_object_pool_; - utils::ObjectPool interest_pool_; asio::io_service &io_service_; }; } // namespace portal_details +class PortalConfiguration; + using PendingInterestHashTable = std::unordered_map; @@ -250,32 +210,32 @@ using interface::BindConfig; * The portal class is not thread safe, appropriate locking is required by the * users of this class. */ -template -class Portal { - static_assert( - std::is_base_of, - ForwarderInt>::value, - "ForwarderInt must inherit from ForwarderInterface!"); +class Portal { public: using ConsumerCallback = interface::Portal::ConsumerCallback; using ProducerCallback = interface::Portal::ProducerCallback; + friend class PortalConfiguration; + Portal() : Portal(internal_io_service_) {} Portal(asio::io_service &io_service) - : io_service_(io_service), + : io_module_(nullptr, [](IoModule *module) { IoModule::unload(module); }), + io_service_(io_service), packet_pool_(io_service), app_name_("libtransport_application"), consumer_callback_(nullptr), producer_callback_(nullptr), - connector_(std::bind(&Portal::processIncomingMessages, this, - std::placeholders::_1), - std::bind(&Portal::setLocalRoutes, this), io_service_, - app_name_), - forwarder_interface_(connector_) {} - + is_consumer_(false) { + /** + * This workaroung allows to initialize memory for packet buffers *before* + * any static variables that may be initialized in the io_modules. In this + * way static variables in modules will be destroyed before the packet + * memory. + */ + PacketManager<>::getInstance(); + } /** * Set the consumer callback. * @@ -304,7 +264,7 @@ class Portal { */ TRANSPORT_ALWAYS_INLINE void setOutputInterface( const std::string &output_interface) { - forwarder_interface_.setOutputInterface(output_interface); + io_module_->setOutputInterface(output_interface); } /** @@ -314,8 +274,19 @@ class Portal { * is a consumer or a producer. */ TRANSPORT_ALWAYS_INLINE void connect(bool is_consumer = true) { - pending_interest_hash_table_.reserve(portal_details::pool_size); - forwarder_interface_.connect(is_consumer); + if (!io_module_) { + pending_interest_hash_table_.reserve(portal_details::pit_size); + io_module_.reset(IoModule::load(io_module_path_.c_str())); + assert(io_module_); + + io_module_->init(std::bind(&Portal::processIncomingMessages, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3), + std::bind(&Portal::setLocalRoutes, this), io_service_, + app_name_); + io_module_->connect(is_consumer); + is_consumer_ = is_consumer; + } } /** @@ -323,14 +294,20 @@ class Portal { */ ~Portal() { killConnection(); } + /** + * Compute name hash + */ + TRANSPORT_ALWAYS_INLINE uint32_t getHash(const Name &name) { + return name.getHash32() + name.getSuffix(); + } + /** * Check if there is already a pending interest for a given name. * * @param name - The interest name. */ TRANSPORT_ALWAYS_INLINE bool interestIsPending(const Name &name) { - auto it = - pending_interest_hash_table_.find(name.getHash32() + name.getSuffix()); + auto it = pending_interest_hash_table_.find(getHash(name)); if (it != pending_interest_hash_table_.end()) { return true; } @@ -357,31 +334,46 @@ class Portal { OnContentObjectCallback &&on_content_object_callback = UNSET_CALLBACK, OnInterestTimeoutCallback &&on_interest_timeout_callback = UNSET_CALLBACK) { - uint32_t hash = - interest->getName().getHash32() + interest->getName().getSuffix(); // Send it - forwarder_interface_.send(*interest); - - auto pending_interest = packet_pool_.getPendingInterest(); - pending_interest->setInterest(std::move(interest)); - pending_interest->setOnContentObjectCallback( - std::move(on_content_object_callback)); - pending_interest->setOnTimeoutCallback( - std::move(on_interest_timeout_callback)); - pending_interest->startCountdown(portal_details::makeCustomAllocatorHandler( - async_callback_memory_, std::bind(&Portal::timerHandler, - this, std::placeholders::_1, hash))); + interest->encodeSuffixes(); + io_module_->send(*interest); + + uint32_t initial_hash = interest->getName().getHash32(); + auto hash = initial_hash + interest->getName().getSuffix(); + uint32_t *suffix = interest->firstSuffix(); + auto n_suffixes = interest->numberOfSuffixes(); + uint32_t counter = 0; + // Set timers + do { + auto pending_interest = packet_pool_.getPendingInterest(); + pending_interest->setInterest(std::move(interest)); + pending_interest->setOnContentObjectCallback( + std::move(on_content_object_callback)); + pending_interest->setOnTimeoutCallback( + std::move(on_interest_timeout_callback)); + + pending_interest->startCountdown( + portal_details::makeCustomAllocatorHandler( + async_callback_memory_, std::bind(&Portal::timerHandler, this, + std::placeholders::_1, hash))); + + auto it = pending_interest_hash_table_.find(hash); + if (it != pending_interest_hash_table_.end()) { + it->second->cancelTimer(); - auto it = pending_interest_hash_table_.find(hash); - if (it != pending_interest_hash_table_.end()) { - it->second->cancelTimer(); + // Get reference to interest packet in order to have it destroyed. + auto _int = it->second->getInterest(); + it->second = std::move(pending_interest); + } else { + pending_interest_hash_table_[hash] = std::move(pending_interest); + } - // Get reference to interest packet in order to have it destroyed. - auto _int = it->second->getInterest(); - it->second = std::move(pending_interest); - } else { - pending_interest_hash_table_[hash] = std::move(pending_interest); - } + if (suffix) { + hash = initial_hash + *suffix; + suffix++; + } + + } while (counter++ < n_suffixes); } /** @@ -423,9 +415,9 @@ class Portal { * @param config - The configuration for the local forwarder binding. */ TRANSPORT_ALWAYS_INLINE void bind(const BindConfig &config) { - forwarder_interface_.setContentStoreSize(config.csReserved()); + assert(io_module_); + io_module_->setContentStoreSize(config.csReserved()); served_namespaces_.push_back(config.prefix()); - setLocalRoutes(); } @@ -460,7 +452,7 @@ class Portal { */ TRANSPORT_ALWAYS_INLINE void sendContentObject( ContentObject &content_object) { - forwarder_interface_.send(content_object); + io_module_->send(content_object); } /** @@ -482,7 +474,7 @@ class Portal { * Disconnect the transport from the local forwarder. */ TRANSPORT_ALWAYS_INLINE void killConnection() { - forwarder_interface_.closeConnection(); + io_module_->closeConnection(); } /** @@ -496,6 +488,17 @@ class Portal { } } + /** + * Remove one pending interest. + */ + TRANSPORT_ALWAYS_INLINE void clearOne(const Name &name) { + if (!io_service_.stopped()) { + io_service_.dispatch(std::bind(&Portal::doClearOne, this, name)); + } else { + doClearOne(name); + } + } + /** * Get a reference to the io_service object. */ @@ -508,8 +511,8 @@ class Portal { */ TRANSPORT_ALWAYS_INLINE void registerRoute(Prefix &prefix) { served_namespaces_.push_back(prefix); - if (connector_.isConnected()) { - forwarder_interface_.registerRoute(prefix); + if (io_module_->isConnected()) { + io_module_->registerRoute(prefix); } } @@ -529,6 +532,23 @@ class Portal { pending_interest_hash_table_.clear(); } + /** + * Remove one pending interest. + */ + TRANSPORT_ALWAYS_INLINE void doClearOne(const Name &name) { + auto it = pending_interest_hash_table_.find(getHash(name)); + + if (it != pending_interest_hash_table_.end()) { + it->second->cancelTimer(); + + // Get interest packet from pending interest and do nothing with it. It + // will get destroyed as it goes out of scope. + auto _int = it->second->getInterest(); + + pending_interest_hash_table_.erase(it); + } + } + /** * Callback called by the underlying connector upon reception of a packet from * the local forwarder. @@ -536,30 +556,26 @@ class Portal { * @param packet_buffer - The bytes of the packet. */ TRANSPORT_ALWAYS_INLINE void processIncomingMessages( - Packet::MemBufPtr &&packet_buffer) { + Connector *c, utils::MemBuf &buffer, const std::error_code &ec) { bool is_stopped = io_service_.stopped(); if (TRANSPORT_EXPECT_FALSE(is_stopped)) { return; } - if (TRANSPORT_EXPECT_FALSE( - ForwarderInt::isControlMessage(packet_buffer->data()))) { - processControlMessage(std::move(packet_buffer)); + if (TRANSPORT_EXPECT_FALSE(io_module_->isControlMessage(buffer.data()))) { + processControlMessage(buffer); return; } - Packet::Format format = Packet::getFormatFromBuffer( - packet_buffer->data(), packet_buffer->length()); + // The buffer is a base class for an interest or a content object + Packet &packet_buffer = static_cast(buffer); + auto format = packet_buffer.getFormat(); if (TRANSPORT_EXPECT_TRUE(_is_tcp(format))) { - if (!Packet::isInterest(packet_buffer->data())) { - auto content_object = packet_pool_.getContentObject(); - content_object->replace(std::move(packet_buffer)); - processContentObject(std::move(content_object)); + if (is_consumer_) { + processContentObject(static_cast(packet_buffer)); } else { - auto interest = packet_pool_.getInterest(); - interest->replace(std::move(packet_buffer)); - processInterest(std::move(interest)); + processInterest(static_cast(packet_buffer)); } } else { TRANSPORT_LOGE("Received not supported packet. Ignoring it."); @@ -573,16 +589,16 @@ class Portal { */ TRANSPORT_ALWAYS_INLINE void setLocalRoutes() { for (auto &prefix : served_namespaces_) { - if (connector_.isConnected()) { - forwarder_interface_.registerRoute(prefix); + if (io_module_->isConnected()) { + io_module_->registerRoute(prefix); } } } - TRANSPORT_ALWAYS_INLINE void processInterest(Interest::Ptr &&interest) { + TRANSPORT_ALWAYS_INLINE void processInterest(Interest &interest) { // Interest for a producer if (TRANSPORT_EXPECT_TRUE(producer_callback_ != nullptr)) { - producer_callback_->onInterest(std::move(interest)); + producer_callback_->onInterest(interest); } } @@ -595,24 +611,27 @@ class Portal { * @param content_object - The data packet */ TRANSPORT_ALWAYS_INLINE void processContentObject( - ContentObject::Ptr &&content_object) { - uint32_t hash = content_object->getName().getHash32() + - content_object->getName().getSuffix(); + ContentObject &content_object) { + TRANSPORT_LOGD("processContentObject %s", + content_object.getName().toString().c_str()); + uint32_t hash = getHash(content_object.getName()); auto it = pending_interest_hash_table_.find(hash); if (it != pending_interest_hash_table_.end()) { + TRANSPORT_LOGD("Found pending interest."); + PendingInterest::Ptr interest_ptr = std::move(it->second); pending_interest_hash_table_.erase(it); interest_ptr->cancelTimer(); auto _int = interest_ptr->getInterest(); if (interest_ptr->getOnDataCallback() != UNSET_CALLBACK) { - interest_ptr->on_content_object_callback_(std::move(_int), - std::move(content_object)); + interest_ptr->on_content_object_callback_(*_int, content_object); } else if (consumer_callback_) { - consumer_callback_->onContentObject(std::move(_int), - std::move(content_object)); + consumer_callback_->onContentObject(*_int, content_object); } + } else { + TRANSPORT_LOGD("No interest pending for received content object."); } } @@ -622,12 +641,13 @@ class Portal { * them. */ TRANSPORT_ALWAYS_INLINE void processControlMessage( - Packet::MemBufPtr &&packet_buffer) { - forwarder_interface_.processControlMessageReply(std::move(packet_buffer)); + utils::MemBuf &packet_buffer) { + io_module_->processControlMessageReply(packet_buffer); } private: portal_details::HandlerMemory async_callback_memory_; + std::unique_ptr io_module_; asio::io_service &io_service_; asio::io_service internal_io_service_; @@ -641,8 +661,19 @@ class Portal { ConsumerCallback *consumer_callback_; ProducerCallback *producer_callback_; - typename ForwarderInt::ConnectorType connector_; - ForwarderInt forwarder_interface_; + bool is_consumer_; + + private: + static std::string defaultIoModule(); + static void parseIoModuleConfiguration(const libconfig::Setting &io_config, + std::error_code &ec); + static void getModuleConfiguration( + interface::global_config::ConfigurationObject &conf, std::error_code &ec); + static void setModuleConfiguration( + const interface::global_config::ConfigurationObject &conf, + std::error_code &ec); + static interface::global_config::IoModuleConfiguration conf_; + static std::string io_module_path_; }; } // namespace core diff --git a/libtransport/src/core/prefix.cc b/libtransport/src/core/prefix.cc index eea4aeb8b..1e2b2ed9d 100644 --- a/libtransport/src/core/prefix.cc +++ b/libtransport/src/core/prefix.cc @@ -25,12 +25,12 @@ extern "C" { #include #endif +#include + #include #include #include -#include - namespace transport { namespace core { @@ -99,7 +99,7 @@ void Prefix::buildPrefix(std::string &prefix, uint16_t prefix_length, ip_prefix_.family = family; } -std::unique_ptr Prefix::toSockaddr() { +std::unique_ptr Prefix::toSockaddr() const { Sockaddr *ret = nullptr; switch (ip_prefix_.family) { @@ -120,14 +120,14 @@ std::unique_ptr Prefix::toSockaddr() { return std::unique_ptr(ret); } -uint16_t Prefix::getPrefixLength() { return ip_prefix_.len; } +uint16_t Prefix::getPrefixLength() const { return ip_prefix_.len; } Prefix &Prefix::setPrefixLength(uint16_t prefix_length) { ip_prefix_.len = prefix_length; return *this; } -int Prefix::getAddressFamily() { return ip_prefix_.family; } +int Prefix::getAddressFamily() const { return ip_prefix_.family; } Prefix &Prefix::setAddressFamily(int address_family) { ip_prefix_.family = address_family; @@ -226,7 +226,7 @@ Name Prefix::getRandomName() const { ip_prefix_.len; size_t size = (size_t)ceil((float)addr_len / 8.0); - uint8_t *buffer = (uint8_t *) malloc(sizeof(uint8_t) * size); + uint8_t *buffer = (uint8_t *)malloc(sizeof(uint8_t) * size); RAND_bytes(buffer, size); @@ -332,7 +332,7 @@ bool Prefix::checkPrefixLengthAndAddressFamily(uint16_t prefix_length, return true; } -ip_prefix_t &Prefix::toIpPrefixStruct() { return ip_prefix_; } +const ip_prefix_t &Prefix::toIpPrefixStruct() const { return ip_prefix_; } } // namespace core diff --git a/libtransport/src/core/rs.cc b/libtransport/src/core/rs.cc new file mode 100644 index 000000000..44b5852e5 --- /dev/null +++ b/libtransport/src/core/rs.cc @@ -0,0 +1,365 @@ + +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include +#include + +#include + +namespace transport { +namespace core { +namespace fec { + +BlockCode::BlockCode(uint32_t k, uint32_t n, struct fec_parms *code) + : Packets(), + k_(k), + n_(n), + code_(code), + max_buffer_size_(0), + current_block_size_(0), + to_decode_(false) { + sorted_index_.reserve(n); +} + +bool BlockCode::addRepairSymbol(const fec::buffer &packet, uint32_t i) { + // Get index + to_decode_ = true; + TRANSPORT_LOGD("adding symbol of size %zu", packet->length()); + return addSymbol(packet, i, packet->length() - sizeof(fec_header)); +} + +bool BlockCode::addSourceSymbol(const fec::buffer &packet, uint32_t i) { + return addSymbol(packet, i, packet->length()); +} + +bool BlockCode::addSymbol(const fec::buffer &packet, uint32_t i, + std::size_t size) { + if (size > max_buffer_size_) { + max_buffer_size_ = size; + } + + operator[](current_block_size_++) = std::make_pair(i, packet); + + if (current_block_size_ >= k_) { + if (to_decode_) { + decode(); + } else { + encode(); + } + + clear(); + return false; + } + + return true; +} + +void BlockCode::encode() { + gf *data[n_]; + std::uint16_t old_values[k_]; + uint32_t base = operator[](0).first; + + // Set packet length in first 2 bytes + for (uint32_t i = 0; i < k_; i++) { + auto &packet = operator[](i).second; + + TRANSPORT_LOGD("Current buffer size: %zu", packet->length()); + + auto ret = packet->ensureCapacityAndFillUnused(max_buffer_size_, 0); + if (TRANSPORT_EXPECT_FALSE(ret == false)) { + throw errors::RuntimeException( + "Provided packet is not suitable to be used as FEC source packet. " + "Aborting."); + } + + // Buffers should hold 2 bytes before the starting pointer, in order to be + // able to set the length for the encoding operation + packet->prepend(2); + uint16_t *length = reinterpret_cast(packet->writableData()); + + old_values[i] = *length; + *length = htons(packet->length() - LEN_SIZE_BYTES); + + data[i] = packet->writableData(); + } + + // Finish to fill source block with the buffers to hold the repair symbols + for (uint32_t i = k_; i < n_; i++) { + // For the moment we get a packet from the pool here.. later we'll need to + // require a packet from the caller with a callback. + auto packet = PacketManager<>::getInstance().getMemBuf(); + packet->append(max_buffer_size_ + sizeof(fec_header) + LEN_SIZE_BYTES); + fec_header *fh = reinterpret_cast(packet->writableData()); + + fh->setSeqNumberBase(base); + fh->setNFecSymbols(n_ - k_); + fh->setEncodedSymbolId(i); + fh->setSourceBlockLen(n_); + + packet->trimStart(sizeof(fec_header)); + + data[i] = packet->writableData(); + operator[](i) = std::make_pair(i, std::move(packet)); + } + + // Generate repair symbols and put them in corresponding buffers + TRANSPORT_LOGD("Calling encode with max_buffer_size_ = %zu", + max_buffer_size_); + for (uint32_t i = k_; i < n_; i++) { + fec_encode(code_, data, data[i], i, max_buffer_size_ + LEN_SIZE_BYTES); + } + + // Restore original content of buffer space used to store the length + for (uint32_t i = 0; i < k_; i++) { + auto &packet = operator[](i).second; + uint16_t *length = reinterpret_cast(packet->writableData()); + *length = old_values[i]; + packet->trimStart(2); + } + + // Re-include header in repair packets + for (uint32_t i = k_; i < n_; i++) { + auto &packet = operator[](i).second; + TRANSPORT_LOGD("Produced repair symbol of size = %zu", packet->length()); + packet->prepend(sizeof(fec_header)); + } +} + +void BlockCode::decode() { + gf *data[k_]; + uint32_t index[k_]; + + for (uint32_t i = 0; i < k_; i++) { + auto &packet = operator[](i).second; + index[i] = operator[](i).first; + sorted_index_[i] = index[i]; + + if (index[i] < k_) { + TRANSPORT_LOGD("DECODE SOURCE - index %u - Current buffer size: %zu", + index[i], packet->length()); + // This is a source packet. We need to prepend the length and fill + // additional space to 0 + + // Buffers should hold 2 bytes before the starting pointer, in order to be + // able to set the length for the encoding operation + packet->prepend(LEN_SIZE_BYTES); + packet->ensureCapacityAndFillUnused(max_buffer_size_, 0); + uint16_t *length = reinterpret_cast(packet->writableData()); + + *length = htons(packet->length() - LEN_SIZE_BYTES); + } else { + TRANSPORT_LOGD("DECODE SYMBOL - index %u - Current buffer size: %zu", + index[i], packet->length()); + packet->trimStart(sizeof(fec_header)); + } + + data[i] = packet->writableData(); + } + + // We decode the source block + TRANSPORT_LOGD("Calling decode with max_buffer_size_ = %zu", + max_buffer_size_); + fec_decode(code_, data, reinterpret_cast(index), max_buffer_size_); + + // Find the index in the block for recovered packets + for (uint32_t i = 0; i < k_; i++) { + if (index[i] != i) { + for (uint32_t j = 0; j < k_; j++) + if (sorted_index_[j] == uint32_t(index[i])) { + sorted_index_[j] = i; + } + } + } + + // Reorder block by index with in-place sorting + for (uint32_t i = 0; i < k_; i++) { + for (uint32_t j = sorted_index_[i]; j != i; j = sorted_index_[i]) { + std::swap(sorted_index_[j], sorted_index_[i]); + std::swap(operator[](j), operator[](i)); + } + } + + // Adjust length according to the one written in the source packet + for (uint32_t i = 0; i < k_; i++) { + auto &packet = operator[](i).second; + uint16_t *length = reinterpret_cast(packet->writableData()); + packet->trimStart(2); + packet->setLength(ntohs(*length)); + } +} + +void BlockCode::clear() { + current_block_size_ = 0; + max_buffer_size_ = 0; + sorted_index_.clear(); + to_decode_ = false; +} + +void rs::MatrixDeleter::operator()(struct fec_parms *params) { + fec_free(params); +} + +rs::Codes rs::createCodes() { + Codes ret; + + ret.emplace(std::make_pair(1, 3), Matrix(fec_new(1, 3), MatrixDeleter())); + ret.emplace(std::make_pair(6, 10), Matrix(fec_new(6, 10), MatrixDeleter())); + ret.emplace(std::make_pair(8, 32), Matrix(fec_new(8, 32), MatrixDeleter())); + ret.emplace(std::make_pair(10, 30), Matrix(fec_new(10, 30), MatrixDeleter())); + ret.emplace(std::make_pair(16, 24), Matrix(fec_new(16, 24), MatrixDeleter())); + ret.emplace(std::make_pair(10, 40), Matrix(fec_new(10, 40), MatrixDeleter())); + ret.emplace(std::make_pair(10, 60), Matrix(fec_new(10, 60), MatrixDeleter())); + ret.emplace(std::make_pair(10, 90), Matrix(fec_new(10, 90), MatrixDeleter())); + + return ret; +} + +rs::Codes rs::codes_ = createCodes(); + +rs::rs(uint32_t k, uint32_t n) : k_(k), n_(n) {} + +void rs::setFECCallback(const PacketsReady &callback) { + fec_callback_ = callback; +} + +encoder::encoder(uint32_t k, uint32_t n) + : rs(k, n), + current_code_(codes_[std::make_pair(k, n)].get()), + source_block_(k_, n_, current_code_) {} + +void encoder::consume(const fec::buffer &packet, uint32_t index) { + if (!source_block_.addSourceSymbol(packet, index)) { + std::vector repair_packets; + for (uint32_t i = k_; i < n_; i++) { + repair_packets.emplace_back(std::move(source_block_[i].second)); + } + fec_callback_(repair_packets); + } +} + +decoder::decoder(uint32_t k, uint32_t n) : rs(k, n) {} + +void decoder::recoverPackets(SourceBlocks::iterator &src_block_it) { + TRANSPORT_LOGD("recoverPackets for %u", k_); + auto &src_block = src_block_it->second; + std::vector source_packets(k_); + for (uint32_t i = 0; i < src_block.getK(); i++) { + source_packets[i] = std::move(src_block[i].second); + } + + fec_callback_(source_packets); + processed_source_blocks_.emplace(src_block_it->first); + + auto it = parked_packets_.find(src_block_it->first); + if (it != parked_packets_.end()) { + parked_packets_.erase(it); + } + + src_blocks_.erase(src_block_it); +} + +void decoder::consume(const fec::buffer &packet, uint32_t index) { + // Normalize index + auto i = index % n_; + + // Get base + uint32_t base = index - i; + + TRANSPORT_LOGD( + "Decoder consume called for source symbol. BASE = %u, index = %u and i = " + "%u", + base, index, i); + + // check if a source block already exist for this symbol. If it does not + // exist, we lazily park this packet until we receive a repair symbol for the + // same block. This is done for 2 reason: + // 1) If we receive all the source packets of a block, we do not need to + // recover anything. + // 2) Sender may change n and k at any moment, so we construct the source + // block based on the (n, k) values written in the fec header. This is + // actually not used right now, since we use fixed value of n and k passed + // at construction time, but it paves the ground for a more dynamic + // protocol that may come in the future. + auto it = src_blocks_.find(base); + if (it != src_blocks_.end()) { + auto ret = it->second.addSourceSymbol(packet, i); + if (!ret) { + recoverPackets(it); + } + } else { + TRANSPORT_LOGD("Adding to parked source packets"); + auto ret = parked_packets_.emplace( + base, std::vector >()); + ret.first->second.emplace_back(packet, i); + } +} + +void decoder::consume(const fec::buffer &packet) { + // Repair symbol! Get index and base source block. + fec_header *h = reinterpret_cast(packet->writableData()); + auto i = h->getEncodedSymbolId(); + auto base = h->getSeqNumberBase(); + auto n = h->getSourceBlockLen(); + auto k = n - h->getNFecSymbols(); + + TRANSPORT_LOGD( + "Decoder consume called for repair symbol. BASE = %u, index = %u and i = " + "%u", + base, base + i, i); + + // check if a source block already exist for this symbol + auto it = src_blocks_.find(base); + if (it == src_blocks_.end()) { + // Create new source block + auto code_it = codes_.find(std::make_pair(k, n)); + if (code_it == codes_.end()) { + TRANSPORT_LOGE("Code for k = %u and n = %u does not exist.", k_, n_); + return; + } + + auto emplace_result = + src_blocks_.emplace(base, BlockCode(k, n, code_it->second.get())); + it = emplace_result.first; + + // Check in the parked packets and insert any packet that is part of this + // source block + + auto it2 = parked_packets_.find(base); + if (it2 != parked_packets_.end()) { + for (auto &packet_index : it2->second) { + auto ret = + it->second.addSourceSymbol(packet_index.first, packet_index.second); + if (!ret) { + recoverPackets(it); + // Finish to delete packets in same source block that were + // eventually not used + return; + } + } + } + } + + auto ret = it->second.addRepairSymbol(packet, i); + if (!ret) { + recoverPackets(it); + } +} + +} // namespace fec +} // namespace core +} // namespace transport diff --git a/libtransport/src/core/rs.h b/libtransport/src/core/rs.h new file mode 100644 index 000000000..d630bd233 --- /dev/null +++ b/libtransport/src/core/rs.h @@ -0,0 +1,338 @@ + +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include + +#include +#include +#include +#include +#include + +namespace transport { +namespace core { + +namespace fec { + +static const constexpr uint16_t MAX_SOURCE_BLOCK_SIZE = 128; + +using buffer = typename utils::MemBuf::Ptr; +/** + * We use a std::array in place of std::vector to avoid to allocate a new vector + * in the heap every time we build a new source block, which would be bad if + * the decoder has to allocate several source blocks for many concurrent bases. + * std::array allows to be constructed in place, saving the allocation at the + * price os knowing in advance its size. + */ +using Packets = std::array, MAX_SOURCE_BLOCK_SIZE>; + +/** + * FEC Header, prepended to symbol packets. + */ +struct fec_header { + /** + * The base source packet seq_number this FES symbol refers to + */ + uint32_t seq_number; + + /** + * The index of the symbol inside the source block, between k and n - 1 + */ + uint8_t encoded_symbol_id; + + /** + * Total length of source block (n) + */ + uint8_t source_block_len; + + /** + * Total number of symbols (n - k) + */ + uint8_t n_fec_symbols; + + /** + * Align header to 64 bits + */ + uint8_t padding; + + void setSeqNumberBase(uint32_t suffix) { seq_number = htonl(suffix); } + uint32_t getSeqNumberBase() { return ntohl(seq_number); } + void setEncodedSymbolId(uint8_t esi) { encoded_symbol_id = esi; } + uint8_t getEncodedSymbolId() { return encoded_symbol_id; } + void setSourceBlockLen(uint8_t k) { source_block_len = k; } + uint8_t getSourceBlockLen() { return source_block_len; } + void setNFecSymbols(uint8_t n_r) { n_fec_symbols = n_r; } + uint8_t getNFecSymbols() { return n_fec_symbols; } +}; + +/** + * This class models the source block itself. + */ +class BlockCode : public Packets { + /** + * For variable length packet we need to prepend to the padded payload the + * real length of the packet. This is *not* sent over the network. + */ + static constexpr std::size_t LEN_SIZE_BYTES = 2; + + public: + BlockCode(uint32_t k, uint32_t n, struct fec_parms *code); + + /** + * Add a repair symbol to the dource block. + */ + bool addRepairSymbol(const fec::buffer &packet, uint32_t i); + + /** + * Add a source symbol to the source block. + */ + bool addSourceSymbol(const fec::buffer &packet, uint32_t i); + + /** + * Get current length of source block. + */ + std::size_t length() { return current_block_size_; } + + /** + * Get N + */ + uint32_t getN() { return n_; } + + /** + * Get K + */ + uint32_t getK() { return k_; } + + /** + * Clear source block + */ + void clear(); + + private: + /** + * Add symbol to source block + **/ + bool addSymbol(const fec::buffer &packet, uint32_t i, std::size_t size); + + /** + * Starting from k source symbols, get the n - k repair symbols + */ + void encode(); + + /** + * Starting from k symbols (mixed repair and source), get k source symbols. + * NOTE: It does not make sense to retrieve the k source symbols using the + * very same k source symbols. With the current implementation that case can + * never happen. + */ + void decode(); + + private: + uint32_t k_; + uint32_t n_; + struct fec_parms *code_; + std::size_t max_buffer_size_; + std::size_t current_block_size_; + std::vector sorted_index_; + bool to_decode_; +}; + +/** + * This class contains common parameters between the fec encoder and decoder. + * In particular it contains: + * - The callback to be called when symbols are encoded / decoded + * - The reference to the static reed-solomon parameters, allocated at program + * startup + * - N and K. Ideally they are useful only for the encoder (the decoder can + * retrieve them from the FEC header). However right now we assume sender and + * receiver agreed on the parameters k and n to use. We will introduce a control + * message later to negotiate them, so that decoder cah dynamically change them + * during the download. + */ +class rs { + /** + * Deleter for static preallocated reed-solomon parameters. + */ + struct MatrixDeleter { + void operator()(struct fec_parms *params); + }; + + /** + * unique_ptr to reed-solomon parameters, with custom deleter to call fec_free + * at the end of the program + */ + using Matrix = std::unique_ptr; + + /** + * Key to retrieve static preallocated reed-solomon parameters. It is pair of + * k and n + */ + using Code = std::pair; + + /** + * Custom hash function for (k, n) pair. + */ + struct CodeHasher { + std::size_t operator()(const transport::core::fec::rs::Code &code) const { + uint64_t ret = uint64_t(code.first) << 32 | uint64_t(code.second); + return std::hash{}(ret); + } + }; + + protected: + /** + * Callback to be called after the encode or the decode operations. In the + * former case it will contain the symbols, while in the latter the sources. + */ + using PacketsReady = std::function &)>; + + /** + * The sequence number base. + */ + using SNBase = std::uint32_t; + + /** + * The map of source blocks, used at the decoder side. For the encoding + * operation we can use one source block only, since packet are produced in + * order. + */ + using SourceBlocks = std::unordered_map; + + /** + * Map (k, n) -> reed-solomon parameter + */ + using Codes = std::unordered_map; + + public: + rs(uint32_t k, uint32_t n); + ~rs() = default; + + /** + * Set callback to call after packet encoding / decoding + */ + void setFECCallback(const PacketsReady &callback); + + virtual void clear() { processed_source_blocks_.clear(); } + + private: + /** + * Create reed-solomon codes at program startup. + */ + static Codes createCodes(); + + protected: + bool processed(SNBase seq_base) { + return processed_source_blocks_.find(seq_base) != + processed_source_blocks_.end(); + } + + std::uint32_t k_; + std::uint32_t n_; + PacketsReady fec_callback_; + + /** + * Keep track of processed source blocks + */ + std::unordered_set processed_source_blocks_; + + static Codes codes_; +}; + +/** + * The reed-solomon encoder. It is feeded with source symbols and it provide + * repair-symbols through the fec_callback_ + */ +class encoder : public rs { + public: + encoder(uint32_t k, uint32_t n); + /** + * Always consume source symbols. + */ + void consume(const fec::buffer &packet, uint32_t index); + + void clear() override { + rs::clear(); + source_block_.clear(); + } + + private: + struct fec_parms *current_code_; + /** + * The source block. As soon as it is filled with k source symbols, the + * encoder calls the callback fec_callback_ and the resets the block 0, ready + * to accept another batch of k source symbols. + */ + BlockCode source_block_; +}; + +/** + * The reed-solomon encoder. It is feeded with source/repair symbols and it + * provides the original source symbols through the fec_callback_ + */ +class decoder : public rs { + public: + decoder(uint32_t k, uint32_t n); + + /** + * Consume source symbol + */ + void consume(const fec::buffer &packet, uint32_t i); + + /** + * Consume repair symbol + */ + void consume(const fec::buffer &packet); + + /** + * Clear decoder to reuse + */ + void clear() override { + rs::clear(); + src_blocks_.clear(); + parked_packets_.clear(); + } + + private: + void recoverPackets(SourceBlocks::iterator &src_block_it); + + private: + /** + * Map of source blocks. We use a map because we may receive symbols belonging + * to diffreent source blocks at the same time, so we need to be able to + * decode many source symbols at the same time. + */ + SourceBlocks src_blocks_; + + /** + * Unordered Map of source symbols for which we did not receive any repair + * symbol in the same source block. Notably this happens when: + * + * - We receive the source symbols first and the repair symbols after + * - We received only source symbols for a given block. In that case it does + * not make any sense to build the source block, since we received all the + * source packet of the block. + */ + std::unordered_map>> + parked_packets_; +}; + +} // namespace fec + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/core/tcp_socket_connector.h b/libtransport/src/core/tcp_socket_connector.h index c57123e9f..9dbd250d1 100644 --- a/libtransport/src/core/tcp_socket_connector.h +++ b/libtransport/src/core/tcp_socket_connector.h @@ -15,12 +15,11 @@ #pragma once +#include #include #include #include -#include - #include #include #include diff --git a/libtransport/src/http/client_connection.cc b/libtransport/src/http/client_connection.cc index 7a3a636fe..a24a821e7 100644 --- a/libtransport/src/http/client_connection.cc +++ b/libtransport/src/http/client_connection.cc @@ -43,12 +43,6 @@ class HTTPClientConnection::Implementation read_buffer_(nullptr), response_(std::make_shared()), timer_(nullptr) { - consumer_.setSocketOption( - ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY, - (ConsumerContentObjectVerificationCallback)std::bind( - &Implementation::verifyData, this, std::placeholders::_1, - std::placeholders::_2)); - consumer_.setSocketOption(ConsumerCallbacksOptions::READ_CALLBACK, this); consumer_.connect(); @@ -124,10 +118,10 @@ class HTTPClientConnection::Implementation return *http_client_; } - HTTPClientConnection &setCertificate(const std::string &cert_path) { - if (consumer_.setSocketOption(GeneralTransportOptions::CERTIFICATE, - cert_path) == SOCKET_OPTION_NOT_SET) { - throw errors::RuntimeException("Error setting the certificate."); + HTTPClientConnection &setVerifier(std::shared_ptr verifier) { + if (consumer_.setSocketOption(GeneralTransportOptions::VERIFIER, + verifier) == SOCKET_OPTION_NOT_SET) { + throw errors::RuntimeException("Error setting the verifier."); } return *http_client_; @@ -177,17 +171,6 @@ class HTTPClientConnection::Implementation consumer_.stop(); } - bool verifyData(interface::ConsumerSocket &c, - const core::ContentObject &contentObject) { - if (contentObject.getPayloadType() == PayloadType::CONTENT_OBJECT) { - TRANSPORT_LOGI("VERIFY CONTENT\n"); - } else if (contentObject.getPayloadType() == PayloadType::MANIFEST) { - TRANSPORT_LOGI("VERIFY MANIFEST\n"); - } - - return true; - } - void processLeavingInterest(interface::ConsumerSocket &c, const core::Interest &interest) { if (interest.payloadSize() == 0) { @@ -307,9 +290,9 @@ HTTPClientConnection &HTTPClientConnection::setTimeout( return implementation_->setTimeout(timeout); } -HTTPClientConnection &HTTPClientConnection::setCertificate( - const std::string &cert_path) { - return implementation_->setCertificate(cert_path); +HTTPClientConnection &HTTPClientConnection::setVerifier( + std::shared_ptr verifier) { + return implementation_->setVerifier(verifier); } } // namespace http diff --git a/libtransport/src/implementation/CMakeLists.txt b/libtransport/src/implementation/CMakeLists.txt index 5423a7697..392c99e15 100644 --- a/libtransport/src/implementation/CMakeLists.txt +++ b/libtransport/src/implementation/CMakeLists.txt @@ -13,13 +13,8 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) -list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_producer.cc -) - list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/socket.h - ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_producer.h ${CMAKE_CURRENT_SOURCE_DIR}/socket_producer.h ${CMAKE_CURRENT_SOURCE_DIR}/socket_consumer.h ) @@ -27,15 +22,16 @@ list(APPEND HEADER_FILES if (${OPENSSL_VERSION} VERSION_EQUAL "1.1.1a" OR ${OPENSSL_VERSION} VERSION_GREATER "1.1.1a") list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_producer.cc - ${CMAKE_CURRENT_SOURCE_DIR}/tls_rtc_socket_producer.cc + # ${CMAKE_CURRENT_SOURCE_DIR}/tls_rtc_socket_producer.cc ${CMAKE_CURRENT_SOURCE_DIR}/p2psecure_socket_producer.cc ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_consumer.cc ${CMAKE_CURRENT_SOURCE_DIR}/p2psecure_socket_consumer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/socket.cc ) list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_producer.h - ${CMAKE_CURRENT_SOURCE_DIR}/tls_rtc_socket_producer.h + # ${CMAKE_CURRENT_SOURCE_DIR}/tls_rtc_socket_producer.h ${CMAKE_CURRENT_SOURCE_DIR}/p2psecure_socket_producer.h ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_consumer.h ${CMAKE_CURRENT_SOURCE_DIR}/p2psecure_socket_consumer.h diff --git a/libtransport/src/implementation/p2psecure_socket_consumer.cc b/libtransport/src/implementation/p2psecure_socket_consumer.cc index 9b79850d6..8c7c175b2 100644 --- a/libtransport/src/implementation/p2psecure_socket_consumer.cc +++ b/libtransport/src/implementation/p2psecure_socket_consumer.cc @@ -15,7 +15,6 @@ #include #include - #include #include #include @@ -175,7 +174,6 @@ P2PSecureConsumerSocket::P2PSecureConsumerSocket( : ConsumerSocket(consumer, handshake_protocol), name_(), tls_consumer_(nullptr), - buf_pool_(), decrypted_content_(), payload_(), head_(), diff --git a/libtransport/src/implementation/p2psecure_socket_consumer.h b/libtransport/src/implementation/p2psecure_socket_consumer.h index d4c3b26c2..a35a50352 100644 --- a/libtransport/src/implementation/p2psecure_socket_consumer.h +++ b/libtransport/src/implementation/p2psecure_socket_consumer.h @@ -16,7 +16,6 @@ #pragma once #include - #include #include #include @@ -75,7 +74,6 @@ class P2PSecureConsumerSocket : public ConsumerSocket, BIO_METHOD *bio_meth_; /* Chain of MemBuf to be used as a temporary buffer to pass descypted data * from the underlying layer to the application */ - utils::ObjectPool buf_pool_; std::unique_ptr decrypted_content_; /* Chain of MemBuf holding the payload to be written into interest or data */ std::unique_ptr payload_; diff --git a/libtransport/src/implementation/p2psecure_socket_producer.cc b/libtransport/src/implementation/p2psecure_socket_producer.cc index 15c7d25cd..6dff2ba08 100644 --- a/libtransport/src/implementation/p2psecure_socket_producer.cc +++ b/libtransport/src/implementation/p2psecure_socket_producer.cc @@ -14,13 +14,11 @@ */ #include - #include -#include +// #include #include #include #include - #include #include #include @@ -34,7 +32,8 @@ namespace implementation { P2PSecureProducerSocket::P2PSecureProducerSocket( interface::ProducerSocket *producer_socket) - : ProducerSocket(producer_socket), + : ProducerSocket(producer_socket, + ProductionProtocolAlgorithms::BYTE_STREAM), mtx_(), cv_(), map_producers(), @@ -42,8 +41,9 @@ P2PSecureProducerSocket::P2PSecureProducerSocket( P2PSecureProducerSocket::P2PSecureProducerSocket( interface::ProducerSocket *producer_socket, bool rtc, - const std::shared_ptr &identity) - : ProducerSocket(producer_socket), + const std::shared_ptr &identity) + : ProducerSocket(producer_socket, + ProductionProtocolAlgorithms::BYTE_STREAM), rtc_(rtc), mtx_(), cv_(), @@ -51,9 +51,9 @@ P2PSecureProducerSocket::P2PSecureProducerSocket( list_producers() { /* Setup SSL context (identity and parameter to use TLS 1.3) */ der_cert_ = parcKeyStore_GetDEREncodedCertificate( - (identity->getSigner()->getKeyStore())); + (identity->getSigner()->getParcKeyStore())); der_prk_ = parcKeyStore_GetDEREncodedPrivateKey( - (identity->getSigner()->getKeyStore())); + (identity->getSigner()->getParcKeyStore())); int cert_size = parcBuffer_Limit(der_cert_); int prk_size = parcBuffer_Limit(der_prk_); @@ -88,15 +88,20 @@ void P2PSecureProducerSocket::initSessionSocket( producer->setSocketOption(MAKE_MANIFEST, this->making_manifest_); producer->setSocketOption(DATA_PACKET_SIZE, (uint32_t)(this->data_packet_size_)); - producer->output_buffer_.setLimit(this->output_buffer_.getLimit()); + uint32_t output_buffer_size = 0; + this->getSocketOption(GeneralTransportOptions::OUTPUT_BUFFER_SIZE, + output_buffer_size); + producer->setSocketOption(GeneralTransportOptions::OUTPUT_BUFFER_SIZE, + output_buffer_size); if (!rtc_) { producer->setInterface(new interface::TLSProducerSocket(producer.get())); } else { - TLSRTCProducerSocket *rtc_producer = - dynamic_cast(producer.get()); - rtc_producer->setInterface( - new interface::TLSRTCProducerSocket(rtc_producer)); + // TODO + // TLSRTCProducerSocket *rtc_producer = + // dynamic_cast(producer.get()); + // rtc_producer->setInterface( + // new interface::TLSRTCProducerSocket(rtc_producer)); } } @@ -114,8 +119,9 @@ void P2PSecureProducerSocket::onInterestCallback(interface::ProducerSocket &p, tls_producer = std::make_unique(nullptr, this, interest.getName()); } else { - tls_producer = std::make_unique(nullptr, this, - interest.getName()); + // TODO + // tls_producer = std::make_unique(nullptr, this, + // interest.getName()); } initSessionSocket(tls_producer); @@ -129,15 +135,19 @@ void P2PSecureProducerSocket::onInterestCallback(interface::ProducerSocket &p, tls_producer_ptr->onInterest(*tls_producer_ptr, interest); tls_producer_ptr->async_accept(); } else { - TLSRTCProducerSocket *rtc_producer_ptr = - dynamic_cast(tls_producer_ptr); - rtc_producer_ptr->onInterest(*rtc_producer_ptr, interest); - rtc_producer_ptr->async_accept(); + // TODO + // TLSRTCProducerSocket *rtc_producer_ptr = + // dynamic_cast(tls_producer_ptr); + // rtc_producer_ptr->onInterest(*rtc_producer_ptr, interest); + // rtc_producer_ptr->async_accept(); } } -void P2PSecureProducerSocket::produce(const uint8_t *buffer, - size_t buffer_size) { +uint32_t P2PSecureProducerSocket::produceDatagram( + const Name &content_name, std::unique_ptr &&buffer) { + // TODO + throw errors::NotImplementedException(); + if (!rtc_) { throw errors::RuntimeException( "RTC must be the transport protocol to start the production of current " @@ -148,16 +158,20 @@ void P2PSecureProducerSocket::produce(const uint8_t *buffer, if (list_producers.empty()) cv_.wait(lck); - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) { - TLSRTCProducerSocket *rtc_producer = - dynamic_cast(it->get()); - rtc_producer->produce(utils::MemBuf::copyBuffer(buffer, buffer_size)); - } + // TODO + // for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) + // { + // TLSRTCProducerSocket *rtc_producer = + // dynamic_cast(it->get()); + // rtc_producer->produce(utils::MemBuf::copyBuffer(buffer, buffer_size)); + // } + + return 0; } -uint32_t P2PSecureProducerSocket::produce( - Name content_name, std::unique_ptr &&buffer, bool is_last, - uint32_t start_offset) { +uint32_t P2PSecureProducerSocket::produceStream( + const Name &content_name, std::unique_ptr &&buffer, + bool is_last, uint32_t start_offset) { if (rtc_) { throw errors::RuntimeException( "RTC transport protocol is not compatible with the production of " @@ -170,16 +184,17 @@ uint32_t P2PSecureProducerSocket::produce( if (list_producers.empty()) cv_.wait(lck); for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - segments += - (*it)->produce(content_name, buffer->clone(), is_last, start_offset); + segments += (*it)->produceStream(content_name, buffer->clone(), is_last, + start_offset); return segments; } -uint32_t P2PSecureProducerSocket::produce(Name content_name, - const uint8_t *buffer, - size_t buffer_size, bool is_last, - uint32_t start_offset) { +uint32_t P2PSecureProducerSocket::produceStream(const Name &content_name, + const uint8_t *buffer, + size_t buffer_size, + bool is_last, + uint32_t start_offset) { if (rtc_) { throw errors::RuntimeException( "RTC transport protocol is not compatible with the production of " @@ -191,29 +206,31 @@ uint32_t P2PSecureProducerSocket::produce(Name content_name, if (list_producers.empty()) cv_.wait(lck); for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - segments += (*it)->produce(content_name, buffer, buffer_size, is_last, - start_offset); + segments += (*it)->produceStream(content_name, buffer, buffer_size, is_last, + start_offset); return segments; } -void P2PSecureProducerSocket::asyncProduce(const Name &content_name, - const uint8_t *buf, - size_t buffer_size, bool is_last, - uint32_t *start_offset) { - if (rtc_) { - throw errors::RuntimeException( - "RTC transport protocol is not compatible with the production of " - "current data. Aborting."); - } - - std::unique_lock lck(mtx_); - if (list_producers.empty()) cv_.wait(lck); - - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) { - (*it)->asyncProduce(content_name, buf, buffer_size, is_last, start_offset); - } -} +// void P2PSecureProducerSocket::asyncProduce(const Name &content_name, +// const uint8_t *buf, +// size_t buffer_size, bool is_last, +// uint32_t *start_offset) { +// if (rtc_) { +// throw errors::RuntimeException( +// "RTC transport protocol is not compatible with the production of " +// "current data. Aborting."); +// } + +// std::unique_lock lck(mtx_); +// if (list_producers.empty()) cv_.wait(lck); + +// for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) +// { +// (*it)->asyncProduce(content_name, buf, buffer_size, is_last, +// start_offset); +// } +// } void P2PSecureProducerSocket::asyncProduce( Name content_name, std::unique_ptr &&buffer, bool is_last, @@ -269,7 +286,7 @@ int P2PSecureProducerSocket::setSocketOption( int P2PSecureProducerSocket::setSocketOption( int socket_option_key, - const std::shared_ptr &socket_option_value) { + const std::shared_ptr &socket_option_value) { if (!list_producers.empty()) for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) (*it)->setSocketOption(socket_option_key, socket_option_value); @@ -322,16 +339,6 @@ int P2PSecureProducerSocket::setSocketOption(int socket_option_key, socket_option_value); } -int P2PSecureProducerSocket::setSocketOption( - int socket_option_key, std::list socket_option_value) { - if (!list_producers.empty()) - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - (*it)->setSocketOption(socket_option_key, socket_option_value); - - return ProducerSocket::setSocketOption(socket_option_key, - socket_option_value); -} - int P2PSecureProducerSocket::setSocketOption( int socket_option_key, ProducerContentObjectCallback socket_option_value) { if (!list_producers.empty()) @@ -361,17 +368,7 @@ int P2PSecureProducerSocket::setSocketOption( } int P2PSecureProducerSocket::setSocketOption( - int socket_option_key, utils::CryptoHashType socket_option_value) { - if (!list_producers.empty()) - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - (*it)->setSocketOption(socket_option_key, socket_option_value); - - return ProducerSocket::setSocketOption(socket_option_key, - socket_option_value); -} - -int P2PSecureProducerSocket::setSocketOption( - int socket_option_key, utils::CryptoSuite socket_option_value) { + int socket_option_key, auth::CryptoHashType socket_option_value) { if (!list_producers.empty()) for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) (*it)->setSocketOption(socket_option_key, socket_option_value); diff --git a/libtransport/src/implementation/p2psecure_socket_producer.h b/libtransport/src/implementation/p2psecure_socket_producer.h index bfc9fc2c1..b7c3d1958 100644 --- a/libtransport/src/implementation/p2psecure_socket_producer.h +++ b/libtransport/src/implementation/p2psecure_socket_producer.h @@ -15,15 +15,14 @@ #pragma once -#include -#include - +#include +#include #include -#include +// #include #include +#include #include -#include #include #include #include @@ -33,39 +32,40 @@ namespace implementation { class P2PSecureProducerSocket : public ProducerSocket { friend class TLSProducerSocket; - friend class TLSRTCProducerSocket; + // TODO + // friend class TLSRTCProducerSocket; public: explicit P2PSecureProducerSocket(interface::ProducerSocket *producer_socket); explicit P2PSecureProducerSocket( interface::ProducerSocket *producer_socket, bool rtc, - const std::shared_ptr &identity); + const std::shared_ptr &identity); ~P2PSecureProducerSocket(); - void produce(const uint8_t *buffer, size_t buffer_size) override; + uint32_t produceDatagram(const Name &content_name, + std::unique_ptr &&buffer) override; - uint32_t produce(Name content_name, const uint8_t *buffer, size_t buffer_size, - bool is_last = true, uint32_t start_offset = 0) override; + uint32_t produceStream(const Name &content_name, const uint8_t *buffer, + size_t buffer_size, bool is_last = true, + uint32_t start_offset = 0) override; - uint32_t produce(Name content_name, std::unique_ptr &&buffer, - bool is_last = true, uint32_t start_offset = 0) override; + uint32_t produceStream(const Name &content_name, + std::unique_ptr &&buffer, + bool is_last = true, + uint32_t start_offset = 0) override; void asyncProduce(Name content_name, std::unique_ptr &&buffer, bool is_last, uint32_t offset, uint32_t **last_segment = nullptr) override; - void asyncProduce(const Name &suffix, const uint8_t *buf, size_t buffer_size, - bool is_last = true, - uint32_t *start_offset = nullptr) override; - int setSocketOption(int socket_option_key, ProducerInterestCallback socket_option_value) override; int setSocketOption( int socket_option_key, - const std::shared_ptr &socket_option_value) override; + const std::shared_ptr &socket_option_value) override; int setSocketOption(int socket_option_key, uint32_t socket_option_value) override; @@ -75,9 +75,6 @@ class P2PSecureProducerSocket : public ProducerSocket { int setSocketOption(int socket_option_key, Name *socket_option_value) override; - int setSocketOption(int socket_option_key, - std::list socket_option_value) override; - int setSocketOption( int socket_option_key, ProducerContentObjectCallback socket_option_value) override; @@ -86,16 +83,13 @@ class P2PSecureProducerSocket : public ProducerSocket { ProducerContentCallback socket_option_value) override; int setSocketOption(int socket_option_key, - utils::CryptoHashType socket_option_value) override; - - int setSocketOption(int socket_option_key, - utils::CryptoSuite socket_option_value) override; + auth::CryptoHashType socket_option_value) override; int setSocketOption(int socket_option_key, const std::string &socket_option_value) override; using ProducerSocket::getSocketOption; - using ProducerSocket::onInterest; + // using ProducerSocket::onInterest; protected: /* Callback invoked once an interest has been received and its payload diff --git a/libtransport/src/implementation/socket.cc b/libtransport/src/implementation/socket.cc new file mode 100644 index 000000000..2e21f2bc3 --- /dev/null +++ b/libtransport/src/implementation/socket.cc @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include + +namespace transport { +namespace implementation { + +Socket::Socket(std::shared_ptr &&portal) + : portal_(std::move(portal)), is_async_(false) {} + +} // namespace implementation +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/implementation/socket.h b/libtransport/src/implementation/socket.h index 2e51f3027..cf22c03e1 100644 --- a/libtransport/src/implementation/socket.h +++ b/libtransport/src/implementation/socket.h @@ -15,13 +15,12 @@ #pragma once +#include #include #include #include #include -#include - #define SOCKET_OPTION_GET 0 #define SOCKET_OPTION_NOT_GET 1 #define SOCKET_OPTION_SET 2 @@ -32,56 +31,23 @@ namespace transport { namespace implementation { // Forward Declarations -template class Socket; -// Define the portal and its connector, depending on the compilation options -// passed by the build tool. -using HicnForwarderPortal = core::HicnForwarderPortal; - -#ifdef __linux__ -#ifndef __ANDROID__ -using RawSocketPortal = core::RawSocketPortal; -#endif -#endif - -#ifdef __vpp__ -using VPPForwarderPortal = core::VPPForwarderPortal; -using BaseSocket = Socket; -using BasePortal = VPPForwarderPortal; -#else -using BaseSocket = Socket; -using BasePortal = HicnForwarderPortal; -#endif - -template class Socket { - static_assert(std::is_same::value -#ifdef __linux__ -#ifndef __ANDROID__ - || std::is_same::value -#ifdef __vpp__ - || std::is_same::value -#endif -#endif - , -#else - , - -#endif - "This class is not allowed as Portal"); - public: - using Portal = PortalType; - - virtual asio::io_service &getIoService() = 0; - virtual void connect() = 0; - virtual bool isRunning() = 0; + virtual asio::io_service &getIoService() { return portal_->getIoService(); } + protected: + Socket(std::shared_ptr &&portal); + virtual ~Socket(){}; + + protected: + std::shared_ptr portal_; + bool is_async_; }; } // namespace implementation diff --git a/libtransport/src/implementation/socket_consumer.h b/libtransport/src/implementation/socket_consumer.h index 87965923e..a7b6ac4e7 100644 --- a/libtransport/src/implementation/socket_consumer.h +++ b/libtransport/src/implementation/socket_consumer.h @@ -13,15 +13,17 @@ * limitations under the License. */ +#pragma once + #include #include #include -#include +#include #include #include -#include #include -#include +#include +#include namespace transport { namespace implementation { @@ -30,12 +32,12 @@ using namespace core; using namespace interface; using ReadCallback = interface::ConsumerSocket::ReadCallback; -class ConsumerSocket : public Socket { +class ConsumerSocket : public Socket { private: ConsumerSocket(interface::ConsumerSocket *consumer, int protocol, - std::shared_ptr &&portal) - : consumer_interface_(consumer), - portal_(portal), + std::shared_ptr &&portal) + : Socket(std::move(portal)), + consumer_interface_(consumer), async_downloader_(), interest_lifetime_(default_values::interest_lifetime), min_window_size_(default_values::min_window_size), @@ -54,16 +56,13 @@ class ConsumerSocket : public Socket { rate_estimation_observer_(nullptr), rate_estimation_batching_parameter_(default_values::batch), rate_estimation_choice_(0), - is_async_(false), - verifier_(std::make_shared()), + verifier_(std::make_shared()), verify_signature_(false), - key_content_(false), reset_window_(false), on_interest_output_(VOID_HANDLER), on_interest_timeout_(VOID_HANDLER), on_interest_satisfied_(VOID_HANDLER), on_content_object_input_(VOID_HANDLER), - on_content_object_verification_(VOID_HANDLER), stats_summary_(VOID_HANDLER), read_callback_(nullptr), timer_interval_milliseconds_(0), @@ -75,7 +74,7 @@ class ConsumerSocket : public Socket { break; case TransportProtocolAlgorithms::RTC: transport_protocol_ = - std::make_unique(this); + std::make_unique(this); break; case TransportProtocolAlgorithms::RAAQM: default: @@ -87,12 +86,12 @@ class ConsumerSocket : public Socket { public: ConsumerSocket(interface::ConsumerSocket *consumer, int protocol) - : ConsumerSocket(consumer, protocol, std::make_shared()) {} + : ConsumerSocket(consumer, protocol, std::make_shared()) {} ConsumerSocket(interface::ConsumerSocket *consumer, int protocol, asio::io_service &io_service) : ConsumerSocket(consumer, protocol, - std::make_shared(io_service)) { + std::make_shared(io_service)) { is_async_ = true; } @@ -138,8 +137,6 @@ class ConsumerSocket : public Socket { return CONSUMER_RUNNING; } - bool verifyKeyPackets() { return transport_protocol_->verifyKeyPackets(); } - void stop() { if (transport_protocol_->isRunning()) { transport_protocol_->stop(); @@ -152,8 +149,6 @@ class ConsumerSocket : public Socket { } } - asio::io_service &getIoService() { return portal_->getIoService(); } - virtual int setSocketOption(int socket_option_key, ReadCallback *socket_option_value) { // Reschedule the function on the io_service to avoid race condition in @@ -316,12 +311,6 @@ class ConsumerSocket : public Socket { break; } - case ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY: - if (socket_option_value == VOID_HANDLER) { - on_content_object_verification_ = VOID_HANDLER; - break; - } - default: return SOCKET_OPTION_NOT_SET; } @@ -334,16 +323,6 @@ class ConsumerSocket : public Socket { int result = SOCKET_OPTION_NOT_SET; if (!transport_protocol_->isRunning()) { switch (socket_option_key) { - case GeneralTransportOptions::VERIFY_SIGNATURE: - verify_signature_ = socket_option_value; - result = SOCKET_OPTION_SET; - break; - - case GeneralTransportOptions::KEY_CONTENT: - key_content_ = socket_option_value; - result = SOCKET_OPTION_SET; - break; - case RaaqmTransportOptions::PER_SESSION_CWINDOW_RESET: reset_window_ = socket_option_value; result = SOCKET_OPTION_SET; @@ -377,29 +356,6 @@ class ConsumerSocket : public Socket { }); } - int setSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationCallback socket_option_value) { - // Reschedule the function on the io_service to avoid race condition in - // case setSocketOption is called while the io_service is running. - return rescheduleOnIOService( - socket_option_key, socket_option_value, - [this](int socket_option_key, - ConsumerContentObjectVerificationCallback socket_option_value) - -> int { - switch (socket_option_key) { - case ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY: - on_content_object_verification_ = socket_option_value; - break; - - default: - return SOCKET_OPTION_NOT_SET; - } - - return SOCKET_OPTION_SET; - }); - } - int setSocketOption(int socket_option_key, ConsumerInterestCallback socket_option_value) { // Reschedule the function on the io_service to avoid race condition in @@ -433,51 +389,6 @@ class ConsumerSocket : public Socket { }); } - int setSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationFailedCallback socket_option_value) { - return rescheduleOnIOService( - socket_option_key, socket_option_value, - [this]( - int socket_option_key, - ConsumerContentObjectVerificationFailedCallback socket_option_value) - -> int { - switch (socket_option_key) { - case ConsumerCallbacksOptions::VERIFICATION_FAILED: - verification_failed_callback_ = socket_option_value; - break; - - default: - return SOCKET_OPTION_NOT_SET; - } - - return SOCKET_OPTION_SET; - }); - } - - // int setSocketOption( - // int socket_option_key, - // ConsumerContentObjectVerificationFailedCallback socket_option_value) { - // return rescheduleOnIOService( - // socket_option_key, socket_option_value, - // [this]( - // int socket_option_key, - // ConsumerContentObjectVerificationFailedCallback - // socket_option_value) - // -> int { - // switch (socket_option_key) { - // case ConsumerCallbacksOptions::VERIFICATION_FAILED: - // verification_failed_callback_ = socket_option_value; - // break; - - // default: - // return SOCKET_OPTION_NOT_SET; - // } - - // return SOCKET_OPTION_SET; - // }); - // } - int setSocketOption(int socket_option_key, IcnObserver *socket_option_value) { utils::SpinLock::Acquire locked(guard_raaqm_params_); switch (socket_option_key) { @@ -494,7 +405,7 @@ class ConsumerSocket : public Socket { int setSocketOption( int socket_option_key, - const std::shared_ptr &socket_option_value) { + const std::shared_ptr &socket_option_value) { int result = SOCKET_OPTION_NOT_SET; if (!transport_protocol_->isRunning()) { switch (socket_option_key) { @@ -516,14 +427,6 @@ class ConsumerSocket : public Socket { int result = SOCKET_OPTION_NOT_SET; if (!transport_protocol_->isRunning()) { switch (socket_option_key) { - case GeneralTransportOptions::CERTIFICATE: - key_id_ = verifier_->addKeyFromCertificate(socket_option_value); - - if (key_id_ != nullptr) { - result = SOCKET_OPTION_SET; - } - break; - case DataLinkOptions::OUTPUT_INTERFACE: output_interface_ = socket_option_value; portal_->setOutputInterface(output_interface_); @@ -642,14 +545,6 @@ class ConsumerSocket : public Socket { socket_option_value = transport_protocol_->isRunning(); break; - case GeneralTransportOptions::VERIFY_SIGNATURE: - socket_option_value = verify_signature_; - break; - - case GeneralTransportOptions::KEY_CONTENT: - socket_option_value = key_content_; - break; - case GeneralTransportOptions::ASYNC_MODE: socket_option_value = is_async_; break; @@ -699,29 +594,6 @@ class ConsumerSocket : public Socket { }); } - int getSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationCallback **socket_option_value) { - // Reschedule the function on the io_service to avoid race condition in - // case setSocketOption is called while the io_service is running. - return rescheduleOnIOService( - socket_option_key, socket_option_value, - [this](int socket_option_key, - ConsumerContentObjectVerificationCallback **socket_option_value) - -> int { - switch (socket_option_key) { - case ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY: - *socket_option_value = &on_content_object_verification_; - break; - - default: - return SOCKET_OPTION_NOT_GET; - } - - return SOCKET_OPTION_GET; - }); - } - int getSocketOption(int socket_option_key, ConsumerInterestCallback **socket_option_value) { // Reschedule the function on the io_service to avoid race condition in @@ -755,30 +627,8 @@ class ConsumerSocket : public Socket { }); } - int getSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationFailedCallback **socket_option_value) { - // Reschedule the function on the io_service to avoid race condition in - // case setSocketOption is called while the io_service is running. - return rescheduleOnIOService( - socket_option_key, socket_option_value, - [this](int socket_option_key, - ConsumerContentObjectVerificationFailedCallback * - *socket_option_value) -> int { - switch (socket_option_key) { - case ConsumerCallbacksOptions::VERIFICATION_FAILED: - *socket_option_value = &verification_failed_callback_; - break; - default: - return SOCKET_OPTION_NOT_GET; - } - - return SOCKET_OPTION_GET; - }); - } - int getSocketOption(int socket_option_key, - std::shared_ptr &socket_option_value) { + std::shared_ptr &socket_option_value) { switch (socket_option_key) { case PORTAL: socket_option_value = portal_; @@ -807,7 +657,7 @@ class ConsumerSocket : public Socket { } int getSocketOption(int socket_option_key, - std::shared_ptr &socket_option_value) { + std::shared_ptr &socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::VERIFIER: socket_option_value = verifier_; @@ -871,7 +721,7 @@ class ConsumerSocket : public Socket { // To enforce type check std::function func = lambda_func; int result = SOCKET_OPTION_SET; - if (transport_protocol_->isRunning()) { + if (transport_protocol_ && transport_protocol_->isRunning()) { std::mutex mtx; /* Condition variable for the wait */ std::condition_variable cv; @@ -898,7 +748,6 @@ class ConsumerSocket : public Socket { protected: interface::ConsumerSocket *consumer_interface_; - std::shared_ptr portal_; utils::EventThread async_downloader_; // No need to protect from multiple accesses in the async consumer @@ -926,13 +775,10 @@ class ConsumerSocket : public Socket { int rate_estimation_batching_parameter_; int rate_estimation_choice_; - bool is_async_; - // Verification parameters - std::shared_ptr verifier_; + std::shared_ptr verifier_; PARCKeyId *key_id_; std::atomic_bool verify_signature_; - bool key_content_; bool reset_window_; ConsumerInterestCallback on_interest_retransmission_; @@ -940,9 +786,7 @@ class ConsumerSocket : public Socket { ConsumerInterestCallback on_interest_timeout_; ConsumerInterestCallback on_interest_satisfied_; ConsumerContentObjectCallback on_content_object_input_; - ConsumerContentObjectVerificationCallback on_content_object_verification_; ConsumerTimerCallback stats_summary_; - ConsumerContentObjectVerificationFailedCallback verification_failed_callback_; ReadCallback *read_callback_; @@ -959,4 +803,4 @@ class ConsumerSocket : public Socket { }; } // namespace implementation -} // namespace transport \ No newline at end of file +} // namespace transport diff --git a/libtransport/src/implementation/socket_producer.h b/libtransport/src/implementation/socket_producer.h index a6f0f969e..af69cd818 100644 --- a/libtransport/src/implementation/socket_producer.h +++ b/libtransport/src/implementation/socket_producer.h @@ -15,9 +15,11 @@ #pragma once -#include +#include #include #include +#include +#include #include #include @@ -39,21 +41,17 @@ namespace implementation { using namespace core; using namespace interface; -class ProducerSocket : public Socket, - public BasePortal::ProducerCallback { - static constexpr uint32_t burst_size = 256; - - public: - explicit ProducerSocket(interface::ProducerSocket *producer_socket) - : producer_interface_(producer_socket), - portal_(std::make_shared(io_service_)), +class ProducerSocket : public Socket { + private: + ProducerSocket(interface::ProducerSocket *producer_socket, int protocol, + std::shared_ptr &&portal) + : Socket(std::move(portal)), + producer_interface_(producer_socket), 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), - hash_algorithm_(utils::CryptoHashType::SHA_256), + hash_algorithm_(auth::CryptoHashType::SHA_256), suffix_strategy_(core::NextSegmentCalculationStrategy::INCREMENTAL), on_interest_input_(VOID_HANDLER), on_interest_dropped_input_buffer_(VOID_HANDLER), @@ -65,15 +63,33 @@ class ProducerSocket : public Socket, on_content_object_in_output_buffer_(VOID_HANDLER), on_content_object_output_(VOID_HANDLER), on_content_object_evicted_from_output_buffer_(VOID_HANDLER), - on_content_produced_(VOID_HANDLER) {} - - virtual ~ProducerSocket() { - stop(); - if (listening_thread_.joinable()) { - listening_thread_.join(); + on_content_produced_(VOID_HANDLER) { + switch (protocol) { + case ProductionProtocolAlgorithms::RTC_PROD: + production_protocol_ = + std::make_unique(this); + break; + case ProductionProtocolAlgorithms::BYTE_STREAM: + default: + production_protocol_ = + std::make_unique(this); + break; } } + public: + ProducerSocket(interface::ProducerSocket *producer, int protocol) + : ProducerSocket(producer, protocol, std::make_shared()) {} + + ProducerSocket(interface::ProducerSocket *producer, int protocol, + asio::io_service &io_service) + : ProducerSocket(producer, protocol, + std::make_shared(io_service)) { + is_async_ = true; + } + + virtual ~ProducerSocket() {} + interface::ProducerSocket *getInterface() { return producer_interface_; } @@ -84,296 +100,10 @@ class ProducerSocket : public Socket, void connect() override { portal_->connect(false); - listening_thread_ = std::thread(std::bind(&ProducerSocket::listen, this)); + production_protocol_->start(); } - bool isRunning() override { return !io_service_.stopped(); }; - - virtual uint32_t produce(Name content_name, const uint8_t *buffer, - size_t buffer_size, bool is_last = true, - uint32_t start_offset = 0) { - return ProducerSocket::produce( - content_name, utils::MemBuf::copyBuffer(buffer, buffer_size), is_last, - start_offset); - } - - virtual uint32_t produce(Name content_name, - std::unique_ptr &&buffer, - bool is_last = true, uint32_t start_offset = 0) { - if (TRANSPORT_EXPECT_FALSE(buffer->length() == 0)) { - return 0; - } - - // Copy the atomic variables to ensure they keep the same value - // during the production - std::size_t data_packet_size = data_packet_size_; - uint32_t content_object_expiry_time = content_object_expiry_time_; - utils::CryptoHashType hash_algo = hash_algorithm_; - bool making_manifest = making_manifest_; - auto suffix_strategy = utils::SuffixStrategyFactory::getSuffixStrategy( - suffix_strategy_, start_offset); - std::shared_ptr signer; - getSocketOption(GeneralTransportOptions::SIGNER, signer); - - auto buffer_size = buffer->length(); - int bytes_segmented = 0; - std::size_t header_size; - std::size_t manifest_header_size = 0; - std::size_t signature_length = 0; - std::uint32_t final_block_number = start_offset; - uint64_t free_space_for_content = 0; - - core::Packet::Format format; - std::shared_ptr manifest; - bool is_last_manifest = false; - - // TODO Manifest may still be used for indexing - if (making_manifest && !signer) { - TRANSPORT_LOGE("Making manifests without setting producer identity."); - } - - core::Packet::Format hf_format = core::Packet::Format::HF_UNSPEC; - core::Packet::Format hf_format_ah = core::Packet::Format::HF_UNSPEC; - if (content_name.getType() == HNT_CONTIGUOUS_V4 || - content_name.getType() == HNT_IOV_V4) { - hf_format = core::Packet::Format::HF_INET_TCP; - hf_format_ah = core::Packet::Format::HF_INET_TCP_AH; - } else if (content_name.getType() == HNT_CONTIGUOUS_V6 || - content_name.getType() == HNT_IOV_V6) { - hf_format = core::Packet::Format::HF_INET6_TCP; - hf_format_ah = core::Packet::Format::HF_INET6_TCP_AH; - } else { - throw errors::RuntimeException("Unknown name format."); - } - - format = hf_format; - if (making_manifest) { - manifest_header_size = core::Packet::getHeaderSizeFromFormat( - signer ? hf_format_ah : hf_format, - signer ? signer->getSignatureLength() : 0); - } else if (signer) { - format = hf_format_ah; - signature_length = signer->getSignatureLength(); - } - - header_size = - core::Packet::getHeaderSizeFromFormat(format, signature_length); - free_space_for_content = data_packet_size - header_size; - uint32_t number_of_segments = uint32_t( - std::ceil(double(buffer_size) / double(free_space_for_content))); - if (free_space_for_content * number_of_segments < buffer_size) { - number_of_segments++; - } - - // TODO allocate space for all the headers - if (making_manifest) { - uint32_t segment_in_manifest = static_cast( - std::floor(double(data_packet_size - manifest_header_size - - ContentObjectManifest::getManifestHeaderSize()) / - ContentObjectManifest::getManifestEntrySize()) - - 1.0); - uint32_t number_of_manifests = static_cast( - std::ceil(float(number_of_segments) / segment_in_manifest)); - final_block_number += number_of_segments + number_of_manifests - 1; - - manifest.reset(ContentObjectManifest::createManifest( - content_name.setSuffix(suffix_strategy->getNextManifestSuffix()), - core::ManifestVersion::VERSION_1, core::ManifestType::INLINE_MANIFEST, - hash_algo, is_last_manifest, content_name, suffix_strategy_, - signer ? signer->getSignatureLength() : 0)); - manifest->setLifetime(content_object_expiry_time); - - if (is_last) { - manifest->setFinalBlockNumber(final_block_number); - } else { - manifest->setFinalBlockNumber(utils::SuffixStrategy::INVALID_SUFFIX); - } - } - - for (unsigned int packaged_segments = 0; - packaged_segments < number_of_segments; packaged_segments++) { - if (making_manifest) { - if (manifest->estimateManifestSize(2) > - data_packet_size - manifest_header_size) { - // Send the current manifest - manifest->encode(); - - // If identity set, sign manifest - if (signer) { - signer->sign(*manifest); - } - - passContentObjectToCallbacks(manifest); - TRANSPORT_LOGD("Send manifest %s", - manifest->getName().toString().c_str()); - - // Send content objects stored in the queue - while (!content_queue_.empty()) { - passContentObjectToCallbacks(content_queue_.front()); - TRANSPORT_LOGD( - "Send content %s", - content_queue_.front()->getName().toString().c_str()); - content_queue_.pop(); - } - - // Create new manifest. The reference to the last manifest has been - // acquired in the passContentObjectToCallbacks function, so we can - // safely release this reference - manifest.reset(ContentObjectManifest::createManifest( - content_name.setSuffix(suffix_strategy->getNextManifestSuffix()), - core::ManifestVersion::VERSION_1, - core::ManifestType::INLINE_MANIFEST, hash_algo, is_last_manifest, - content_name, suffix_strategy_, - signer ? signer->getSignatureLength() : 0)); - - manifest->setLifetime(content_object_expiry_time); - manifest->setFinalBlockNumber( - is_last ? final_block_number - : utils::SuffixStrategy::INVALID_SUFFIX); - } - } - - auto content_suffix = suffix_strategy->getNextContentSuffix(); - auto content_object = std::make_shared( - content_name.setSuffix(content_suffix), format); - content_object->setLifetime(content_object_expiry_time); - - auto b = buffer->cloneOne(); - b->trimStart(free_space_for_content * packaged_segments); - b->trimEnd(b->length()); - - if (TRANSPORT_EXPECT_FALSE(packaged_segments == number_of_segments - 1)) { - b->append(buffer_size - bytes_segmented); - bytes_segmented += (int)(buffer_size - bytes_segmented); - - if (is_last && making_manifest) { - is_last_manifest = true; - } else if (is_last) { - content_object->setRst(); - } - - } else { - b->append(free_space_for_content); - bytes_segmented += (int)(free_space_for_content); - } - - content_object->appendPayload(std::move(b)); - - if (making_manifest) { - using namespace std::chrono_literals; - utils::CryptoHash hash = content_object->computeDigest(hash_algo); - manifest->addSuffixHash(content_suffix, hash); - content_queue_.push(content_object); - } else { - if (signer) { - signer->sign(*content_object); - } - passContentObjectToCallbacks(content_object); - TRANSPORT_LOGD("Send content %s", - content_object->getName().toString().c_str()); - } - } - - if (making_manifest) { - if (is_last_manifest) { - manifest->setFinalManifest(is_last_manifest); - } - - manifest->encode(); - if (signer) { - signer->sign(*manifest); - } - - passContentObjectToCallbacks(manifest); - TRANSPORT_LOGD("Send manifest %s", - manifest->getName().toString().c_str()); - - while (!content_queue_.empty()) { - passContentObjectToCallbacks(content_queue_.front()); - TRANSPORT_LOGD("Send content %s", - content_queue_.front()->getName().toString().c_str()); - content_queue_.pop(); - } - } - - io_service_.post([this]() { - std::shared_ptr co; - while (object_queue_for_callbacks_.pop(co)) { - if (on_new_segment_) { - on_new_segment_(*producer_interface_, *co); - } - - if (on_content_object_to_sign_) { - on_content_object_to_sign_(*producer_interface_, *co); - } - - if (on_content_object_in_output_buffer_) { - on_content_object_in_output_buffer_(*producer_interface_, *co); - } - - if (on_content_object_output_) { - on_content_object_output_(*producer_interface_, *co); - } - } - }); - - io_service_.dispatch([this, buffer_size]() { - if (on_content_produced_) { - on_content_produced_(*producer_interface_, - std::make_error_code(std::errc(0)), buffer_size); - } - }); - - return suffix_strategy->getTotalCount(); - } - - virtual void produce(ContentObject &content_object) { - io_service_.dispatch([this, &content_object]() { - if (on_content_object_in_output_buffer_) { - on_content_object_in_output_buffer_(*producer_interface_, - content_object); - } - }); - - output_buffer_.insert(std::static_pointer_cast( - content_object.shared_from_this())); - - io_service_.dispatch([this, &content_object]() { - if (on_content_object_output_) { - on_content_object_output_(*producer_interface_, content_object); - } - }); - - portal_->sendContentObject(content_object); - } - - virtual void produce(const uint8_t *buffer, size_t buffer_size) { - produce(utils::MemBuf::copyBuffer(buffer, buffer_size)); - } - - virtual void produce(std::unique_ptr &&buffer) { - // This API is meant to be used just with the RTC producer. - // Here it cannot be used since no name for the content is specified. - throw errors::NotImplementedException(); - } - - virtual void asyncProduce(const Name &suffix, const uint8_t *buf, - size_t buffer_size, bool is_last = true, - uint32_t *start_offset = nullptr) { - if (!async_thread_.stopped()) { - async_thread_.add([this, suffix, buffer = buf, size = buffer_size, - is_last, start_offset]() { - if (start_offset != nullptr) { - *start_offset = ProducerSocket::produce(suffix, buffer, size, is_last, - *start_offset); - } else { - ProducerSocket::produce(suffix, buffer, size, is_last, 0); - } - }); - } - } - - void asyncProduce(const Name &suffix); + bool isRunning() override { return !production_protocol_->isRunning(); }; virtual void asyncProduce(Name content_name, std::unique_ptr &&buffer, @@ -381,75 +111,56 @@ class ProducerSocket : public Socket, uint32_t **last_segment = nullptr) { if (!async_thread_.stopped()) { auto a = buffer.release(); - async_thread_.add( - [this, content_name, a, is_last, offset, last_segment]() { - auto buf = std::unique_ptr(a); - if (last_segment != NULL) { - **last_segment = - offset + ProducerSocket::produce(content_name, std::move(buf), - is_last, offset); - } else { - ProducerSocket::produce(content_name, std::move(buf), is_last, - offset); - } - }); - } - } - - virtual void asyncProduce(ContentObject &content_object) { - if (!async_thread_.stopped()) { - auto co_ptr = std::static_pointer_cast( - content_object.shared_from_this()); - async_thread_.add([this, content_object = std::move(co_ptr)]() { - ProducerSocket::produce(*content_object); + async_thread_.add([this, content_name, a, is_last, offset, + last_segment]() { + auto buf = std::unique_ptr(a); + if (last_segment != NULL) { + **last_segment = offset + produceStream(content_name, std::move(buf), + is_last, offset); + } else { + produceStream(content_name, std::move(buf), is_last, offset); + } }); } } - virtual void registerPrefix(const Prefix &producer_namespace) { - served_namespaces_.push_back(producer_namespace); + virtual uint32_t produceStream(const Name &content_name, + std::unique_ptr &&buffer, + bool is_last = true, + uint32_t start_offset = 0) { + return production_protocol_->produceStream(content_name, std::move(buffer), + is_last, start_offset); } - void serveForever() { - if (listening_thread_.joinable()) { - listening_thread_.join(); - } + virtual uint32_t produceStream(const Name &content_name, + const uint8_t *buffer, size_t buffer_size, + bool is_last = true, + uint32_t start_offset = 0) { + return production_protocol_->produceStream( + content_name, buffer, buffer_size, is_last, start_offset); } - void stop() { portal_->stopEventsLoop(); } - - asio::io_service &getIoService() override { return portal_->getIoService(); }; - - virtual void onInterest(Interest &interest) { - if (on_interest_input_) { - on_interest_input_(*producer_interface_, interest); - } - - const std::shared_ptr content_object = - output_buffer_.find(interest); - - if (content_object) { - if (on_interest_satisfied_output_buffer_) { - on_interest_satisfied_output_buffer_(*producer_interface_, interest); - } + virtual uint32_t produceDatagram(const Name &content_name, + std::unique_ptr &&buffer) { + return production_protocol_->produceDatagram(content_name, + std::move(buffer)); + } - if (on_content_object_output_) { - on_content_object_output_(*producer_interface_, *content_object); - } + virtual uint32_t produceDatagram(const Name &content_name, + const uint8_t *buffer, size_t buffer_size) { + return production_protocol_->produceDatagram(content_name, buffer, + buffer_size); + } - portal_->sendContentObject(*content_object); - } else { - if (on_interest_process_) { - on_interest_process_(*producer_interface_, interest); - } - } + void produce(ContentObject &content_object) { + production_protocol_->produce(content_object); } - virtual void onInterest(Interest::Ptr &&interest) override { - onInterest(*interest); - }; + void registerPrefix(const Prefix &producer_namespace) { + production_protocol_->registerNamespaceWithNetwork(producer_namespace); + } - virtual void onError(std::error_code ec) override {} + void stop() { production_protocol_->stop(); } virtual int setSocketOption(int socket_option_key, uint32_t socket_option_value) { @@ -462,7 +173,7 @@ class ProducerSocket : public Socket, break; case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: - output_buffer_.setLimit(socket_option_value); + production_protocol_->setOutputBufferSize(socket_option_value); break; case GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME: @@ -533,6 +244,12 @@ class ProducerSocket : public Socket, break; } + case ProducerCallbacksOptions::CONTENT_OBJECT_TO_SIGN: + if (socket_option_value == VOID_HANDLER) { + on_content_object_to_sign_ = VOID_HANDLER; + break; + } + default: return SOCKET_OPTION_NOT_SET; } @@ -559,19 +276,6 @@ class ProducerSocket : public Socket, return SOCKET_OPTION_NOT_SET; } - virtual int setSocketOption(int socket_option_key, - std::list socket_option_value) { - switch (socket_option_key) { - case GeneralTransportOptions::NETWORK_NAME: - served_namespaces_ = socket_option_value; - break; - default: - return SOCKET_OPTION_NOT_SET; - } - - return SOCKET_OPTION_SET; - } - virtual int setSocketOption( int socket_option_key, interface::ProducerContentObjectCallback socket_option_value) { @@ -594,6 +298,10 @@ class ProducerSocket : public Socket, on_content_object_output_ = socket_option_value; break; + case ProducerCallbacksOptions::CONTENT_OBJECT_TO_SIGN: + on_content_object_to_sign_ = socket_option_value; + break; + default: return SOCKET_OPTION_NOT_SET; } @@ -663,7 +371,7 @@ class ProducerSocket : public Socket, } virtual int setSocketOption(int socket_option_key, - utils::CryptoHashType socket_option_value) { + auth::CryptoHashType socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::HASH_ALGORITHM: hash_algorithm_ = socket_option_value; @@ -675,11 +383,12 @@ class ProducerSocket : public Socket, return SOCKET_OPTION_SET; } - virtual int setSocketOption(int socket_option_key, - utils::CryptoSuite socket_option_value) { + virtual int setSocketOption( + int socket_option_key, + core::NextSegmentCalculationStrategy socket_option_value) { switch (socket_option_key) { - case GeneralTransportOptions::CRYPTO_SUITE: - crypto_suite_ = socket_option_value; + case GeneralTransportOptions::SUFFIX_STRATEGY: + suffix_strategy_ = socket_option_value; break; default: return SOCKET_OPTION_NOT_SET; @@ -690,7 +399,7 @@ class ProducerSocket : public Socket, virtual int setSocketOption( int socket_option_key, - const std::shared_ptr &socket_option_value) { + const std::shared_ptr &socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::SIGNER: { utils::SpinLock::Acquire locked(signer_lock_); @@ -708,7 +417,7 @@ class ProducerSocket : public Socket, uint32_t &socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: - socket_option_value = (uint32_t)output_buffer_.getLimit(); + socket_option_value = production_protocol_->getOutputBufferSize(); break; case GeneralTransportOptions::DATA_PACKET_SIZE: @@ -733,18 +442,8 @@ class ProducerSocket : public Socket, socket_option_value = making_manifest_; break; - default: - return SOCKET_OPTION_NOT_GET; - } - - return SOCKET_OPTION_GET; - } - - virtual int getSocketOption(int socket_option_key, - std::list &socket_option_value) { - switch (socket_option_key) { - case GeneralTransportOptions::NETWORK_NAME: - socket_option_value = served_namespaces_; + case GeneralTransportOptions::ASYNC_MODE: + socket_option_value = is_async_; break; default: @@ -776,6 +475,10 @@ class ProducerSocket : public Socket, *socket_option_value = &on_content_object_output_; break; + case ProducerCallbacksOptions::CONTENT_OBJECT_TO_SIGN: + *socket_option_value = &on_content_object_to_sign_; + break; + default: return SOCKET_OPTION_NOT_GET; } @@ -828,11 +531,11 @@ class ProducerSocket : public Socket, *socket_option_value = &on_interest_inserted_input_buffer_; break; - case CACHE_HIT: + case ProducerCallbacksOptions::CACHE_HIT: *socket_option_value = &on_interest_satisfied_output_buffer_; break; - case CACHE_MISS: + case ProducerCallbacksOptions::CACHE_MISS: *socket_option_value = &on_interest_process_; break; @@ -844,8 +547,9 @@ class ProducerSocket : public Socket, }); } - virtual int getSocketOption(int socket_option_key, - std::shared_ptr &socket_option_value) { + virtual int getSocketOption( + int socket_option_key, + std::shared_ptr &socket_option_value) { switch (socket_option_key) { case PORTAL: socket_option_value = portal_; @@ -859,7 +563,7 @@ class ProducerSocket : public Socket, } virtual int getSocketOption(int socket_option_key, - utils::CryptoHashType &socket_option_value) { + auth::CryptoHashType &socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::HASH_ALGORITHM: socket_option_value = hash_algorithm_; @@ -871,22 +575,22 @@ class ProducerSocket : public Socket, return SOCKET_OPTION_GET; } - virtual int getSocketOption(int socket_option_key, - utils::CryptoSuite &socket_option_value) { + virtual int getSocketOption( + int socket_option_key, + core::NextSegmentCalculationStrategy &socket_option_value) { switch (socket_option_key) { - case GeneralTransportOptions::HASH_ALGORITHM: - socket_option_value = crypto_suite_; + case GeneralTransportOptions::SUFFIX_STRATEGY: + socket_option_value = suffix_strategy_; break; default: return SOCKET_OPTION_NOT_GET; } - return SOCKET_OPTION_GET; } virtual int getSocketOption( int socket_option_key, - std::shared_ptr &socket_option_value) { + std::shared_ptr &socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::SIGNER: { utils::SpinLock::Acquire locked(signer_lock_); @@ -907,19 +611,21 @@ class ProducerSocket : public Socket, // If the thread calling lambda_func is not the same of io_service, this // function reschedule the function on it template - int rescheduleOnIOService(int socket_option_key, arg2 socket_option_value, - Lambda lambda_func) { + int rescheduleOnIOServiceWithReference(int socket_option_key, + arg2 &socket_option_value, + Lambda lambda_func) { // To enforce type check - std::function func = lambda_func; + std::function func = lambda_func; int result = SOCKET_OPTION_SET; - if (listening_thread_.joinable() && - std::this_thread::get_id() != listening_thread_.get_id()) { + if (production_protocol_ && production_protocol_->isRunning()) { std::mutex mtx; /* Condition variable for the wait */ std::condition_variable cv; + bool done = false; - io_service_.dispatch([&socket_option_key, &socket_option_value, &mtx, &cv, - &result, &done, &func]() { + portal_->getIoService().dispatch([&socket_option_key, + &socket_option_value, &mtx, &cv, + &result, &done, &func]() { std::unique_lock lck(mtx); done = true; result = func(socket_option_key, socket_option_value); @@ -939,21 +645,19 @@ class ProducerSocket : public Socket, // If the thread calling lambda_func is not the same of io_service, this // function reschedule the function on it template - int rescheduleOnIOServiceWithReference(int socket_option_key, - arg2 &socket_option_value, - Lambda lambda_func) { + int rescheduleOnIOService(int socket_option_key, arg2 socket_option_value, + Lambda lambda_func) { // To enforce type check - std::function func = lambda_func; + std::function func = lambda_func; int result = SOCKET_OPTION_SET; - if (listening_thread_.joinable() && - std::this_thread::get_id() != this->listening_thread_.get_id()) { + if (production_protocol_ && production_protocol_->isRunning()) { std::mutex mtx; /* Condition variable for the wait */ std::condition_variable cv; - bool done = false; - io_service_.dispatch([&socket_option_key, &socket_option_value, &mtx, &cv, - &result, &done, &func]() { + portal_->getIoService().dispatch([&socket_option_key, + &socket_option_value, &mtx, &cv, + &result, &done, &func]() { std::unique_lock lck(mtx); done = true; result = func(socket_option_key, socket_option_value); @@ -973,39 +677,20 @@ class ProducerSocket : public Socket, // Threads protected: interface::ProducerSocket *producer_interface_; - std::thread listening_thread_; asio::io_service io_service_; - std::shared_ptr portal_; std::atomic data_packet_size_; - std::list - served_namespaces_; // No need to be threadsafe, this is always modified - // by the application thread std::atomic content_object_expiry_time_; - utils::CircularFifo, 2048> - object_queue_for_callbacks_; - - // buffers - // ContentStore is thread-safe - utils::ContentStore output_buffer_; - utils::EventThread async_thread_; - int registration_status_; std::atomic making_manifest_; - - // map for storing sequence numbers for several calls of the publish - // function - std::unordered_map> seq_number_map_; - - std::atomic hash_algorithm_; - std::atomic crypto_suite_; + std::atomic hash_algorithm_; + std::atomic crypto_suite_; utils::SpinLock signer_lock_; - std::shared_ptr signer_; + std::shared_ptr signer_; core::NextSegmentCalculationStrategy suffix_strategy_; - // While manifests are being built, contents are stored in a queue - std::queue> content_queue_; + std::unique_ptr production_protocol_; // callbacks ProducerInterestCallback on_interest_input_; @@ -1021,63 +706,6 @@ class ProducerSocket : public Socket, ProducerContentObjectCallback on_content_object_evicted_from_output_buffer_; ProducerContentCallback on_content_produced_; - - private: - void listen() { - bool first = true; - - for (core::Prefix &producer_namespace : served_namespaces_) { - if (first) { - core::BindConfig bind_config(producer_namespace, 1000); - portal_->bind(bind_config); - portal_->setProducerCallback(this); - first = !first; - } else { - portal_->registerRoute(producer_namespace); - } - } - - portal_->runEventsLoop(); - } - - void scheduleSendBurst() { - io_service_.post([this]() { - std::shared_ptr co; - - for (uint32_t i = 0; i < burst_size; i++) { - if (object_queue_for_callbacks_.pop(co)) { - if (on_new_segment_) { - on_new_segment_(*producer_interface_, *co); - } - - if (on_content_object_to_sign_) { - on_content_object_to_sign_(*producer_interface_, *co); - } - - if (on_content_object_in_output_buffer_) { - on_content_object_in_output_buffer_(*producer_interface_, *co); - } - - if (on_content_object_output_) { - on_content_object_output_(*producer_interface_, *co); - } - } else { - break; - } - } - }); - } - - void passContentObjectToCallbacks( - const std::shared_ptr &content_object) { - output_buffer_.insert(content_object); - portal_->sendContentObject(*content_object); - object_queue_for_callbacks_.push(std::move(content_object)); - - if (object_queue_for_callbacks_.size() >= burst_size) { - scheduleSendBurst(); - } - } }; } // namespace implementation diff --git a/libtransport/src/implementation/tls_rtc_socket_producer.cc b/libtransport/src/implementation/tls_rtc_socket_producer.cc index 9ef79ca23..9a62c8683 100644 --- a/libtransport/src/implementation/tls_rtc_socket_producer.cc +++ b/libtransport/src/implementation/tls_rtc_socket_producer.cc @@ -15,10 +15,8 @@ #include #include - #include #include - #include #include #include diff --git a/libtransport/src/implementation/tls_rtc_socket_producer.h b/libtransport/src/implementation/tls_rtc_socket_producer.h index 685c91244..92c657afc 100644 --- a/libtransport/src/implementation/tls_rtc_socket_producer.h +++ b/libtransport/src/implementation/tls_rtc_socket_producer.h @@ -15,7 +15,6 @@ #pragma once -#include #include namespace transport { @@ -23,8 +22,7 @@ namespace implementation { class P2PSecureProducerSocket; -class TLSRTCProducerSocket : public RTCProducerSocket, - public TLSProducerSocket { +class TLSRTCProducerSocket : public TLSProducerSocket { friend class P2PSecureProducerSocket; public: @@ -34,7 +32,8 @@ class TLSRTCProducerSocket : public RTCProducerSocket, ~TLSRTCProducerSocket() = default; - void produce(std::unique_ptr &&buffer) override; + uint32_t produceDatagram(const Name &content_name, + std::unique_ptr &&buffer) override; void accept() override; diff --git a/libtransport/src/implementation/tls_socket_consumer.cc b/libtransport/src/implementation/tls_socket_consumer.cc index 1be6f41a7..99bcd4360 100644 --- a/libtransport/src/implementation/tls_socket_consumer.cc +++ b/libtransport/src/implementation/tls_socket_consumer.cc @@ -136,7 +136,6 @@ TLSConsumerSocket::TLSConsumerSocket(interface::ConsumerSocket *consumer_socket, int protocol, SSL *ssl) : ConsumerSocket(consumer_socket, protocol), name_(), - buf_pool_(), decrypted_content_(), payload_(), head_(), @@ -223,14 +222,15 @@ int TLSConsumerSocket::download_content(const Name &name) { content_downloaded_ = false; std::size_t max_buffer_size = read_callback_decrypted_->maxBufferSize(); - std::size_t buffer_size = read_callback_decrypted_->maxBufferSize() + SSL3_RT_MAX_PLAIN_LENGTH; + std::size_t buffer_size = + read_callback_decrypted_->maxBufferSize() + SSL3_RT_MAX_PLAIN_LENGTH; decrypted_content_ = utils::MemBuf::createCombined(buffer_size); int result = -1; std::size_t size = 0; while (!content_downloaded_ || something_to_read_) { - result = SSL_read( - this->ssl_, decrypted_content_->writableTail(), SSL3_RT_MAX_PLAIN_LENGTH); + result = SSL_read(this->ssl_, decrypted_content_->writableTail(), + SSL3_RT_MAX_PLAIN_LENGTH); /* SSL_read returns the data only if there were SSL3_RT_MAX_PLAIN_LENGTH of * the data has been fully downloaded */ diff --git a/libtransport/src/implementation/tls_socket_consumer.h b/libtransport/src/implementation/tls_socket_consumer.h index 1c5df346a..be08ec47d 100644 --- a/libtransport/src/implementation/tls_socket_consumer.h +++ b/libtransport/src/implementation/tls_socket_consumer.h @@ -16,9 +16,7 @@ #pragma once #include - #include - #include namespace transport { @@ -74,7 +72,6 @@ class TLSConsumerSocket : public ConsumerSocket, SSL_CTX *ctx_; /* Chain of MemBuf to be used as a temporary buffer to pass descypted data * from the underlying layer to the application */ - utils::ObjectPool buf_pool_; std::unique_ptr decrypted_content_; /* Chain of MemBuf holding the payload to be written into interest or data */ std::unique_ptr payload_; diff --git a/libtransport/src/implementation/tls_socket_producer.cc b/libtransport/src/implementation/tls_socket_producer.cc index 339a1ad58..e54d38d56 100644 --- a/libtransport/src/implementation/tls_socket_producer.cc +++ b/libtransport/src/implementation/tls_socket_producer.cc @@ -14,10 +14,8 @@ */ #include - #include #include - #include #include #include @@ -50,10 +48,14 @@ int TLSProducerSocket::readOld(BIO *b, char *buf, int size) { std::unique_lock lck(socket->mtx_); + TRANSPORT_LOGD("Start wait on the CV."); + if (!socket->something_to_read_) { (socket->cv_).wait(lck); } + TRANSPORT_LOGD("CV unlocked."); + /* Either there already is something to read, or the thread has been waken up. * We must return the payload in the interest anyway */ utils::MemBuf *membuf = socket->handshake_packet_->next(); @@ -103,7 +105,7 @@ int TLSProducerSocket::writeOld(BIO *b, const char *buf, int num) { socket->tls_chunks_--; socket->parent_->setSocketOption(GeneralTransportOptions::MAKE_MANIFEST, false); - socket->parent_->ProducerSocket::produce( + socket->parent_->ProducerSocket::produceStream( socket->name_, (const uint8_t *)buf, num, socket->tls_chunks_ == 0, socket->last_segment_); socket->parent_->setSocketOption(GeneralTransportOptions::MAKE_MANIFEST, @@ -122,18 +124,18 @@ int TLSProducerSocket::writeOld(BIO *b, const char *buf, int num) { socket->tls_chunks_--; socket->to_call_oncontentproduced_--; - socket->last_segment_ += socket->ProducerSocket::produce( + socket->last_segment_ += socket->ProducerSocket::produceStream( socket->name_, std::move(mbuf), socket->tls_chunks_ == 0, socket->last_segment_); - ProducerContentCallback on_content_produced_application; + ProducerContentCallback *on_content_produced_application; socket->getSocketOption(ProducerCallbacksOptions::CONTENT_PRODUCED, - on_content_produced_application); + &on_content_produced_application); if (socket->to_call_oncontentproduced_ == 0 && on_content_produced_application) { - on_content_produced_application(*socket->getInterface(), - std::error_code(), 0); + on_content_produced_application->operator()(*socket->getInterface(), + std::error_code(), 0); } }); } @@ -144,7 +146,8 @@ int TLSProducerSocket::writeOld(BIO *b, const char *buf, int num) { TLSProducerSocket::TLSProducerSocket(interface::ProducerSocket *producer_socket, P2PSecureProducerSocket *parent, const Name &handshake_name) - : ProducerSocket(producer_socket), + : ProducerSocket(producer_socket, + ProductionProtocolAlgorithms::BYTE_STREAM), on_content_produced_application_(), mtx_(), cv_(), @@ -236,13 +239,14 @@ void TLSProducerSocket::accept() { std::move(parent_->map_producers[handshake_name_])); parent_->map_producers.erase(handshake_name_); - ProducerInterestCallback on_interest_process_decrypted; + ProducerInterestCallback *on_interest_process_decrypted; getSocketOption(ProducerCallbacksOptions::CACHE_MISS, - on_interest_process_decrypted); + &on_interest_process_decrypted); - if (on_interest_process_decrypted) { - Interest inter(std::move(handshake_packet_)); - on_interest_process_decrypted(*getInterface(), inter); + if (*on_interest_process_decrypted) { + Interest inter(std::move(*handshake_packet_)); + handshake_packet_.reset(); + on_interest_process_decrypted->operator()(*getInterface(), inter); } else { throw errors::RuntimeException( "On interest process unset: unable to perform handshake"); @@ -270,14 +274,14 @@ void TLSProducerSocket::onInterest(ProducerSocket &p, Interest &interest) { std::unique_lock lck(mtx_); name_ = interest.getName(); - interest.separateHeaderPayload(); + // interest.separateHeaderPayload(); handshake_packet_ = interest.acquireMemBufReference(); something_to_read_ = true; cv_.notify_one(); return; } else if (handshake_state == SERVER_FINISHED) { - interest.separateHeaderPayload(); + // interest.separateHeaderPayload(); handshake_packet_ = interest.acquireMemBufReference(); something_to_read_ = true; @@ -288,12 +292,12 @@ void TLSProducerSocket::onInterest(ProducerSocket &p, Interest &interest) { interest.getPayload()->length()); } - ProducerInterestCallback on_interest_input_decrypted; + ProducerInterestCallback *on_interest_input_decrypted; getSocketOption(ProducerCallbacksOptions::INTEREST_INPUT, - on_interest_input_decrypted); + &on_interest_input_decrypted); - if (on_interest_input_decrypted) - (on_interest_input_decrypted)(*getInterface(), interest); + if (*on_interest_input_decrypted) + (*on_interest_input_decrypted)(*getInterface(), interest); } } @@ -301,17 +305,19 @@ void TLSProducerSocket::cacheMiss(interface::ProducerSocket &p, Interest &interest) { HandshakeState handshake_state = getHandshakeState(); + TRANSPORT_LOGD("On cache miss in TLS socket producer."); + if (handshake_state == CLIENT_HELLO) { std::unique_lock lck(mtx_); - interest.separateHeaderPayload(); + // interest.separateHeaderPayload(); handshake_packet_ = interest.acquireMemBufReference(); something_to_read_ = true; handshake_state_ = CLIENT_FINISHED; cv_.notify_one(); } else if (handshake_state == SERVER_FINISHED) { - interest.separateHeaderPayload(); + // interest.separateHeaderPayload(); handshake_packet_ = interest.acquireMemBufReference(); something_to_read_ = true; @@ -343,16 +349,16 @@ void TLSProducerSocket::onContentProduced(interface::ProducerSocket &p, const std::error_code &err, uint64_t bytes_written) {} -uint32_t TLSProducerSocket::produce(Name content_name, - std::unique_ptr &&buffer, - bool is_last, uint32_t start_offset) { +uint32_t TLSProducerSocket::produceStream( + const Name &content_name, std::unique_ptr &&buffer, + bool is_last, uint32_t start_offset) { if (getHandshakeState() != SERVER_FINISHED) { throw errors::RuntimeException( "New handshake on the same P2P secure producer socket not supported"); } size_t buf_size = buffer->length(); - name_ = served_namespaces_.front().mapName(content_name); + name_ = production_protocol_->getNamespaces().front().mapName(content_name); tls_chunks_ = to_call_oncontentproduced_ = ceil((float)buf_size / (float)SSL3_RT_MAX_PLAIN_LENGTH); @@ -370,46 +376,6 @@ uint32_t TLSProducerSocket::produce(Name content_name, return 0; } -void TLSProducerSocket::asyncProduce(const Name &content_name, - const uint8_t *buf, size_t buffer_size, - bool is_last, uint32_t *start_offset) { - if (!encryption_thread_.stopped()) { - encryption_thread_.add([this, content_name, buffer = buf, - size = buffer_size, is_last, start_offset]() { - if (start_offset != NULL) { - produce(content_name, buffer, size, is_last, *start_offset); - } else { - produce(content_name, buffer, size, is_last, 0); - } - }); - } -} - -void TLSProducerSocket::asyncProduce(Name content_name, - std::unique_ptr &&buffer, - bool is_last, uint32_t offset, - uint32_t **last_segment) { - if (!encryption_thread_.stopped()) { - auto a = buffer.release(); - encryption_thread_.add( - [this, content_name, a, is_last, offset, last_segment]() { - auto buf = std::unique_ptr(a); - if (last_segment != NULL) { - *last_segment = &last_segment_; - } - produce(content_name, std::move(buf), is_last, offset); - }); - } -} - -void TLSProducerSocket::asyncProduce(ContentObject &content_object) { - throw errors::RuntimeException("API not supported"); -} - -void TLSProducerSocket::produce(ContentObject &content_object) { - throw errors::RuntimeException("API not supported"); -} - long TLSProducerSocket::ctrl(BIO *b, int cmd, long num, void *ptr) { if (cmd == BIO_CTRL_FLUSH) { } @@ -424,13 +390,14 @@ int TLSProducerSocket::addHicnKeyIdCb(SSL *s, unsigned int ext_type, void *add_arg) { TLSProducerSocket *socket = reinterpret_cast(add_arg); + TRANSPORT_LOGD("On addHicnKeyIdCb, for the prefix registration."); + if (ext_type == 100) { - ip_prefix_t ip_prefix = - socket->parent_->served_namespaces_.front().toIpPrefixStruct(); - int inet_family = - socket->parent_->served_namespaces_.front().getAddressFamily(); - uint16_t prefix_len_bits = - socket->parent_->served_namespaces_.front().getPrefixLength(); + auto &prefix = + socket->parent_->production_protocol_->getNamespaces().front(); + const ip_prefix_t &ip_prefix = prefix.toIpPrefixStruct(); + int inet_family = prefix.getAddressFamily(); + uint16_t prefix_len_bits = prefix.getPrefixLength(); uint8_t prefix_len_bytes = prefix_len_bits / 8; uint8_t prefix_len_u32 = prefix_len_bits / 32; @@ -479,10 +446,9 @@ int TLSProducerSocket::addHicnKeyIdCb(SSL *s, unsigned int ext_type, socket->parent_->on_interest_process_decrypted_; socket->registerPrefix( - Prefix(socket->parent_->served_namespaces_.front().getName( - Name(inet_family, (uint8_t *)&mask), - Name(inet_family, (uint8_t *)&keyId_component), - socket->parent_->served_namespaces_.front().getName()), + Prefix(prefix.getName(Name(inet_family, (uint8_t *)&mask), + Name(inet_family, (uint8_t *)&keyId_component), + prefix.getName()), out_ip->len)); socket->connect(); } @@ -580,61 +546,5 @@ int TLSProducerSocket::getSocketOption( }); } -int TLSProducerSocket::getSocketOption( - int socket_option_key, ProducerContentCallback &socket_option_value) { - return rescheduleOnIOServiceWithReference( - socket_option_key, socket_option_value, - [this](int socket_option_key, - ProducerContentCallback &socket_option_value) -> int { - switch (socket_option_key) { - case ProducerCallbacksOptions::CONTENT_PRODUCED: - socket_option_value = on_content_produced_application_; - break; - - default: - return SOCKET_OPTION_NOT_GET; - } - - return SOCKET_OPTION_GET; - }); -} - -int TLSProducerSocket::getSocketOption( - int socket_option_key, ProducerInterestCallback &socket_option_value) { - // Reschedule the function on the io_service to avoid race condition in case - // setSocketOption is called while the io_service is running. - return rescheduleOnIOServiceWithReference( - socket_option_key, socket_option_value, - [this](int socket_option_key, - ProducerInterestCallback &socket_option_value) -> int { - switch (socket_option_key) { - case ProducerCallbacksOptions::INTEREST_INPUT: - socket_option_value = on_interest_input_decrypted_; - break; - - case ProducerCallbacksOptions::INTEREST_DROP: - socket_option_value = on_interest_dropped_input_buffer_; - break; - - case ProducerCallbacksOptions::INTEREST_PASS: - socket_option_value = on_interest_inserted_input_buffer_; - break; - - case ProducerCallbacksOptions::CACHE_HIT: - socket_option_value = on_interest_satisfied_output_buffer_; - break; - - case ProducerCallbacksOptions::CACHE_MISS: - socket_option_value = on_interest_process_decrypted_; - break; - - default: - return SOCKET_OPTION_NOT_GET; - } - - return SOCKET_OPTION_GET; - }); -} - } // namespace implementation } // namespace transport diff --git a/libtransport/src/implementation/tls_socket_producer.h b/libtransport/src/implementation/tls_socket_producer.h index 2382e8695..a542a4d9f 100644 --- a/libtransport/src/implementation/tls_socket_producer.h +++ b/libtransport/src/implementation/tls_socket_producer.h @@ -16,8 +16,8 @@ #pragma once #include - #include + #include #include @@ -36,26 +36,18 @@ class TLSProducerSocket : virtual public ProducerSocket { ~TLSProducerSocket(); - uint32_t produce(Name content_name, const uint8_t *buffer, size_t buffer_size, - bool is_last = true, uint32_t start_offset = 0) override { - return produce(content_name, utils::MemBuf::copyBuffer(buffer, buffer_size), - is_last, start_offset); + uint32_t produceStream(const Name &content_name, const uint8_t *buffer, + size_t buffer_size, bool is_last = true, + uint32_t start_offset = 0) override { + return produceStream(content_name, + utils::MemBuf::copyBuffer(buffer, buffer_size), + is_last, start_offset); } - uint32_t produce(Name content_name, std::unique_ptr &&buffer, - bool is_last = true, uint32_t start_offset = 0) override; - - void produce(ContentObject &content_object) override; - - void asyncProduce(const Name &suffix, const uint8_t *buf, size_t buffer_size, - bool is_last = true, - uint32_t *start_offset = nullptr) override; - - void asyncProduce(Name content_name, std::unique_ptr &&buffer, - bool is_last, uint32_t offset, - uint32_t **last_segment = nullptr) override; - - void asyncProduce(ContentObject &content_object) override; + uint32_t produceStream(const Name &content_name, + std::unique_ptr &&buffer, + bool is_last = true, + uint32_t start_offset = 0) override; virtual void accept(); @@ -80,7 +72,7 @@ class TLSProducerSocket : virtual public ProducerSocket { ProducerInterestCallback &socket_option_value); using ProducerSocket::getSocketOption; - using ProducerSocket::onInterest; + // using ProducerSocket::onInterest; using ProducerSocket::setSocketOption; protected: @@ -119,6 +111,7 @@ class TLSProducerSocket : virtual public ProducerSocket { int to_call_oncontentproduced_; bool still_writing_; utils::EventThread encryption_thread_; + utils::EventThread async_thread_; void onInterest(ProducerSocket &p, Interest &interest); diff --git a/libtransport/src/interfaces/CMakeLists.txt b/libtransport/src/interfaces/CMakeLists.txt index e1d144596..0284aa412 100644 --- a/libtransport/src/interfaces/CMakeLists.txt +++ b/libtransport/src/interfaces/CMakeLists.txt @@ -14,24 +14,24 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_producer.cc ${CMAKE_CURRENT_SOURCE_DIR}/socket_producer.cc ${CMAKE_CURRENT_SOURCE_DIR}/socket_consumer.cc ${CMAKE_CURRENT_SOURCE_DIR}/portal.cc ${CMAKE_CURRENT_SOURCE_DIR}/callbacks.cc + ${CMAKE_CURRENT_SOURCE_DIR}/global_configuration.cc ) if (${OPENSSL_VERSION} VERSION_EQUAL "1.1.1a" OR ${OPENSSL_VERSION} VERSION_GREATER "1.1.1a") list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/p2psecure_socket_producer.cc ${CMAKE_CURRENT_SOURCE_DIR}/p2psecure_socket_consumer.cc - ${CMAKE_CURRENT_SOURCE_DIR}/tls_rtc_socket_producer.cc + # ${CMAKE_CURRENT_SOURCE_DIR}/tls_rtc_socket_producer.cc ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_producer.cc ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_consumer.cc ) list(APPEND HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/tls_rtc_socket_producer.h + # ${CMAKE_CURRENT_SOURCE_DIR}/tls_rtc_socket_producer.h ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_producer.h ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_consumer.h ) diff --git a/libtransport/src/interfaces/global_configuration.cc b/libtransport/src/interfaces/global_configuration.cc new file mode 100644 index 000000000..8fb6601f3 --- /dev/null +++ b/libtransport/src/interfaces/global_configuration.cc @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include + +#include + +namespace transport { +namespace interface { +namespace global_config { + +void parseConfigurationFile(const std::string& path) { + core::GlobalConfiguration::getInstance().parseConfiguration(path); +} + +void ConfigurationObject::get() { + std::error_code ec; + core::GlobalConfiguration::getInstance().getConfiguration(*this, ec); + + if (ec) { + TRANSPORT_LOGE("Error setting global config: %s", ec.message().c_str()); + } +} + +void ConfigurationObject::set() { + std::error_code ec; + core::GlobalConfiguration::getInstance().setConfiguration(*this, ec); + + if (ec) { + TRANSPORT_LOGE("Error setting global config: %s", ec.message().c_str()); + } +} + +} // namespace global_config +} // namespace interface +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/interfaces/p2psecure_socket_consumer.cc b/libtransport/src/interfaces/p2psecure_socket_consumer.cc index 038441dfc..e473a1e2e 100644 --- a/libtransport/src/interfaces/p2psecure_socket_consumer.cc +++ b/libtransport/src/interfaces/p2psecure_socket_consumer.cc @@ -14,7 +14,6 @@ */ #include - #include namespace transport { diff --git a/libtransport/src/interfaces/p2psecure_socket_producer.cc b/libtransport/src/interfaces/p2psecure_socket_producer.cc index 37352259c..10d8a1367 100644 --- a/libtransport/src/interfaces/p2psecure_socket_producer.cc +++ b/libtransport/src/interfaces/p2psecure_socket_producer.cc @@ -14,7 +14,6 @@ */ #include - #include namespace transport { @@ -25,7 +24,7 @@ P2PSecureProducerSocket::P2PSecureProducerSocket() { } P2PSecureProducerSocket::P2PSecureProducerSocket( - bool rtc, const std::shared_ptr &identity) { + bool rtc, const std::shared_ptr &identity) { socket_ = std::make_unique(this, rtc, identity); } diff --git a/libtransport/src/interfaces/portal.cc b/libtransport/src/interfaces/portal.cc index 36cbd0c3b..2ab51c4b9 100644 --- a/libtransport/src/interfaces/portal.cc +++ b/libtransport/src/interfaces/portal.cc @@ -14,38 +14,35 @@ */ #include - #include namespace transport { namespace interface { -using implementation::BasePortal; - -Portal::Portal() { implementation_ = new implementation::BasePortal(); } +Portal::Portal() { implementation_ = new core::Portal(); } Portal::Portal(asio::io_service &io_service) { - implementation_ = new BasePortal(io_service); + implementation_ = new core::Portal(io_service); } -Portal::~Portal() { delete reinterpret_cast(implementation_); } +Portal::~Portal() { delete reinterpret_cast(implementation_); } void Portal::setConsumerCallback(ConsumerCallback *consumer_callback) { - reinterpret_cast(implementation_) + reinterpret_cast(implementation_) ->setConsumerCallback(consumer_callback); } void Portal::setProducerCallback(ProducerCallback *producer_callback) { - reinterpret_cast(implementation_) + reinterpret_cast(implementation_) ->setProducerCallback(producer_callback); } void Portal::connect(bool is_consumer) { - reinterpret_cast(implementation_)->connect(is_consumer); + reinterpret_cast(implementation_)->connect(is_consumer); } bool Portal::interestIsPending(const core::Name &name) { - return reinterpret_cast(implementation_) + return reinterpret_cast(implementation_) ->interestIsPending(name); } @@ -53,46 +50,46 @@ void Portal::sendInterest( core::Interest::Ptr &&interest, OnContentObjectCallback &&on_content_object_callback, OnInterestTimeoutCallback &&on_interest_timeout_callback) { - reinterpret_cast(implementation_) + reinterpret_cast(implementation_) ->sendInterest(std::move(interest), std::move(on_content_object_callback), std::move(on_interest_timeout_callback)); } void Portal::bind(const BindConfig &config) { - reinterpret_cast(implementation_)->bind(config); + reinterpret_cast(implementation_)->bind(config); } void Portal::runEventsLoop() { - reinterpret_cast(implementation_)->runEventsLoop(); + reinterpret_cast(implementation_)->runEventsLoop(); } void Portal::runOneEvent() { - reinterpret_cast(implementation_)->runOneEvent(); + reinterpret_cast(implementation_)->runOneEvent(); } void Portal::sendContentObject(core::ContentObject &content_object) { - reinterpret_cast(implementation_) + reinterpret_cast(implementation_) ->sendContentObject(content_object); } void Portal::stopEventsLoop() { - reinterpret_cast(implementation_)->stopEventsLoop(); + reinterpret_cast(implementation_)->stopEventsLoop(); } void Portal::killConnection() { - reinterpret_cast(implementation_)->killConnection(); + reinterpret_cast(implementation_)->killConnection(); } void Portal::clear() { - reinterpret_cast(implementation_)->clear(); + reinterpret_cast(implementation_)->clear(); } asio::io_service &Portal::getIoService() { - return reinterpret_cast(implementation_)->getIoService(); + return reinterpret_cast(implementation_)->getIoService(); } void Portal::registerRoute(core::Prefix &prefix) { - reinterpret_cast(implementation_)->registerRoute(prefix); + reinterpret_cast(implementation_)->registerRoute(prefix); } } // namespace interface diff --git a/libtransport/src/interfaces/socket_consumer.cc b/libtransport/src/interfaces/socket_consumer.cc index ea0606347..4eee73cab 100644 --- a/libtransport/src/interfaces/socket_consumer.cc +++ b/libtransport/src/interfaces/socket_consumer.cc @@ -46,8 +46,6 @@ void ConsumerSocket::stop() { socket_->stop(); } void ConsumerSocket::resume() { socket_->resume(); } -bool ConsumerSocket::verifyKeyPackets() { return socket_->verifyKeyPackets(); } - asio::io_service &ConsumerSocket::getIoService() { return socket_->getIoService(); } @@ -87,23 +85,11 @@ int ConsumerSocket::setSocketOption( return socket_->setSocketOption(socket_option_key, socket_option_value); } -int ConsumerSocket::setSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationCallback socket_option_value) { - return socket_->setSocketOption(socket_option_key, socket_option_value); -} - int ConsumerSocket::setSocketOption( int socket_option_key, ConsumerInterestCallback socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } -int ConsumerSocket::setSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationFailedCallback socket_option_value) { - return socket_->setSocketOption(socket_option_key, socket_option_value); -} - int ConsumerSocket::setSocketOption(int socket_option_key, IcnObserver *socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); @@ -111,7 +97,7 @@ int ConsumerSocket::setSocketOption(int socket_option_key, int ConsumerSocket::setSocketOption( int socket_option_key, - const std::shared_ptr &socket_option_value) { + const std::shared_ptr &socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } @@ -151,23 +137,11 @@ int ConsumerSocket::getSocketOption( return socket_->setSocketOption(socket_option_key, socket_option_value); } -int ConsumerSocket::getSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationCallback **socket_option_value) { - return socket_->setSocketOption(socket_option_key, socket_option_value); -} - int ConsumerSocket::getSocketOption( int socket_option_key, ConsumerInterestCallback **socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } -int ConsumerSocket::getSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationFailedCallback **socket_option_value) { - return socket_->setSocketOption(socket_option_key, socket_option_value); -} - int ConsumerSocket::getSocketOption(int socket_option_key, IcnObserver **socket_option_value) { return socket_->getSocketOption(socket_option_key, socket_option_value); @@ -175,7 +149,7 @@ int ConsumerSocket::getSocketOption(int socket_option_key, int ConsumerSocket::getSocketOption( int socket_option_key, - std::shared_ptr &socket_option_value) { + std::shared_ptr &socket_option_value) { return socket_->getSocketOption(socket_option_key, socket_option_value); } diff --git a/libtransport/src/interfaces/socket_producer.cc b/libtransport/src/interfaces/socket_producer.cc index d030fe756..b04947dfd 100644 --- a/libtransport/src/interfaces/socket_producer.cc +++ b/libtransport/src/interfaces/socket_producer.cc @@ -14,7 +14,6 @@ */ #include - #include #include @@ -31,11 +30,12 @@ namespace interface { using namespace core; ProducerSocket::ProducerSocket(int protocol) { - if (protocol != 0) { - throw std::runtime_error("Production protocol must be 0."); - } + socket_ = std::make_unique(this, protocol); +} - socket_ = std::make_unique(this); +ProducerSocket::ProducerSocket(int protocol, asio::io_service &io_service) { + socket_ = std::make_unique(this, protocol, + io_service); } ProducerSocket::ProducerSocket(bool) {} @@ -46,19 +46,34 @@ void ProducerSocket::connect() { socket_->connect(); } bool ProducerSocket::isRunning() { return socket_->isRunning(); } -uint32_t ProducerSocket::produce(Name content_name, - std::unique_ptr &&buffer, - bool is_last, uint32_t start_offset) { - return socket_->produce(content_name, std::move(buffer), is_last, - start_offset); +uint32_t ProducerSocket::produceStream(const Name &content_name, + std::unique_ptr &&buffer, + bool is_last, uint32_t start_offset) { + return socket_->produceStream(content_name, std::move(buffer), is_last, + start_offset); } -void ProducerSocket::produce(ContentObject &content_object) { - return socket_->produce(content_object); +uint32_t ProducerSocket::produceStream(const Name &content_name, + const uint8_t *buffer, + size_t buffer_size, bool is_last, + uint32_t start_offset) { + return socket_->produceStream(content_name, buffer, buffer_size, is_last, + start_offset); } -void ProducerSocket::produce(std::unique_ptr &&buffer) { - socket_->produce(std::move(buffer)); +uint32_t ProducerSocket::produceDatagram( + const Name &content_name, std::unique_ptr &&buffer) { + return socket_->produceDatagram(content_name, std::move(buffer)); +} + +uint32_t ProducerSocket::produceDatagram(const Name &content_name, + const uint8_t *buffer, + size_t buffer_size) { + return socket_->produceDatagram(content_name, buffer, buffer_size); +} + +void ProducerSocket::produce(ContentObject &content_object) { + return socket_->produce(content_object); } void ProducerSocket::asyncProduce(Name content_name, @@ -69,16 +84,10 @@ void ProducerSocket::asyncProduce(Name content_name, last_segment); } -void ProducerSocket::asyncProduce(ContentObject &content_object) { - return socket_->asyncProduce(content_object); -} - void ProducerSocket::registerPrefix(const Prefix &producer_namespace) { return socket_->registerPrefix(producer_namespace); } -void ProducerSocket::serveForever() { return socket_->serveForever(); } - void ProducerSocket::stop() { return socket_->stop(); } asio::io_service &ProducerSocket::getIoService() { @@ -105,11 +114,6 @@ int ProducerSocket::setSocketOption(int socket_option_key, return socket_->setSocketOption(socket_option_key, socket_option_value); } -int ProducerSocket::setSocketOption(int socket_option_key, - std::list socket_option_value) { - return socket_->setSocketOption(socket_option_key, socket_option_value); -} - int ProducerSocket::setSocketOption( int socket_option_key, ProducerContentObjectCallback socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); @@ -126,18 +130,13 @@ int ProducerSocket::setSocketOption( } int ProducerSocket::setSocketOption(int socket_option_key, - utils::CryptoHashType socket_option_value) { - return socket_->setSocketOption(socket_option_key, socket_option_value); -} - -int ProducerSocket::setSocketOption(int socket_option_key, - utils::CryptoSuite socket_option_value) { + auth::CryptoHashType socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } int ProducerSocket::setSocketOption( int socket_option_key, - const std::shared_ptr &socket_option_value) { + const std::shared_ptr &socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } @@ -156,11 +155,6 @@ int ProducerSocket::getSocketOption(int socket_option_key, return socket_->getSocketOption(socket_option_key, socket_option_value); } -int ProducerSocket::getSocketOption(int socket_option_key, - std::list &socket_option_value) { - return socket_->getSocketOption(socket_option_key, socket_option_value); -} - int ProducerSocket::getSocketOption( int socket_option_key, ProducerContentObjectCallback **socket_option_value) { @@ -177,19 +171,13 @@ int ProducerSocket::getSocketOption( return socket_->getSocketOption(socket_option_key, socket_option_value); } -int ProducerSocket::getSocketOption( - int socket_option_key, utils::CryptoHashType &socket_option_value) { - return socket_->getSocketOption(socket_option_key, socket_option_value); -} - int ProducerSocket::getSocketOption(int socket_option_key, - utils::CryptoSuite &socket_option_value) { + auth::CryptoHashType &socket_option_value) { return socket_->getSocketOption(socket_option_key, socket_option_value); } int ProducerSocket::getSocketOption( - int socket_option_key, - std::shared_ptr &socket_option_value) { + int socket_option_key, std::shared_ptr &socket_option_value) { return socket_->getSocketOption(socket_option_key, socket_option_value); } diff --git a/libtransport/src/interfaces/tls_rtc_socket_producer.cc b/libtransport/src/interfaces/tls_rtc_socket_producer.cc index 132f34721..7326fcbcb 100644 --- a/libtransport/src/interfaces/tls_rtc_socket_producer.cc +++ b/libtransport/src/interfaces/tls_rtc_socket_producer.cc @@ -13,9 +13,8 @@ * limitations under the License. */ -#include - #include +#include namespace transport { namespace interface { diff --git a/libtransport/src/interfaces/tls_socket_consumer.cc b/libtransport/src/interfaces/tls_socket_consumer.cc index d87642f73..6c1c535b5 100644 --- a/libtransport/src/interfaces/tls_socket_consumer.cc +++ b/libtransport/src/interfaces/tls_socket_consumer.cc @@ -13,9 +13,8 @@ * limitations under the License. */ -#include - #include +#include namespace transport { namespace interface { diff --git a/libtransport/src/interfaces/tls_socket_producer.cc b/libtransport/src/interfaces/tls_socket_producer.cc index 44aa0cf8b..037702f72 100644 --- a/libtransport/src/interfaces/tls_socket_producer.cc +++ b/libtransport/src/interfaces/tls_socket_producer.cc @@ -13,9 +13,8 @@ * limitations under the License. */ -#include - #include +#include namespace transport { namespace interface { diff --git a/libtransport/src/io_modules/CMakeLists.txt b/libtransport/src/io_modules/CMakeLists.txt new file mode 100644 index 000000000..6553b9a2b --- /dev/null +++ b/libtransport/src/io_modules/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (c) 2021 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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) + +if (${CMAKE_SYSTEM_NAME} MATCHES Android) + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/udp/hicn_forwarder_module.cc + ${CMAKE_CURRENT_SOURCE_DIR}/udp/udp_socket_connector.cc + ) + + list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/udp/hicn_forwarder_module.h + ${CMAKE_CURRENT_SOURCE_DIR}/udp/udp_socket_connector.h + ) + + set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) + set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) +else() + add_subdirectory(udp) + add_subdirectory(loopback) + add_subdirectory(forwarder) + + if (__vpp__) + add_subdirectory(memif) + endif() +endif() \ No newline at end of file diff --git a/libtransport/src/io_modules/forwarder/CMakeLists.txt b/libtransport/src/io_modules/forwarder/CMakeLists.txt new file mode 100644 index 000000000..92662bc4c --- /dev/null +++ b/libtransport/src/io_modules/forwarder/CMakeLists.txt @@ -0,0 +1,44 @@ +# Copyright (c) 2021 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + + +list(APPEND MODULE_HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/endpoint.h + ${CMAKE_CURRENT_SOURCE_DIR}/errors.h + ${CMAKE_CURRENT_SOURCE_DIR}/forwarder_module.h + ${CMAKE_CURRENT_SOURCE_DIR}/forwarder.h + ${CMAKE_CURRENT_SOURCE_DIR}/udp_tunnel_listener.h + ${CMAKE_CURRENT_SOURCE_DIR}/udp_tunnel.h + ${CMAKE_CURRENT_SOURCE_DIR}/global_counter.h +) + +list(APPEND MODULE_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/errors.cc + ${CMAKE_CURRENT_SOURCE_DIR}/forwarder_module.cc + ${CMAKE_CURRENT_SOURCE_DIR}/forwarder.cc + ${CMAKE_CURRENT_SOURCE_DIR}/udp_tunnel_listener.cc + ${CMAKE_CURRENT_SOURCE_DIR}/udp_tunnel.cc +) + +build_module(forwarder_module + SHARED + SOURCES ${MODULE_SOURCE_FILES} + DEPENDS ${DEPENDENCIES} + COMPONENT lib${LIBTRANSPORT} + INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILE_FLAGS} +) diff --git a/libtransport/src/io_modules/forwarder/configuration.h b/libtransport/src/io_modules/forwarder/configuration.h new file mode 100644 index 000000000..fcaa5530d --- /dev/null +++ b/libtransport/src/io_modules/forwarder/configuration.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace transport { +namespace core { + +struct ListenerConfig { + std::string address; + std::uint16_t port; + std::string name; +}; + +struct ConnectorConfig { + std::string local_address; + std::uint16_t local_port; + std::string remote_address; + std::uint16_t remote_port; + std::string name; +}; + +struct RouteConfig { + std::string prefix; + uint16_t weight; + std::string connector; + std::string name; +}; + +class Configuration { + public: + Configuration() : n_threads_(1) {} + + bool empty() { + return listeners_.empty() && connectors_.empty() && routes_.empty(); + } + + Configuration& setThreadNumber(std::size_t threads) { + n_threads_ = threads; + return *this; + } + + std::size_t getThreadNumber() { return n_threads_; } + + template + Configuration& addListener(Args&&... args) { + listeners_.emplace_back(std::forward(args)...); + return *this; + } + + template + Configuration& addConnector(Args&&... args) { + connectors_.emplace_back(std::forward(args)...); + return *this; + } + + template + Configuration& addRoute(Args&&... args) { + routes_.emplace_back(std::forward(args)...); + return *this; + } + + std::vector& getListeners() { return listeners_; } + + std::vector& getConnectors() { return connectors_; } + + std::vector& getRoutes() { return routes_; } + + private: + std::vector listeners_; + std::vector connectors_; + std::vector routes_; + std::size_t n_threads_; +}; + +} // namespace core +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/io_modules/forwarder/errors.cc b/libtransport/src/io_modules/forwarder/errors.cc new file mode 100644 index 000000000..b5f131499 --- /dev/null +++ b/libtransport/src/io_modules/forwarder/errors.cc @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Cisco and/or its affiliates. + */ + +#include + +namespace transport { +namespace core { + +const std::error_category& forwarder_category() { + static forwarder_category_impl instance; + + return instance; +} + +const char* forwarder_category_impl::name() const throw() { + return "proxy::connector::error"; +} + +std::string forwarder_category_impl::message(int ev) const { + switch (static_cast(ev)) { + case forwarder_error::success: { + return "Success"; + } + case forwarder_error::disconnected: { + return "Connector is disconnected"; + } + case forwarder_error::receive_failed: { + return "Packet reception failed"; + } + case forwarder_error::send_failed: { + return "Packet send failed"; + } + case forwarder_error::memory_allocation_error: { + return "Impossible to allocate memory for packet pool"; + } + case forwarder_error::invalid_connector_type: { + return "Invalid type specified for connector."; + } + case forwarder_error::invalid_connector: { + return "Created connector was invalid."; + } + case forwarder_error::interest_cache_miss: { + return "interest cache miss."; + } + default: { + return "Unknown connector error"; + } + } +} +} // namespace core +} // namespace transport diff --git a/libtransport/src/io_modules/forwarder/errors.h b/libtransport/src/io_modules/forwarder/errors.h new file mode 100644 index 000000000..dd5cc8fe7 --- /dev/null +++ b/libtransport/src/io_modules/forwarder/errors.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace transport { +namespace core { +/** + * @brief Get the default server error category. + * @return The default server error category instance. + * + * @warning The first call to this function is thread-safe only starting with + * C++11. + */ +const std::error_category& forwarder_category(); + +/** + * The list of errors. + */ +enum class forwarder_error { + success = 0, + send_failed, + receive_failed, + disconnected, + memory_allocation_error, + invalid_connector_type, + invalid_connector, + interest_cache_miss +}; + +/** + * @brief Create an error_code instance for the given error. + * @param error The error. + * @return The error_code instance. + */ +inline std::error_code make_error_code(forwarder_error error) { + return std::error_code(static_cast(error), forwarder_category()); +} + +/** + * @brief Create an error_condition instance for the given error. + * @param error The error. + * @return The error_condition instance. + */ +inline std::error_condition make_error_condition(forwarder_error error) { + return std::error_condition(static_cast(error), forwarder_category()); +} + +/** + * @brief A server error category. + */ +class forwarder_category_impl : public std::error_category { + public: + /** + * @brief Get the name of the category. + * @return The name of the category. + */ + virtual const char* name() const throw(); + + /** + * @brief Get the error message for a given error. + * @param ev The error numeric value. + * @return The message associated to the error. + */ + virtual std::string message(int ev) const; +}; +} // namespace core +} // namespace transport + +namespace std { +// namespace system { +template <> +struct is_error_code_enum<::transport::core::forwarder_error> + : public std::true_type {}; +// } // namespace system +} // namespace std diff --git a/libtransport/src/io_modules/forwarder/forwarder.cc b/libtransport/src/io_modules/forwarder/forwarder.cc new file mode 100644 index 000000000..7e89e2f9f --- /dev/null +++ b/libtransport/src/io_modules/forwarder/forwarder.cc @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include +#include +#include +#include + +namespace transport { + +namespace core { + +constexpr char Forwarder::forwarder_config_section[]; + +Forwarder::Forwarder() : config_() { + using namespace std::placeholders; + GlobalConfiguration::getInstance().registerConfigurationParser( + forwarder_config_section, + std::bind(&Forwarder::parseForwarderConfiguration, this, _1, _2)); + + if (!config_.empty()) { + initThreads(); + initListeners(); + initConnectors(); + } +} + +Forwarder::~Forwarder() { + for (auto &l : listeners_) { + l->close(); + } + + for (auto &c : remote_connectors_) { + c.second->close(); + } + + GlobalConfiguration::getInstance().unregisterConfigurationParser( + forwarder_config_section); +} + +void Forwarder::initThreads() { + for (unsigned i = 0; i < config_.getThreadNumber(); i++) { + thread_pool_.emplace_back(io_service_, /* detached */ false); + } +} + +void Forwarder::initListeners() { + using namespace std::placeholders; + for (auto &l : config_.getListeners()) { + listeners_.emplace_back(std::make_shared( + io_service_, + std::bind(&Forwarder::onPacketFromListener, this, _1, _2, _3), + asio::ip::udp::endpoint(asio::ip::address::from_string(l.address), + l.port))); + } +} + +void Forwarder::initConnectors() { + using namespace std::placeholders; + for (auto &c : config_.getConnectors()) { + auto id = GlobalCounter::getInstance().getNext(); + auto conn = new UdpTunnelConnector( + io_service_, std::bind(&Forwarder::onPacketReceived, this, _1, _2, _3), + std::bind(&Forwarder::onPacketSent, this, _1, _2), + std::bind(&Forwarder::onConnectorClosed, this, _1), + std::bind(&Forwarder::onConnectorReconnected, this, _1)); + conn->setConnectorId(id); + remote_connectors_.emplace(id, conn); + conn->connect(c.remote_address, c.remote_port, c.local_address, + c.local_port); + } +} + +Connector::Id Forwarder::registerLocalConnector( + asio::io_service &io_service, + Connector::PacketReceivedCallback &&receive_callback, + Connector::OnReconnectCallback &&reconnect_callback) { + utils::SpinLock::Acquire locked(connector_lock_); + auto id = GlobalCounter::getInstance().getNext(); + auto connector = std::make_shared( + io_service, receive_callback, nullptr, nullptr, reconnect_callback); + connector->setConnectorId(id); + local_connectors_.emplace(id, std::move(connector)); + return id; +} + +Forwarder &Forwarder::deleteConnector(Connector::Id id) { + utils::SpinLock::Acquire locked(connector_lock_); + auto it = local_connectors_.find(id); + if (it != local_connectors_.end()) { + it->second->close(); + local_connectors_.erase(it); + } + + return *this; +} + +Connector::Ptr Forwarder::getConnector(Connector::Id id) { + utils::SpinLock::Acquire locked(connector_lock_); + auto it = local_connectors_.find(id); + if (it != local_connectors_.end()) { + return it->second; + } + + return nullptr; +} + +void Forwarder::onPacketFromListener(Connector *connector, + utils::MemBuf &packet_buffer, + const std::error_code &ec) { + // Create connector + connector->setReceiveCallback( + std::bind(&Forwarder::onPacketReceived, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3)); + + TRANSPORT_LOGD("Packet received from listener."); + + { + utils::SpinLock::Acquire locked(connector_lock_); + remote_connectors_.emplace(connector->getConnectorId(), + connector->shared_from_this()); + } + // TODO Check if control packet or not. For the moment it is not. + onPacketReceived(connector, packet_buffer, ec); +} + +void Forwarder::onPacketReceived(Connector *connector, + utils::MemBuf &packet_buffer, + const std::error_code &ec) { + // Figure out the type of packet we received + bool is_interest = Packet::isInterest(packet_buffer.data()); + + Packet *packet = nullptr; + if (is_interest) { + packet = static_cast(&packet_buffer); + } else { + packet = static_cast(&packet_buffer); + } + + for (auto &c : local_connectors_) { + auto role = c.second->getRole(); + auto is_producer = role == Connector::Role::PRODUCER; + if ((is_producer && is_interest) || (!is_producer && !is_interest)) { + c.second->send(*packet); + } else { + TRANSPORT_LOGD( + "Error sending packet to local connector. is_interest = %d - " + "is_producer = %d", + (int)is_interest, (int)is_producer); + } + } + + // PCS Lookup + FIB lookup. Skip for now + + // Forward packet to local connectors +} + +void Forwarder::send(Packet &packet) { + // TODo Here a nice PIT/CS / FIB would be required:) + // For now let's just forward the packet on the remote connector we get + if (remote_connectors_.begin() == remote_connectors_.end()) { + return; + } + + auto remote_endpoint = + remote_connectors_.begin()->second->getRemoteEndpoint(); + TRANSPORT_LOGD("Sending packet to: %s:%u", + remote_endpoint.getAddress().to_string().c_str(), + remote_endpoint.getPort()); + remote_connectors_.begin()->second->send(packet); +} + +void Forwarder::onPacketSent(Connector *connector, const std::error_code &ec) {} + +void Forwarder::onConnectorClosed(Connector *connector) {} + +void Forwarder::onConnectorReconnected(Connector *connector) {} + +void Forwarder::parseForwarderConfiguration( + const libconfig::Setting &forwarder_config, std::error_code &ec) { + using namespace libconfig; + + // n_thread + if (forwarder_config.exists("n_threads")) { + // Get number of threads + int n_threads = 1; + forwarder_config.lookupValue("n_threads", n_threads); + TRANSPORT_LOGD("Forwarder threads from config file: %u", n_threads); + config_.setThreadNumber(n_threads); + } + + // listeners + if (forwarder_config.exists("listeners")) { + // get path where looking for modules + const Setting &listeners = forwarder_config.lookup("listeners"); + auto count = listeners.getLength(); + + for (int i = 0; i < count; i++) { + const Setting &listener = listeners[i]; + ListenerConfig list; + unsigned port; + + list.name = listener.getName(); + listener.lookupValue("local_address", list.address); + listener.lookupValue("local_port", port); + list.port = (uint16_t)(port); + + TRANSPORT_LOGD("Adding listener %s, (%s:%u)", list.name.c_str(), + list.address.c_str(), list.port); + config_.addListener(std::move(list)); + } + } + + // connectors + if (forwarder_config.exists("connectors")) { + // get path where looking for modules + const Setting &connectors = forwarder_config.lookup("connectors"); + auto count = connectors.getLength(); + + for (int i = 0; i < count; i++) { + const Setting &connector = connectors[i]; + ConnectorConfig conn; + + conn.name = connector.getName(); + unsigned port = 0; + + if (!connector.lookupValue("local_address", conn.local_address)) { + conn.local_address = ""; + } + + if (!connector.lookupValue("local_port", port)) { + port = 0; + } + + conn.local_port = (uint16_t)(port); + + if (!connector.lookupValue("remote_address", conn.remote_address)) { + throw errors::RuntimeException( + "Error in configuration file: remote_address is a mandatory field " + "of Connectors."); + } + + if (!connector.lookupValue("remote_port", port)) { + throw errors::RuntimeException( + "Error in configuration file: remote_port is a mandatory field " + "of Connectors."); + } + + conn.remote_port = (uint16_t)(port); + + TRANSPORT_LOGD("Adding connector %s, (%s:%u %s:%u)", conn.name.c_str(), + conn.local_address.c_str(), conn.local_port, + conn.remote_address.c_str(), conn.remote_port); + config_.addConnector(std::move(conn)); + } + } + + // Routes + if (forwarder_config.exists("routes")) { + const Setting &routes = forwarder_config.lookup("routes"); + auto count = routes.getLength(); + + for (int i = 0; i < count; i++) { + const Setting &route = routes[i]; + RouteConfig r; + unsigned weight; + + r.name = route.getName(); + route.lookupValue("prefix", r.prefix); + route.lookupValue("weight", weight); + route.lookupValue("connector", r.connector); + r.weight = (uint16_t)(weight); + + TRANSPORT_LOGD("Adding route %s %s (%s %u)", r.name.c_str(), + r.prefix.c_str(), r.connector.c_str(), r.weight); + config_.addRoute(std::move(r)); + } + } +} + +} // namespace core +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/io_modules/forwarder/forwarder.h b/libtransport/src/io_modules/forwarder/forwarder.h new file mode 100644 index 000000000..5b564bb5e --- /dev/null +++ b/libtransport/src/io_modules/forwarder/forwarder.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace transport { + +namespace core { + +class Forwarder : public utils::Singleton { + static constexpr char forwarder_config_section[] = "forwarder"; + friend class utils::Singleton; + + public: + Forwarder(); + + ~Forwarder(); + + void initThreads(); + void initListeners(); + void initConnectors(); + + Connector::Id registerLocalConnector( + asio::io_service &io_service, + Connector::PacketReceivedCallback &&receive_callback, + Connector::OnReconnectCallback &&reconnect_callback); + + Forwarder &deleteConnector(Connector::Id id); + + Connector::Ptr getConnector(Connector::Id id); + + void send(Packet &packet); + + void stop(); + + private: + void onPacketFromListener(Connector *connector, utils::MemBuf &packet_buffer, + const std::error_code &ec); + void onPacketReceived(Connector *connector, utils::MemBuf &packet_buffer, + const std::error_code &ec); + void onPacketSent(Connector *connector, const std::error_code &ec); + void onConnectorClosed(Connector *connector); + void onConnectorReconnected(Connector *connector); + + void parseForwarderConfiguration(const libconfig::Setting &io_config, + std::error_code &ec); + + asio::io_service io_service_; + utils::SpinLock connector_lock_; + + /** + * Connectors and listeners must be declares *before* thread_pool_, so that + * threads destructors will wait for them to gracefully close before being + * destroyed. + */ + std::unordered_map remote_connectors_; + std::unordered_map local_connectors_; + std::vector listeners_; + + std::vector thread_pool_; + + Configuration config_; +}; + +} // namespace core +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/io_modules/forwarder/forwarder_module.cc b/libtransport/src/io_modules/forwarder/forwarder_module.cc new file mode 100644 index 000000000..356b42d3b --- /dev/null +++ b/libtransport/src/io_modules/forwarder/forwarder_module.cc @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +namespace transport { + +namespace core { + +ForwarderModule::ForwarderModule() + : IoModule(), + name_(""), + connector_id_(Connector::invalid_connector), + forwarder_(Forwarder::getInstance()) {} + +ForwarderModule::~ForwarderModule() { + forwarder_.deleteConnector(connector_id_); +} + +bool ForwarderModule::isConnected() { return true; } + +void ForwarderModule::send(Packet &packet) { + IoModule::send(packet); + forwarder_.send(packet); + // TRANSPORT_LOGD("ForwarderModule: sending from %u to %d", local_id_, + // 1 - local_id_); + + // local_faces_.at(1 - local_id_).onPacket(packet); +} + +void ForwarderModule::send(const uint8_t *packet, std::size_t len) { + // not supported + throw errors::NotImplementedException(); +} + +void ForwarderModule::registerRoute(const Prefix &prefix) { + // For the moment we route packets from one socket to the other. + // Next step will be to introduce a FIB + return; +} + +void ForwarderModule::closeConnection() { + forwarder_.deleteConnector(connector_id_); +} + +void ForwarderModule::init(Connector::PacketReceivedCallback &&receive_callback, + Connector::OnReconnectCallback &&reconnect_callback, + asio::io_service &io_service, + const std::string &app_name) { + connector_id_ = forwarder_.registerLocalConnector( + io_service, std::move(receive_callback), std::move(reconnect_callback)); + name_ = app_name; +} + +void ForwarderModule::processControlMessageReply(utils::MemBuf &packet_buffer) { + return; +} + +void ForwarderModule::connect(bool is_consumer) { + forwarder_.getConnector(connector_id_) + ->setRole(is_consumer ? Connector::Role::CONSUMER + : Connector::Role::PRODUCER); +} + +std::uint32_t ForwarderModule::getMtu() { return interface_mtu; } + +bool ForwarderModule::isControlMessage(const uint8_t *message) { return false; } + +extern "C" IoModule *create_module(void) { return new ForwarderModule(); } + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/forwarder/forwarder_module.h b/libtransport/src/io_modules/forwarder/forwarder_module.h new file mode 100644 index 000000000..58bfb7996 --- /dev/null +++ b/libtransport/src/io_modules/forwarder/forwarder_module.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace transport { + +namespace core { + +class Forwarder; + +class ForwarderModule : public IoModule { + static constexpr std::uint16_t interface_mtu = 1500; + + public: + ForwarderModule(); + + ~ForwarderModule(); + + void connect(bool is_consumer) override; + + void send(Packet &packet) override; + void send(const uint8_t *packet, std::size_t len) override; + + bool isConnected() override; + + void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::OnReconnectCallback &&reconnect_callback, + asio::io_service &io_service, + const std::string &app_name = "Libtransport") override; + + void registerRoute(const Prefix &prefix) override; + + std::uint32_t getMtu() override; + + bool isControlMessage(const uint8_t *message) override; + + void processControlMessageReply(utils::MemBuf &packet_buffer) override; + + void closeConnection() override; + + private: + std::string name_; + Connector::Id connector_id_; + Forwarder &forwarder_; +}; + +extern "C" IoModule *create_module(void); + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/forwarder/global_id_counter.h b/libtransport/src/io_modules/forwarder/global_id_counter.h new file mode 100644 index 000000000..fe8d76730 --- /dev/null +++ b/libtransport/src/io_modules/forwarder/global_id_counter.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include + +namespace transport { + +namespace core { + +template +class GlobalCounter { + public: + static GlobalCounter& getInstance() { + std::lock_guard lock(global_mutex_); + + if (!instance_) { + instance_.reset(new GlobalCounter()); + } + + return *instance_; + } + + T getNext() { return counter_++; } + + private: + GlobalCounter() : counter_(0) {} + static std::unique_ptr> instance_; + static std::mutex global_mutex_; + std::atomic counter_; +}; + +template +std::unique_ptr> GlobalCounter::instance_ = nullptr; + +template +std::mutex GlobalCounter::global_mutex_; + +} // namespace core +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/io_modules/forwarder/udp_tunnel.cc b/libtransport/src/io_modules/forwarder/udp_tunnel.cc new file mode 100644 index 000000000..dc725fc4e --- /dev/null +++ b/libtransport/src/io_modules/forwarder/udp_tunnel.cc @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + */ + +#include +#include +#include + +#include +#include +#include + +namespace transport { +namespace core { + +UdpTunnelConnector::~UdpTunnelConnector() {} + +void UdpTunnelConnector::connect(const std::string &hostname, uint16_t port, + const std::string &bind_address, + uint16_t bind_port) { + if (state_ == State::CLOSED) { + state_ = State::CONNECTING; + endpoint_iterator_ = resolver_.resolve({hostname, std::to_string(port)}); + remote_endpoint_send_ = *endpoint_iterator_; + socket_->open(remote_endpoint_send_.protocol()); + + if (!bind_address.empty() && bind_port != 0) { + using namespace asio::ip; + socket_->bind( + udp::endpoint(address::from_string(bind_address), bind_port)); + } + + state_ = State::CONNECTED; + + remote_endpoint_ = Endpoint(remote_endpoint_send_); + local_endpoint_ = Endpoint(socket_->local_endpoint()); + + doRecvPacket(); + +#ifdef LINUX + send_timer_.expires_from_now(std::chrono::microseconds(50)); + send_timer_.async_wait(std::bind(&UdpTunnelConnector::writeHandler, this, + std::placeholders::_1)); +#endif + } +} + +void UdpTunnelConnector::send(Packet &packet) { + strand_->post([this, pkt{packet.shared_from_this()}]() { + bool write_in_progress = !output_buffer_.empty(); + output_buffer_.push_back(std::move(pkt)); + if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { + if (!write_in_progress) { + doSendPacket(); + } + } else { + data_available_ = true; + } + }); +} + +void UdpTunnelConnector::send(const uint8_t *packet, std::size_t len) {} + +void UdpTunnelConnector::close() { + TRANSPORT_LOGD("UDPTunnelConnector::close"); + state_ = State::CLOSED; + bool is_socket_owned = socket_.use_count() == 1; + if (is_socket_owned) { + io_service_.dispatch([this]() { + this->socket_->close(); + // on_close_callback_(shared_from_this()); + }); + } +} + +void UdpTunnelConnector::doSendPacket() { +#ifdef LINUX + send_timer_.expires_from_now(std::chrono::microseconds(50)); + send_timer_.async_wait(std::bind(&UdpTunnelConnector::writeHandler, this, + std::placeholders::_1)); +#else + auto packet = output_buffer_.front().get(); + auto array = std::vector(); + + const ::utils::MemBuf *current = packet; + do { + array.push_back(asio::const_buffer(current->data(), current->length())); + current = current->next(); + } while (current != packet); + + socket_->async_send_to( + std::move(array), remote_endpoint_send_, + strand_->wrap([this](std::error_code ec, std::size_t length) { + if (TRANSPORT_EXPECT_TRUE(!ec)) { + sent_callback_(this, make_error_code(forwarder_error::success)); + } else if (ec.value() == + static_cast(std::errc::operation_canceled)) { + // The connection has been closed by the application. + return; + } else { + sendFailed(); + sent_callback_(this, ec); + } + + output_buffer_.pop_front(); + if (!output_buffer_.empty()) { + doSendPacket(); + } + })); +#endif +} + +#ifdef LINUX +void UdpTunnelConnector::writeHandler(std::error_code ec) { + if (TRANSPORT_EXPECT_FALSE(state_ != State::CONNECTED)) { + return; + } + + auto len = std::min(output_buffer_.size(), std::size_t(Connector::max_burst)); + + if (len) { + int m = 0; + for (auto &p : output_buffer_) { + auto packet = p.get(); + ::utils::MemBuf *current = packet; + int b = 0; + do { + // array.push_back(asio::const_buffer(current->data(), + // current->length())); + tx_iovecs_[m][b].iov_base = current->writableData(); + tx_iovecs_[m][b].iov_len = current->length(); + current = current->next(); + b++; + } while (current != packet); + + tx_msgs_[m].msg_hdr.msg_iov = tx_iovecs_[m]; + tx_msgs_[m].msg_hdr.msg_iovlen = b; + tx_msgs_[m].msg_hdr.msg_name = remote_endpoint_send_.data(); + tx_msgs_[m].msg_hdr.msg_namelen = remote_endpoint_send_.size(); + m++; + + if (--len == 0) { + break; + } + } + + int retval = sendmmsg(socket_->native_handle(), tx_msgs_, m, MSG_DONTWAIT); + if (retval > 0) { + while (retval--) { + output_buffer_.pop_front(); + } + } else if (retval != EWOULDBLOCK && retval != EAGAIN) { + TRANSPORT_LOGE("Error sending messages! %s %d\n", strerror(errno), + retval); + return; + } + } + + if (!output_buffer_.empty()) { + send_timer_.expires_from_now(std::chrono::microseconds(50)); + send_timer_.async_wait(std::bind(&UdpTunnelConnector::writeHandler, this, + std::placeholders::_1)); + } +} + +void UdpTunnelConnector::readHandler(std::error_code ec) { + TRANSPORT_LOGD("UdpTunnelConnector receive packet"); + + // TRANSPORT_LOGD("UdpTunnelConnector received packet length=%lu", length); + if (TRANSPORT_EXPECT_TRUE(!ec)) { + if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { + if (current_position_ == 0) { + for (int i = 0; i < max_burst; i++) { + auto read_buffer = getRawBuffer(); + rx_iovecs_[i][0].iov_base = read_buffer.first; + rx_iovecs_[i][0].iov_len = read_buffer.second; + rx_msgs_[i].msg_hdr.msg_iov = rx_iovecs_[i]; + rx_msgs_[i].msg_hdr.msg_iovlen = 1; + } + } + + int res = recvmmsg(socket_->native_handle(), rx_msgs_ + current_position_, + max_burst - current_position_, MSG_DONTWAIT, nullptr); + if (res < 0) { + TRANSPORT_LOGE("Error receiving messages! %s %d\n", strerror(errno), + res); + return; + } + + for (int i = 0; i < res; i++) { + auto packet = getPacketFromBuffer( + reinterpret_cast( + rx_msgs_[current_position_].msg_hdr.msg_iov[0].iov_base), + rx_msgs_[current_position_].msg_len); + receiveSuccess(*packet); + receive_callback_(this, *packet, + make_error_code(forwarder_error::success)); + ++current_position_; + } + + doRecvPacket(); + } else { + TRANSPORT_LOGE( + "Error in UDP: Receiving packets from a not connected socket."); + } + } else if (ec.value() == static_cast(std::errc::operation_canceled)) { + TRANSPORT_LOGE("The connection has been closed by the application."); + return; + } else { + if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { + // receive_callback_(this, *read_msg_, ec); + TRANSPORT_LOGE("Error in UDP connector: %d %s", ec.value(), + ec.message().c_str()); + } else { + TRANSPORT_LOGE("Error while not connector"); + } + } +} +#endif + +void UdpTunnelConnector::doRecvPacket() { +#ifdef LINUX + if (state_ == State::CONNECTED) { +#if ((ASIO_VERSION / 100 % 1000) < 11) + socket_->async_receive(asio::null_buffers(), +#else + socket_->async_wait(asio::ip::tcp::socket::wait_read, +#endif + std::bind(&UdpTunnelConnector::readHandler, this, + std::placeholders::_1)); + } +#else + TRANSPORT_LOGD("UdpTunnelConnector receive packet"); + read_msg_ = getRawBuffer(); + socket_->async_receive_from( + asio::buffer(read_msg_.first, read_msg_.second), remote_endpoint_recv_, + [this](std::error_code ec, std::size_t length) { + TRANSPORT_LOGD("UdpTunnelConnector received packet length=%lu", length); + if (TRANSPORT_EXPECT_TRUE(!ec)) { + if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { + auto packet = getPacketFromBuffer(read_msg_.first, length); + receiveSuccess(*packet); + receive_callback_(this, *packet, + make_error_code(forwarder_error::success)); + doRecvPacket(); + } else { + TRANSPORT_LOGE( + "Error in UDP: Receiving packets from a not connected socket."); + } + } else if (ec.value() == + static_cast(std::errc::operation_canceled)) { + TRANSPORT_LOGE("The connection has been closed by the application."); + return; + } else { + if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { + TRANSPORT_LOGE("Error in UDP connector: %d %s", ec.value(), + ec.message().c_str()); + } else { + TRANSPORT_LOGE("Error while not connector"); + } + } + }); +#endif +} + +void UdpTunnelConnector::doConnect() { + asio::async_connect( + *socket_, endpoint_iterator_, + [this](std::error_code ec, asio::ip::udp::resolver::iterator) { + if (!ec) { + state_ = State::CONNECTED; + doRecvPacket(); + + if (data_available_) { + data_available_ = false; + doSendPacket(); + } + } else { + TRANSPORT_LOGE("[Hproxy] - UDP Connection failed!!!"); + timer_.expires_from_now(std::chrono::milliseconds(500)); + timer_.async_wait(std::bind(&UdpTunnelConnector::doConnect, this)); + } + }); +} + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/forwarder/udp_tunnel.h b/libtransport/src/io_modules/forwarder/udp_tunnel.h new file mode 100644 index 000000000..df472af91 --- /dev/null +++ b/libtransport/src/io_modules/forwarder/udp_tunnel.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +namespace transport { +namespace core { + +class UdpTunnelListener; + +class UdpTunnelConnector : public Connector { + friend class UdpTunnelListener; + + public: + template + UdpTunnelConnector(asio::io_service &io_service, + ReceiveCallback &&receive_callback, + SentCallback &&packet_sent, OnClose &&on_close_callback, + OnReconnect &&on_reconnect) + : Connector(receive_callback, packet_sent, on_close_callback, + on_reconnect), + io_service_(io_service), + strand_(std::make_shared(io_service_)), + socket_(std::make_shared(io_service_)), + resolver_(io_service_), + timer_(io_service_), +#ifdef LINUX + send_timer_(io_service_), + tx_iovecs_{0}, + tx_msgs_{0}, + rx_iovecs_{0}, + rx_msgs_{0}, + current_position_(0), +#else + read_msg_(nullptr, 0), +#endif + data_available_(false) { + } + + template + UdpTunnelConnector(std::shared_ptr &socket, + std::shared_ptr &strand, + ReceiveCallback &&receive_callback, + SentCallback &&packet_sent, OnClose &&on_close_callback, + OnReconnect &&on_reconnect, EndpointType &&remote_endpoint) + : Connector(receive_callback, packet_sent, on_close_callback, + on_reconnect), +#if ((ASIO_VERSION / 100 % 1000) < 12) + io_service_(socket->get_io_service()), +#else + io_service_((asio::io_context &)(socket->get_executor().context())), +#endif + strand_(strand), + socket_(socket), + resolver_(io_service_), + remote_endpoint_send_(std::forward(remote_endpoint)), + timer_(io_service_), +#ifdef LINUX + send_timer_(io_service_), + tx_iovecs_{0}, + tx_msgs_{0}, + rx_iovecs_{0}, + rx_msgs_{0}, + current_position_(0), +#else + read_msg_(nullptr, 0), +#endif + data_available_(false) { + if (socket_->is_open()) { + state_ = State::CONNECTED; + remote_endpoint_ = Endpoint(remote_endpoint_send_); + local_endpoint_ = socket_->local_endpoint(); + } + } + + ~UdpTunnelConnector() override; + + void send(Packet &packet) override; + + void send(const uint8_t *packet, std::size_t len) override; + + void close() override; + + void connect(const std::string &hostname, std::uint16_t port, + const std::string &bind_address = "", + std::uint16_t bind_port = 0); + + auto shared_from_this() { return utils::shared_from(this); } + + private: + void doConnect(); + void doRecvPacket(); + + void doRecvPacket(utils::MemBuf &buffer) { + receive_callback_(this, buffer, make_error_code(forwarder_error::success)); + } + +#ifdef LINUX + void readHandler(std::error_code ec); + void writeHandler(std::error_code ec); +#endif + + void setConnected() { state_ = State::CONNECTED; } + + void doSendPacket(); + void doClose(); + + private: + asio::io_service &io_service_; + std::shared_ptr strand_; + std::shared_ptr socket_; + asio::ip::udp::resolver resolver_; + asio::ip::udp::resolver::iterator endpoint_iterator_; + asio::ip::udp::endpoint remote_endpoint_send_; + asio::ip::udp::endpoint remote_endpoint_recv_; + + asio::steady_timer timer_; + +#ifdef LINUX + asio::steady_timer send_timer_; + struct iovec tx_iovecs_[max_burst][8]; + struct mmsghdr tx_msgs_[max_burst]; + struct iovec rx_iovecs_[max_burst][8]; + struct mmsghdr rx_msgs_[max_burst]; + std::uint8_t current_position_; +#else + std::pair read_msg_; +#endif + + bool data_available_; +}; + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/forwarder/udp_tunnel_listener.cc b/libtransport/src/io_modules/forwarder/udp_tunnel_listener.cc new file mode 100644 index 000000000..12246c3cf --- /dev/null +++ b/libtransport/src/io_modules/forwarder/udp_tunnel_listener.cc @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + */ + +#include +#include +#include +#include + +#ifndef LINUX +namespace std { +size_t hash::operator()( + const asio::ip::udp::endpoint &endpoint) const { + auto hash_ip = endpoint.address().is_v4() + ? endpoint.address().to_v4().to_ulong() + : utils::hash::fnv32_buf( + endpoint.address().to_v6().to_bytes().data(), 16); + uint16_t port = endpoint.port(); + return utils::hash::fnv32_buf(&port, 2, hash_ip); +} +} // namespace std +#endif + +namespace transport { +namespace core { + +UdpTunnelListener::~UdpTunnelListener() {} + +void UdpTunnelListener::close() { + strand_->post([this]() { + if (socket_->is_open()) { + socket_->close(); + } + }); +} + +#ifdef LINUX +void UdpTunnelListener::readHandler(std::error_code ec) { + TRANSPORT_LOGD("UdpTunnelConnector receive packet"); + + // TRANSPORT_LOGD("UdpTunnelConnector received packet length=%lu", length); + if (TRANSPORT_EXPECT_TRUE(!ec)) { + if (current_position_ == 0) { + for (int i = 0; i < Connector::max_burst; i++) { + auto read_buffer = Connector::getRawBuffer(); + iovecs_[i][0].iov_base = read_buffer.first; + iovecs_[i][0].iov_len = read_buffer.second; + msgs_[i].msg_hdr.msg_iov = iovecs_[i]; + msgs_[i].msg_hdr.msg_iovlen = 1; + msgs_[i].msg_hdr.msg_name = &remote_endpoints_[i]; + msgs_[i].msg_hdr.msg_namelen = sizeof(remote_endpoints_[i]); + } + } + + int res = recvmmsg(socket_->native_handle(), msgs_ + current_position_, + Connector::max_burst - current_position_, MSG_DONTWAIT, + nullptr); + if (res < 0) { + TRANSPORT_LOGE("Error in recvmmsg."); + } + + for (int i = 0; i < res; i++) { + auto packet = Connector::getPacketFromBuffer( + reinterpret_cast( + msgs_[current_position_].msg_hdr.msg_iov[0].iov_base), + msgs_[current_position_].msg_len); + auto connector_id = + utils::hash::fnv64_buf(msgs_[current_position_].msg_hdr.msg_name, + msgs_[current_position_].msg_hdr.msg_namelen); + + auto connector = connectors_.find(connector_id); + if (connector == connectors_.end()) { + // Create new connector corresponding to new client + + /* + * Get the remote endpoint for this particular message + */ + using namespace asio::ip; + if (local_endpoint_.address().is_v4()) { + auto addr = reinterpret_cast( + &remote_endpoints_[current_position_]); + address_v4::bytes_type address_bytes; + std::copy_n(reinterpret_cast(&addr->sin_addr), + address_bytes.size(), address_bytes.begin()); + address_v4 address(address_bytes); + remote_endpoint_ = udp::endpoint(address, ntohs(addr->sin_port)); + } else { + auto addr = reinterpret_cast( + &remote_endpoints_[current_position_]); + address_v6::bytes_type address_bytes; + std::copy_n(reinterpret_cast(&addr->sin6_addr), + address_bytes.size(), address_bytes.begin()); + address_v6 address(address_bytes); + remote_endpoint_ = udp::endpoint(address, ntohs(addr->sin6_port)); + } + + /** + * Create new connector sharing the same socket of this listener. + */ + auto ret = connectors_.emplace( + connector_id, + std::make_shared( + socket_, strand_, receive_callback_, + [](Connector *, const std::error_code &) {}, [](Connector *) {}, + [](Connector *) {}, std::move(remote_endpoint_))); + connector = ret.first; + connector->second->setConnectorId(connector_id); + } + + /** + * Use connector callback to process incoming message. + */ + UdpTunnelConnector *c = + dynamic_cast(connector->second.get()); + c->doRecvPacket(*packet); + + ++current_position_; + } + + doRecvPacket(); + } else if (ec.value() == static_cast(std::errc::operation_canceled)) { + TRANSPORT_LOGE("The connection has been closed by the application."); + return; + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + } +} +#endif + +void UdpTunnelListener::doRecvPacket() { +#ifdef LINUX +#if ((ASIO_VERSION / 100 % 1000) < 11) + socket_->async_receive( + asio::null_buffers(), +#else + socket_->async_wait( + asio::ip::tcp::socket::wait_read, +#endif + std::bind(&UdpTunnelListener::readHandler, this, std::placeholders::_1)); +#else + read_msg_ = Connector::getRawBuffer(); + socket_->async_receive_from( + asio::buffer(read_msg_.first, read_msg_.second), remote_endpoint_, + [this](std::error_code ec, std::size_t length) { + if (TRANSPORT_EXPECT_TRUE(!ec)) { + auto packet = Connector::getPacketFromBuffer(read_msg_.first, length); + auto connector_id = + std::hash{}(remote_endpoint_); + auto connector = connectors_.find(connector_id); + if (connector == connectors_.end()) { + // Create new connector corresponding to new client + auto ret = connectors_.emplace( + connector_id, std::make_shared( + socket_, strand_, receive_callback_, + [](Connector *, const std::error_code &) {}, + [](Connector *) {}, [](Connector *) {}, + std::move(remote_endpoint_))); + connector = ret.first; + connector->second->setConnectorId(connector_id); + } + + UdpTunnelConnector *c = + dynamic_cast(connector->second.get()); + c->doRecvPacket(*packet); + doRecvPacket(); + } else if (ec.value() == + static_cast(std::errc::operation_canceled)) { + TRANSPORT_LOGE("The connection has been closed by the application."); + return; + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + } + }); +#endif +} +} // namespace core +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/io_modules/forwarder/udp_tunnel_listener.h b/libtransport/src/io_modules/forwarder/udp_tunnel_listener.h new file mode 100644 index 000000000..0ee40a400 --- /dev/null +++ b/libtransport/src/io_modules/forwarder/udp_tunnel_listener.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace std { +template <> +struct hash { + size_t operator()(const asio::ip::udp::endpoint &endpoint) const; +}; +} // namespace std + +namespace transport { +namespace core { + +class UdpTunnelListener + : public std::enable_shared_from_this { + using PacketReceivedCallback = Connector::PacketReceivedCallback; + using EndpointId = std::pair; + + static constexpr uint16_t default_port = 5004; + + public: + using Ptr = std::shared_ptr; + + template + UdpTunnelListener(asio::io_service &io_service, + ReceiveCallback &&receive_callback, + asio::ip::udp::endpoint endpoint = asio::ip::udp::endpoint( + asio::ip::udp::v4(), default_port)) + : io_service_(io_service), + strand_(std::make_shared(io_service_)), + socket_(std::make_shared(io_service_, + endpoint.protocol())), + local_endpoint_(endpoint), + receive_callback_(std::forward(receive_callback)), +#ifndef LINUX + read_msg_(nullptr, 0) +#else + iovecs_{0}, + msgs_{0}, + current_position_(0) +#endif + { + if (endpoint.protocol() == asio::ip::udp::v6()) { + std::error_code ec; + socket_->set_option(asio::ip::v6_only(false), ec); + // Call succeeds only on dual stack systems. + } + socket_->bind(local_endpoint_); + io_service_.post(std::bind(&UdpTunnelListener::doRecvPacket, this)); + } + + ~UdpTunnelListener(); + + void close(); + + int deleteConnector(Connector *connector) { + return connectors_.erase(connector->getConnectorId()); + } + + template + void setReceiveCallback(ReceiveCallback &&callback) { + receive_callback_ = std::forward(callback); + } + + Connector *findConnector(Connector::Id connId) { + auto it = connectors_.find(connId); + if (it != connectors_.end()) { + return it->second.get(); + } + + return nullptr; + } + + private: + void doRecvPacket(); + + void readHandler(std::error_code ec); + + asio::io_service &io_service_; + std::shared_ptr strand_; + std::shared_ptr socket_; + asio::ip::udp::endpoint local_endpoint_; + asio::ip::udp::endpoint remote_endpoint_; + std::unordered_map> connectors_; + + PacketReceivedCallback receive_callback_; + +#ifdef LINUX + struct iovec iovecs_[Connector::max_burst][8]; + struct mmsghdr msgs_[Connector::max_burst]; + struct sockaddr_storage remote_endpoints_[Connector::max_burst]; + std::uint8_t current_position_; +#else + std::pair read_msg_; +#endif +}; + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/loopback/CMakeLists.txt b/libtransport/src/io_modules/loopback/CMakeLists.txt new file mode 100644 index 000000000..ac6dc8068 --- /dev/null +++ b/libtransport/src/io_modules/loopback/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright (c) 2021 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 MODULE_HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/loopback_module.h +) + +list(APPEND MODULE_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/loopback_module.cc +) + +build_module(loopback_module + SHARED + SOURCES ${MODULE_SOURCE_FILES} + DEPENDS ${DEPENDENCIES} + COMPONENT lib${LIBTRANSPORT} + INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + # LIBRARY_ROOT_DIR "vpp_plugins" + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILE_FLAGS} +) diff --git a/libtransport/src/io_modules/loopback/local_face.cc b/libtransport/src/io_modules/loopback/local_face.cc new file mode 100644 index 000000000..a59dab235 --- /dev/null +++ b/libtransport/src/io_modules/loopback/local_face.cc @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include + +namespace transport { +namespace core { + +Face::Face(Connector::PacketReceivedCallback &&receive_callback, + asio::io_service &io_service, const std::string &app_name) + : receive_callback_(std::move(receive_callback)), + io_service_(io_service), + name_(app_name) {} + +Face::Face(const Face &other) + : receive_callback_(other.receive_callback_), + io_service_(other.io_service_), + name_(other.name_) {} + +Face::Face(Face &&other) + : receive_callback_(std::move(other.receive_callback_)), + io_service_(other.io_service_), + name_(std::move(other.name_)) {} + +Face &Face::operator=(const Face &other) { + receive_callback_ = other.receive_callback_; + io_service_ = other.io_service_; + name_ = other.name_; + + return *this; +} + +Face &Face::operator=(Face &&other) { + receive_callback_ = std::move(other.receive_callback_); + io_service_ = std::move(other.io_service_); + name_ = std::move(other.name_); + + return *this; +} + +void Face::onPacket(const Packet &packet) { + TRANSPORT_LOGD("Sending content to local socket."); + + if (Packet::isInterest(packet.data())) { + rescheduleOnIoService(packet); + } else { + rescheduleOnIoService(packet); + } +} + +} // namespace core +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/io_modules/loopback/local_face.h b/libtransport/src/io_modules/loopback/local_face.h new file mode 100644 index 000000000..1cbcc2c72 --- /dev/null +++ b/libtransport/src/io_modules/loopback/local_face.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace transport { +namespace core { + +class Face { + public: + Face(Connector::PacketReceivedCallback &&receive_callback, + asio::io_service &io_service, const std::string &app_name); + + Face(const Face &other); + Face(Face &&other); + void onPacket(const Packet &packet); + Face &operator=(Face &&other); + Face &operator=(const Face &other); + + private: + template + void rescheduleOnIoService(const Packet &packet) { + auto p = core::PacketManager::getInstance().getPacket(); + p->replace(packet.data(), packet.length()); + io_service_.get().post([this, p]() mutable { + receive_callback_(nullptr, *p, make_error_code(0)); + }); + } + + Connector::PacketReceivedCallback receive_callback_; + std::reference_wrapper io_service_; + std::string name_; +}; + +} // namespace core +} // namespace transport diff --git a/libtransport/src/io_modules/loopback/loopback_module.cc b/libtransport/src/io_modules/loopback/loopback_module.cc new file mode 100644 index 000000000..0bdbf8c8e --- /dev/null +++ b/libtransport/src/io_modules/loopback/loopback_module.cc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +namespace transport { + +namespace core { + +std::vector> LoopbackModule::local_faces_; +std::atomic LoopbackModule::global_counter_(0); + +LoopbackModule::LoopbackModule() : IoModule(), local_id_(~0) {} + +LoopbackModule::~LoopbackModule() {} + +void LoopbackModule::connect(bool is_consumer) {} + +bool LoopbackModule::isConnected() { return true; } + +void LoopbackModule::send(Packet &packet) { + IoModule::send(packet); + + TRANSPORT_LOGD("LoopbackModule: sending from %u to %d", local_id_, + 1 - local_id_); + + local_faces_.at(1 - local_id_)->send(packet); +} + +void LoopbackModule::send(const uint8_t *packet, std::size_t len) { + // not supported + throw errors::NotImplementedException(); +} + +void LoopbackModule::registerRoute(const Prefix &prefix) { + // For the moment we route packets from one socket to the other. + // Next step will be to introduce a FIB + return; +} + +void LoopbackModule::closeConnection() { + local_faces_.erase(local_faces_.begin() + local_id_); +} + +void LoopbackModule::init(Connector::PacketReceivedCallback &&receive_callback, + Connector::OnReconnectCallback &&reconnect_callback, + asio::io_service &io_service, + const std::string &app_name) { + if (local_id_ == uint32_t(~0) && global_counter_ < 2) { + local_id_ = global_counter_++; + local_faces_.emplace( + local_faces_.begin() + local_id_, + new LocalConnector(io_service, std::move(receive_callback), nullptr, + nullptr, std::move(reconnect_callback))); + } +} + +void LoopbackModule::processControlMessageReply(utils::MemBuf &packet_buffer) { + return; +} + +std::uint32_t LoopbackModule::getMtu() { return interface_mtu; } + +bool LoopbackModule::isControlMessage(const uint8_t *message) { return false; } + +extern "C" IoModule *create_module(void) { return new LoopbackModule(); } + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/loopback/loopback_module.h b/libtransport/src/io_modules/loopback/loopback_module.h new file mode 100644 index 000000000..219fa8841 --- /dev/null +++ b/libtransport/src/io_modules/loopback/loopback_module.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace transport { + +namespace core { + +class LoopbackModule : public IoModule { + static constexpr std::uint16_t interface_mtu = 1500; + + public: + LoopbackModule(); + + ~LoopbackModule(); + + void connect(bool is_consumer) override; + + void send(Packet &packet) override; + void send(const uint8_t *packet, std::size_t len) override; + + bool isConnected() override; + + void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::OnReconnectCallback &&reconnect_callback, + asio::io_service &io_service, + const std::string &app_name = "Libtransport") override; + + void registerRoute(const Prefix &prefix) override; + + std::uint32_t getMtu() override; + + bool isControlMessage(const uint8_t *message) override; + + void processControlMessageReply(utils::MemBuf &packet_buffer) override; + + void closeConnection() override; + + private: + static std::vector> local_faces_; + static std::atomic global_counter_; + + private: + uint32_t local_id_; +}; + +extern "C" IoModule *create_module(void); + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/memif/CMakeLists.txt b/libtransport/src/io_modules/memif/CMakeLists.txt new file mode 100644 index 000000000..c8a930e7b --- /dev/null +++ b/libtransport/src/io_modules/memif/CMakeLists.txt @@ -0,0 +1,56 @@ +# Copyright (c) 2021 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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) + +find_package(Vpp REQUIRED) +find_package(Libmemif REQUIRED) + +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + find_package(HicnPlugin REQUIRED) + find_package(SafeVapi REQUIRED) +else() + list(APPEND DEPENDENCIES + ${SAFE_VAPI_SHARED} + ) +endif() + +list(APPEND MODULE_HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_vapi.h + ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/memif_vapi.h + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_module.h +) + +list(APPEND MODULE_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_vapi.c + ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/memif_vapi.c + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_module.cc +) + +build_module(memif_module + SHARED + SOURCES ${MODULE_SOURCE_FILES} + DEPENDS ${DEPENDENCIES} + COMPONENT lib${LIBTRANSPORT} + LINK_LIBRARIES ${LIBMEMIF_LIBRARIES} ${SAFE_VAPI_LIBRARIES} + INCLUDE_DIRS + ${LIBTRANSPORT_INCLUDE_DIRS} + ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + ${VPP_INCLUDE_DIRS} + ${LIBMEMIF_INCLUDE_DIRS} + ${SAFE_VAPI_INCLUDE_DIRS} + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILE_FLAGS} +) diff --git a/libtransport/src/io_modules/memif/hicn_vapi.c b/libtransport/src/io_modules/memif/hicn_vapi.c new file mode 100644 index 000000000..b83a36b47 --- /dev/null +++ b/libtransport/src/io_modules/memif/hicn_vapi.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#define HICN_VPP_PLUGIN +#include +#undef HICN_VPP_PLUGIN + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///////////////////////////////////////////////////// +const char *HICN_ERROR_STRING[] = { +#define _(a, b, c) c, + foreach_hicn_error +#undef _ +}; +///////////////////////////////////////////////////// + +/*********************** Missing Symbol in vpp libraries + * *************************/ +u8 *format_vl_api_address_union(u8 *s, va_list *args) { return NULL; } + +/*********************************************************************************/ + +DEFINE_VAPI_MSG_IDS_HICN_API_JSON +DEFINE_VAPI_MSG_IDS_IP_API_JSON + +static vapi_error_e register_prod_app_cb( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_hicn_api_register_prod_app_reply *reply) { + hicn_producer_output_params *output_params = + (hicn_producer_output_params *)callback_ctx; + + if (reply == NULL) return rv; + + output_params->cs_reserved = reply->cs_reserved; + output_params->prod_addr = (ip_address_t *)malloc(sizeof(ip_address_t)); + memset(output_params->prod_addr, 0, sizeof(ip_address_t)); + if (reply->prod_addr.af == ADDRESS_IP6) + memcpy(&output_params->prod_addr->v6, reply->prod_addr.un.ip6, + sizeof(ip6_address_t)); + else + memcpy(&output_params->prod_addr->v4, reply->prod_addr.un.ip4, + sizeof(ip4_address_t)); + output_params->face_id = reply->faceid; + + return reply->retval; +} + +int hicn_vapi_register_prod_app(vapi_ctx_t ctx, + hicn_producer_input_params *input_params, + hicn_producer_output_params *output_params) { + vapi_lock(); + vapi_msg_hicn_api_register_prod_app *msg = + vapi_alloc_hicn_api_register_prod_app(ctx); + + if (ip46_address_is_ip4((ip46_address_t *)&input_params->prefix->address)) { + memcpy(&msg->payload.prefix.address.un.ip4, &input_params->prefix->address, + sizeof(ip4_address_t)); + msg->payload.prefix.address.af = ADDRESS_IP4; + } else { + memcpy(&msg->payload.prefix.address.un.ip6, &input_params->prefix->address, + sizeof(ip6_address_t)); + msg->payload.prefix.address.af = ADDRESS_IP6; + } + msg->payload.prefix.len = input_params->prefix->len; + + msg->payload.swif = input_params->swif; + msg->payload.cs_reserved = input_params->cs_reserved; + + int ret = vapi_hicn_api_register_prod_app(ctx, msg, register_prod_app_cb, + output_params); + vapi_unlock(); + return ret; +} + +static vapi_error_e face_prod_del_cb( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_hicn_api_face_prod_del_reply *reply) { + if (reply == NULL) return rv; + + return reply->retval; +} + +int hicn_vapi_face_prod_del(vapi_ctx_t ctx, + hicn_del_face_app_input_params *input_params) { + vapi_lock(); + vapi_msg_hicn_api_face_prod_del *msg = vapi_alloc_hicn_api_face_prod_del(ctx); + + msg->payload.faceid = input_params->face_id; + + int ret = vapi_hicn_api_face_prod_del(ctx, msg, face_prod_del_cb, NULL); + vapi_unlock(); + return ret; +} + +static vapi_error_e register_cons_app_cb( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_hicn_api_register_cons_app_reply *reply) { + hicn_consumer_output_params *output_params = + (hicn_consumer_output_params *)callback_ctx; + + if (reply == NULL) return rv; + + output_params->src6 = (ip_address_t *)malloc(sizeof(ip_address_t)); + output_params->src4 = (ip_address_t *)malloc(sizeof(ip_address_t)); + memset(output_params->src6, 0, sizeof(ip_address_t)); + memset(output_params->src4, 0, sizeof(ip_address_t)); + memcpy(&output_params->src6->v6, &reply->src_addr6.un.ip6, + sizeof(ip6_address_t)); + memcpy(&output_params->src4->v4, &reply->src_addr4.un.ip4, + sizeof(ip4_address_t)); + + output_params->face_id1 = reply->faceid1; + output_params->face_id2 = reply->faceid2; + + return reply->retval; +} + +int hicn_vapi_register_cons_app(vapi_ctx_t ctx, + hicn_consumer_input_params *input_params, + hicn_consumer_output_params *output_params) { + vapi_lock(); + vapi_msg_hicn_api_register_cons_app *msg = + vapi_alloc_hicn_api_register_cons_app(ctx); + + msg->payload.swif = input_params->swif; + + int ret = vapi_hicn_api_register_cons_app(ctx, msg, register_cons_app_cb, + output_params); + vapi_unlock(); + return ret; +} + +static vapi_error_e face_cons_del_cb( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_hicn_api_face_cons_del_reply *reply) { + if (reply == NULL) return rv; + + return reply->retval; +} + +int hicn_vapi_face_cons_del(vapi_ctx_t ctx, + hicn_del_face_app_input_params *input_params) { + vapi_lock(); + vapi_msg_hicn_api_face_cons_del *msg = vapi_alloc_hicn_api_face_cons_del(ctx); + + msg->payload.faceid = input_params->face_id; + + int ret = vapi_hicn_api_face_cons_del(ctx, msg, face_cons_del_cb, NULL); + vapi_unlock(); + return ret; +} + +static vapi_error_e reigster_route_cb( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_ip_route_add_del_reply *reply) { + if (reply == NULL) return rv; + + return reply->retval; +} + +int hicn_vapi_register_route(vapi_ctx_t ctx, + hicn_producer_set_route_params *input_params) { + vapi_lock(); + vapi_msg_ip_route_add_del *msg = vapi_alloc_ip_route_add_del(ctx, 1); + + msg->payload.is_add = 1; + if (ip46_address_is_ip4((ip46_address_t *)(input_params->prod_addr))) { + memcpy(&msg->payload.route.prefix.address.un.ip4, + &input_params->prefix->address.v4, sizeof(ip4_address_t)); + msg->payload.route.prefix.address.af = ADDRESS_IP4; + msg->payload.route.prefix.len = input_params->prefix->len; + } else { + memcpy(&msg->payload.route.prefix.address.un.ip6, + &input_params->prefix->address.v6, sizeof(ip6_address_t)); + msg->payload.route.prefix.address.af = ADDRESS_IP6; + msg->payload.route.prefix.len = input_params->prefix->len; + } + + msg->payload.route.paths[0].sw_if_index = ~0; + msg->payload.route.paths[0].table_id = 0; + if (ip46_address_is_ip4((ip46_address_t *)(input_params->prod_addr))) { + memcpy(&(msg->payload.route.paths[0].nh.address.ip4), + input_params->prod_addr->v4.as_u8, sizeof(ip4_address_t)); + msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; + } else { + memcpy(&(msg->payload.route.paths[0].nh.address.ip6), + input_params->prod_addr->v6.as_u8, sizeof(ip6_address_t)); + msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; + } + + msg->payload.route.paths[0].type = FIB_API_PATH_FLAG_NONE; + msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; + + int ret = vapi_ip_route_add_del(ctx, msg, reigster_route_cb, NULL); + + vapi_unlock(); + return ret; +} + +char *hicn_vapi_get_error_string(int ret_val) { + return get_error_string(ret_val); +} diff --git a/libtransport/src/io_modules/memif/hicn_vapi.h b/libtransport/src/io_modules/memif/hicn_vapi.h new file mode 100644 index 000000000..e94c97749 --- /dev/null +++ b/libtransport/src/io_modules/memif/hicn_vapi.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "stdint.h" + +typedef struct { + ip_prefix_t* 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 face_id; +} hicn_del_face_app_input_params; + +typedef struct { + uint32_t cs_reserved; + ip_address_t* prod_addr; + uint32_t face_id; +} hicn_producer_output_params; + +typedef struct { + ip_address_t* src4; + ip_address_t* src6; + uint32_t face_id1; + uint32_t face_id2; +} hicn_consumer_output_params; + +typedef struct { + ip_prefix_t* prefix; + ip_address_t* prod_addr; +} hicn_producer_set_route_params; + +int hicn_vapi_register_prod_app(vapi_ctx_t ctx, + hicn_producer_input_params* input_params, + hicn_producer_output_params* output_params); + +int hicn_vapi_register_cons_app(vapi_ctx_t ctx, + hicn_consumer_input_params* input_params, + hicn_consumer_output_params* output_params); + +int hicn_vapi_register_route(vapi_ctx_t ctx, + hicn_producer_set_route_params* input_params); + +int hicn_vapi_face_cons_del(vapi_ctx_t ctx, + hicn_del_face_app_input_params* input_params); + +int hicn_vapi_face_prod_del(vapi_ctx_t ctx, + hicn_del_face_app_input_params* input_params); + +char* hicn_vapi_get_error_string(int ret_val); + +#ifdef __cplusplus +} +#endif diff --git a/libtransport/src/io_modules/memif/memif_connector.cc b/libtransport/src/io_modules/memif/memif_connector.cc new file mode 100644 index 000000000..4a688d68f --- /dev/null +++ b/libtransport/src/io_modules/memif/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 +#include +#include + +#include + +extern "C" { +#include +}; + +#define CANCEL_TIMER 1 + +namespace transport { + +namespace core { + +struct memif_connection { + uint16_t index; + /* memif conenction handle */ + memif_conn_handle_t conn; + /* transmit queue id */ + uint16_t tx_qid; + /* tx buffers */ + memif_buffer_t *tx_bufs; + /* allocated tx buffers counter */ + /* number of tx buffers pointing to shared memory */ + uint16_t tx_buf_num; + /* rx buffers */ + memif_buffer_t *rx_bufs; + /* allcoated rx buffers counter */ + /* number of rx buffers pointing to shared memory */ + uint16_t rx_buf_num; + /* interface ip address */ + uint8_t ip_addr[4]; +}; + +std::once_flag MemifConnector::flag_; +utils::EpollEventReactor MemifConnector::main_event_reactor_; + +MemifConnector::MemifConnector(PacketReceivedCallback &&receive_callback, + PacketSentCallback &&packet_sent, + OnCloseCallback &&close_callback, + OnReconnectCallback &&on_reconnect, + asio::io_service &io_service, + std::string app_name) + : Connector(std::move(receive_callback), std::move(packet_sent), + std::move(close_callback), std::move(on_reconnect)), + memif_worker_(nullptr), + timer_set_(false), + send_timer_(std::make_unique(event_reactor_)), + disconnect_timer_( + std::make_unique(event_reactor_)), + io_service_(io_service), + memif_connection_(std::make_unique()), + tx_buf_counter_(0), + is_reconnection_(false), + data_available_(false), + app_name_(app_name), + socket_filename_("") { + std::call_once(MemifConnector::flag_, &MemifConnector::init, this); +} + +MemifConnector::~MemifConnector() { close(); } + +void MemifConnector::init() { + /* initialize memory interface */ + int err = memif_init(controlFdUpdate, const_cast(app_name_.c_str()), + nullptr, nullptr, nullptr); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGE("memif_init: %s", memif_strerror(err)); + } +} + +void MemifConnector::connect(uint32_t memif_id, long memif_mode) { + state_ = State::CONNECTING; + + memif_id_ = memif_id; + socket_filename_ = "/run/vpp/memif.sock"; + + createMemif(memif_id, memif_mode, nullptr); + + work_ = std::make_unique(io_service_); + + while (state_ != State::CONNECTED) { + MemifConnector::main_event_reactor_.runOneEvent(); + } + + int err; + + /* get interrupt queue id */ + int fd = -1; + err = memif_get_queue_efd(memif_connection_->conn, 0, &fd); + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGE("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::bind(&MemifConnector::threadMain, this)); +} + +int MemifConnector::createMemif(uint32_t index, uint8_t mode, char *s) { + memif_connection_t *c = memif_connection_.get(); + + /* setting memif connection arguments */ + memif_conn_args_t args; + memset(&args, 0, sizeof(args)); + + args.is_master = mode; + args.log2_ring_size = MEMIF_LOG2_RING_SIZE; + args.buffer_size = MEMIF_BUF_SIZE; + args.num_s2m_rings = 1; + args.num_m2s_rings = 1; + strncpy((char *)args.interface_name, IF_NAME, strlen(IF_NAME) + 1); + args.mode = memif_interface_mode_t::MEMIF_INTERFACE_MODE_IP; + + int err; + + err = memif_create_socket(&args.socket, socket_filename_.c_str(), nullptr); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + throw errors::RuntimeException(memif_strerror(err)); + } + + args.interface_id = index; + /* last argument for memif_create (void * private_ctx) is used by user + to identify connection. this context is returned with callbacks */ + + /* default interrupt */ + if (s == nullptr) { + err = memif_create(&c->conn, &args, onConnect, onDisconnect, onInterrupt, + this); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + throw errors::RuntimeException(memif_strerror(err)); + } + } + + c->index = (uint16_t)index; + c->tx_qid = 0; + /* alloc memif buffers */ + c->rx_buf_num = 0; + c->rx_bufs = static_cast( + malloc(sizeof(memif_buffer_t) * MAX_MEMIF_BUFS)); + c->tx_buf_num = 0; + c->tx_bufs = static_cast( + malloc(sizeof(memif_buffer_t) * MAX_MEMIF_BUFS)); + + // memif_set_rx_mode (c->conn, MEMIF_RX_MODE_POLLING, 0); + + return 0; +} + +int MemifConnector::deleteMemif() { + memif_connection_t *c = memif_connection_.get(); + + if (c->rx_bufs) { + free(c->rx_bufs); + } + + c->rx_bufs = nullptr; + c->rx_buf_num = 0; + + if (c->tx_bufs) { + free(c->tx_bufs); + } + + c->tx_bufs = nullptr; + c->tx_buf_num = 0; + + int err; + /* disconenct then delete memif connection */ + err = memif_delete(&c->conn); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGE("memif_delete: %s", memif_strerror(err)); + } + + if (TRANSPORT_EXPECT_FALSE(c->conn != nullptr)) { + TRANSPORT_LOGE("memif delete fail"); + } + + return 0; +} + +int MemifConnector::controlFdUpdate(int fd, uint8_t events, void *private_ctx) { + /* convert memif event definitions to epoll events */ + if (events & MEMIF_FD_EVENT_DEL) { + return MemifConnector::main_event_reactor_.delFileDescriptor(fd); + } + + uint32_t evt = 0; + + if (events & MEMIF_FD_EVENT_READ) { + evt |= EPOLLIN; + } + + if (events & MEMIF_FD_EVENT_WRITE) { + evt |= EPOLLOUT; + } + + if (events & MEMIF_FD_EVENT_MOD) { + return MemifConnector::main_event_reactor_.modFileDescriptor(fd, evt); + } + + return MemifConnector::main_event_reactor_.addFileDescriptor( + fd, evt, [](const utils::Event &evt) -> int { + uint32_t event = 0; + int memif_err = 0; + + if (evt.events & EPOLLIN) { + event |= MEMIF_FD_EVENT_READ; + } + + if (evt.events & EPOLLOUT) { + event |= MEMIF_FD_EVENT_WRITE; + } + + if (evt.events & EPOLLERR) { + event |= MEMIF_FD_EVENT_ERROR; + } + + memif_err = memif_control_fd_handler(evt.data.fd, event); + + if (TRANSPORT_EXPECT_FALSE(memif_err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGE("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_.get(); + int err; + uint16_t r; + /* set data pointer to shared memory and set buffer_len to shared mmeory + * buffer len */ + err = memif_buffer_alloc(c->conn, qid, c->tx_bufs, n, &r, 2000); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGE("memif_buffer_alloc: %s", memif_strerror(err)); + return -1; + } + + c->tx_buf_num += r; + return r; +} + +int MemifConnector::txBurst(uint16_t qid) { + memif_connection_t *c = memif_connection_.get(); + int err; + uint16_t r; + /* inform peer memif interface about data in shared memory buffers */ + /* mark memif buffers as free */ + err = memif_tx_burst(c->conn, qid, c->tx_bufs, c->tx_buf_num, &r); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGE("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_LOGE("memif_tx_burst: %s", memif_strerror(err)); + c->tx_buf_num -= r; + return -1; + } + + c->tx_buf_num -= r; + return 0; +} + +void MemifConnector::sendCallback(const std::error_code &ec) { + timer_set_ = false; + + if (TRANSPORT_EXPECT_TRUE(!ec && state_ == State::CONNECTED)) { + doSend(); + } +} + +void MemifConnector::processInputBuffer(std::uint16_t total_packets) { + utils::MemBuf::Ptr ptr; + + for (; total_packets > 0; total_packets--) { + if (input_buffer_.pop(ptr)) { + receive_callback_(this, *ptr, std::make_error_code(std::errc(0))); + } + } +} + +/* informs user about connected status. private_ctx is used by user to identify + connection (multiple connections WIP) */ +int MemifConnector::onConnect(memif_conn_handle_t conn, void *private_ctx) { + MemifConnector *connector = (MemifConnector *)private_ctx; + connector->state_ = State::CONNECTED; + memif_refill_queue(conn, 0, -1, 0); + + return 0; +} + +/* informs user about disconnected status. private_ctx is used by user to + identify connection (multiple connections WIP) */ +int MemifConnector::onDisconnect(memif_conn_handle_t conn, void *private_ctx) { + MemifConnector *connector = (MemifConnector *)private_ctx; + connector->state_ = State::CLOSED; + return 0; +} + +void MemifConnector::threadMain() { event_reactor_.runEventLoop(1000); } + +int MemifConnector::onInterrupt(memif_conn_handle_t conn, void *private_ctx, + uint16_t qid) { + MemifConnector *connector = (MemifConnector *)private_ctx; + + memif_connection_t *c = connector->memif_connection_.get(); + int err = MEMIF_ERR_SUCCESS, ret_val; + uint16_t total_packets = 0; + uint16_t rx; + + do { + err = memif_rx_burst(conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx); + ret_val = err; + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS && + err != MEMIF_ERR_NOBUF)) { + TRANSPORT_LOGE("memif_rx_burst: %s", memif_strerror(err)); + goto error; + } + + c->rx_buf_num += rx; + + if (TRANSPORT_EXPECT_FALSE(connector->io_service_.stopped())) { + TRANSPORT_LOGE("socket stopped: ignoring %u packets", rx); + goto error; + } + + std::size_t packet_length; + for (int i = 0; i < rx; i++) { + auto buffer = connector->getRawBuffer(); + packet_length = (c->rx_bufs + i)->len; + std::memcpy(buffer.first, (c->rx_bufs + i)->data, packet_length); + auto packet = connector->getPacketFromBuffer(buffer.first, packet_length); + + if (!connector->input_buffer_.push(std::move(packet))) { + TRANSPORT_LOGE("Error pushing packet. Ring buffer full."); + + // TODO Here we should consider the possibility to signal the congestion + // to the application, that would react properly (e.g. slow down + // message) + } + } + + /* 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_LOGE("memif_buffer_free: %s", memif_strerror(err)); + } + + c->rx_buf_num -= rx; + total_packets += rx; + + } while (ret_val == MEMIF_ERR_NOBUF); + + connector->io_service_.post( + std::bind(&MemifConnector::processInputBuffer, connector, total_packets)); + + return 0; + +error: + err = memif_refill_queue(c->conn, qid, rx, 0); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGE("memif_buffer_free: %s", memif_strerror(err)); + } + c->rx_buf_num -= rx; + + return 0; +} + +void MemifConnector::close() { + if (state_ != State::CLOSED) { + disconnect_timer_->expiresFromNow(std::chrono::microseconds(50)); + disconnect_timer_->asyncWait([this](const std::error_code &ec) { + deleteMemif(); + event_reactor_.stop(); + work_.reset(); + }); + + if (memif_worker_ && memif_worker_->joinable()) { + memif_worker_->join(); + } + } +} + +void MemifConnector::send(Packet &packet) { + { + utils::SpinLock::Acquire locked(write_msgs_lock_); + output_buffer_.push_back(packet.shared_from_this()); + } +#if CANCEL_TIMER + if (!timer_set_) { + timer_set_ = true; + send_timer_->expiresFromNow(std::chrono::microseconds(50)); + send_timer_->asyncWait( + std::bind(&MemifConnector::sendCallback, this, std::placeholders::_1)); + } +#endif +} + +int MemifConnector::doSend() { + std::size_t max = 0; + int32_t n = 0; + std::size_t size = 0; + + { + utils::SpinLock::Acquire locked(write_msgs_lock_); + size = output_buffer_.size(); + } + + do { + max = size < MAX_MEMIF_BUFS ? size : MAX_MEMIF_BUFS; + n = bufferAlloc(max, memif_connection_->tx_qid); + + if (TRANSPORT_EXPECT_FALSE(n < 0)) { + TRANSPORT_LOGE("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(memif_connection_->tx_bufs[i].data); + do { + std::memcpy(shared_buffer + offset, current->data(), current->length()); + offset += current->length(); + current = current->next(); + } while (current != packet); + + memif_connection_->tx_bufs[i].len = uint32_t(offset); + + output_buffer_.pop_front(); + } + + txBurst(memif_connection_->tx_qid); + + utils::SpinLock::Acquire locked(write_msgs_lock_); + size = output_buffer_.size(); + } while (size > 0); + + return 0; +} + +void MemifConnector::send(const uint8_t *packet, std::size_t len) { + throw errors::NotImplementedException(); +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/io_modules/memif/memif_connector.h b/libtransport/src/io_modules/memif/memif_connector.h new file mode 100644 index 000000000..bed3516dc --- /dev/null +++ b/libtransport/src/io_modules/memif/memif_connector.h @@ -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. + */ + +#pragma once + +#include +#include +#include +#include +//#include +#include +#include + +#include +#include +#include +#include + +#define _Static_assert static_assert + +namespace transport { + +namespace core { + +typedef struct memif_connection memif_connection_t; + +#define APP_NAME "libtransport" +#define IF_NAME "vpp_connection" + +#define MEMIF_BUF_SIZE 2048 +#define MEMIF_LOG2_RING_SIZE 13 +#define MAX_MEMIF_BUFS (1 << MEMIF_LOG2_RING_SIZE) + +class MemifConnector : public Connector { + using memif_conn_handle_t = void *; + using PacketRing = utils::CircularFifo; + + public: + MemifConnector(PacketReceivedCallback &&receive_callback, + PacketSentCallback &&packet_sent, + OnCloseCallback &&close_callback, + OnReconnectCallback &&on_reconnect, + asio::io_service &io_service, + std::string app_name = "Libtransport"); + + ~MemifConnector() override; + + void send(Packet &packet) override; + + void send(const uint8_t *packet, std::size_t len) override; + + void close() override; + + void connect(uint32_t memif_id, long memif_mode); + + 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, void *private_ctx); + + 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(std::uint16_t total_packets); + + private: + static utils::EpollEventReactor main_event_reactor_; + static std::unique_ptr main_worker_; + + int epfd; + std::unique_ptr memif_worker_; + utils::EpollEventReactor event_reactor_; + std::atomic_bool timer_set_; + std::unique_ptr send_timer_; + std::unique_ptr disconnect_timer_; + asio::io_service &io_service_; + std::unique_ptr work_; + std::unique_ptr memif_connection_; + uint16_t tx_buf_counter_; + + PacketRing input_buffer_; + bool is_reconnection_; + bool data_available_; + uint32_t memif_id_; + uint8_t memif_mode_; + std::string app_name_; + uint16_t transmission_index_; + utils::SpinLock write_msgs_lock_; + std::string socket_filename_; + + static std::once_flag flag_; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/io_modules/memif/memif_vapi.c b/libtransport/src/io_modules/memif/memif_vapi.c new file mode 100644 index 000000000..b3da2b012 --- /dev/null +++ b/libtransport/src/io_modules/memif/memif_vapi.c @@ -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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DEFINE_VAPI_MSG_IDS_MEMIF_API_JSON + +static vapi_error_e memif_details_cb(vapi_ctx_t ctx, void *callback_ctx, + vapi_error_e rv, bool is_last, + vapi_payload_memif_details *reply) { + uint32_t *last_memif_id = (uint32_t *)callback_ctx; + uint32_t current_memif_id = 0; + if (reply != NULL) { + current_memif_id = reply->id; + } else { + return rv; + } + + if (current_memif_id >= *last_memif_id) { + *last_memif_id = current_memif_id + 1; + } + + return rv; +} + +int memif_vapi_get_next_memif_id(vapi_ctx_t ctx, uint32_t *memif_id) { + vapi_lock(); + vapi_msg_memif_dump *msg = vapi_alloc_memif_dump(ctx); + int ret = vapi_memif_dump(ctx, msg, memif_details_cb, memif_id); + vapi_unlock(); + return ret; +} + +static vapi_error_e memif_create_cb(vapi_ctx_t ctx, void *callback_ctx, + vapi_error_e rv, bool is_last, + vapi_payload_memif_create_reply *reply) { + memif_output_params_t *output_params = (memif_output_params_t *)callback_ctx; + + if (reply == NULL) return rv; + + output_params->sw_if_index = reply->sw_if_index; + + return rv; +} + +int memif_vapi_create_memif(vapi_ctx_t ctx, memif_create_params_t *input_params, + memif_output_params_t *output_params) { + vapi_lock(); + vapi_msg_memif_create *msg = vapi_alloc_memif_create(ctx); + + int ret = 0; + if (input_params->socket_id == ~0) { + // invalid socket-id + ret = -1; + goto END; + } + + if (!is_pow2(input_params->ring_size)) { + // ring size must be power of 2 + ret = -1; + goto END; + } + + if (input_params->rx_queues > 255 || input_params->rx_queues < 1) { + // rx queue must be between 1 - 255 + ret = -1; + goto END; + } + + if (input_params->tx_queues > 255 || input_params->tx_queues < 1) { + // tx queue must be between 1 - 255 + ret = -1; + goto END; + } + + msg->payload.role = input_params->role; + msg->payload.mode = input_params->mode; + msg->payload.rx_queues = input_params->rx_queues; + msg->payload.tx_queues = input_params->tx_queues; + msg->payload.id = input_params->id; + msg->payload.socket_id = input_params->socket_id; + msg->payload.ring_size = input_params->ring_size; + msg->payload.buffer_size = input_params->buffer_size; + + ret = vapi_memif_create(ctx, msg, memif_create_cb, output_params); +END: + vapi_unlock(); + return ret; +} + +static vapi_error_e memif_delete_cb(vapi_ctx_t ctx, void *callback_ctx, + vapi_error_e rv, bool is_last, + vapi_payload_memif_delete_reply *reply) { + if (reply == NULL) return rv; + + return reply->retval; +} + +int memif_vapi_delete_memif(vapi_ctx_t ctx, uint32_t sw_if_index) { + vapi_lock(); + vapi_msg_memif_delete *msg = vapi_alloc_memif_delete(ctx); + + msg->payload.sw_if_index = sw_if_index; + + int ret = vapi_memif_delete(ctx, msg, memif_delete_cb, NULL); + vapi_unlock(); + return ret; +} diff --git a/libtransport/src/io_modules/memif/memif_vapi.h b/libtransport/src/io_modules/memif/memif_vapi.h new file mode 100644 index 000000000..bcf06ed43 --- /dev/null +++ b/libtransport/src/io_modules/memif/memif_vapi.h @@ -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. + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#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; + +int memif_vapi_get_next_memif_id(vapi_ctx_t ctx, uint32_t *memif_id); + +int memif_vapi_create_memif(vapi_ctx_t ctx, memif_create_params_t *input_params, + memif_output_params_t *output_params); + +int memif_vapi_delete_memif(vapi_ctx_t ctx, uint32_t sw_if_index); + +#ifdef __cplusplus +} +#endif diff --git a/libtransport/src/io_modules/memif/vpp_forwarder_module.cc b/libtransport/src/io_modules/memif/vpp_forwarder_module.cc new file mode 100644 index 000000000..dcbcd7ed0 --- /dev/null +++ b/libtransport/src/io_modules/memif/vpp_forwarder_module.cc @@ -0,0 +1,263 @@ +/* + * 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 +#include +#include +#include +#include +#include + +extern "C" { +#include +}; + +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 { + +VPPForwarderModule::VPPForwarderModule() + : IoModule(), + connector_(nullptr), + sw_if_index_(~0), + face_id1_(~0), + face_id2_(~0), + is_consumer_(false) {} + +VPPForwarderModule::~VPPForwarderModule() { delete connector_; } + +void VPPForwarderModule::init( + Connector::PacketReceivedCallback &&receive_callback, + Connector::OnReconnectCallback &&reconnect_callback, + asio::io_service &io_service, const std::string &app_name) { + if (!connector_) { + connector_ = + new MemifConnector(std::move(receive_callback), 0, 0, + std::move(reconnect_callback), io_service, app_name); + } +} + +void VPPForwarderModule::processControlMessageReply( + utils::MemBuf &packet_buffer) { + throw errors::NotImplementedException(); +} + +bool VPPForwarderModule::isControlMessage(const uint8_t *message) { + return false; +} + +bool VPPForwarderModule::isConnected() { return connector_->isConnected(); }; + +void VPPForwarderModule::send(Packet &packet) { + IoModule::send(packet); + connector_->send(packet); +} + +void VPPForwarderModule::send(const uint8_t *packet, std::size_t len) { + counters_.tx_packets++; + counters_.tx_bytes += len; + + // Perfect forwarding + connector_->send(packet, len); +} + +std::uint32_t VPPForwarderModule::getMtu() { return interface_mtu; } + +/** + * @brief Create a memif interface in the local VPP forwarder. + */ +uint32_t VPPForwarderModule::getMemifConfiguration() { + memif_create_params_t input_params = {0}; + + int ret = memif_vapi_get_next_memif_id(VPPForwarderModule::sock_, &memif_id_); + + if (ret < 0) { + throw errors::RuntimeException( + "Error getting next memif id. Could not create memif interface."); + } + + 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}; + + ret = memif_vapi_create_memif(VPPForwarderModule::sock_, &input_params, + &output_params); + + if (ret < 0) { + throw errors::RuntimeException( + "Error creating memif interface in the local VPP forwarder."); + } + + return output_params.sw_if_index; +} + +void VPPForwarderModule::consumerConnection() { + hicn_consumer_input_params input = {0}; + hicn_consumer_output_params output = {0}; + ip_address_t ip4_address; + ip_address_t ip6_address; + + output.src4 = &ip4_address; + output.src6 = &ip6_address; + input.swif = sw_if_index_; + + int ret = + hicn_vapi_register_cons_app(VPPForwarderModule::sock_, &input, &output); + + if (ret < 0) { + throw errors::RuntimeException(hicn_vapi_get_error_string(ret)); + } + + face_id1_ = output.face_id1; + face_id2_ = output.face_id2; + + std::memcpy(inet_address_.v4.as_u8, output.src4->v4.as_u8, IPV4_ADDR_LEN); + + std::memcpy(inet6_address_.v6.as_u8, output.src6->v6.as_u8, IPV6_ADDR_LEN); +} + +void VPPForwarderModule::producerConnection() { + // Producer connection will be set when we set the first route. +} + +void VPPForwarderModule::connect(bool is_consumer) { + int retry = 20; + + TRANSPORT_LOGI("Connecting to VPP through vapi."); + vapi_error_e ret = vapi_connect_safe(&sock_, 0); + + while (ret != VAPI_OK && retry > 0) { + TRANSPORT_LOGE("Error connecting to VPP through vapi. Retrying.."); + --retry; + ret = vapi_connect_safe(&sock_, 0); + } + + if (ret != VAPI_OK) { + throw std::runtime_error( + "Impossible to connect to forwarder. Is VPP running?"); + } + + TRANSPORT_LOGI("Connected to VPP through vapi."); + + sw_if_index_ = getMemifConfiguration(); + + is_consumer_ = is_consumer; + if (is_consumer_) { + consumerConnection(); + } + + connector_->connect(memif_id_, 0); + connector_->setRole(is_consumer_ ? Connector::Role::CONSUMER + : Connector::Role::PRODUCER); +} + +void VPPForwarderModule::registerRoute(const Prefix &prefix) { + const ip_prefix_t &addr = prefix.toIpPrefixStruct(); + + ip_prefix_t producer_prefix; + ip_address_t producer_locator; + + if (face_id1_ == 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)); + + input.prefix = &producer_prefix; + output.prod_addr = &producer_locator; + + // Here we have to ask to the actual connector what is the + // memif_id, since this function should be called after the + // memif creation.n + input.swif = sw_if_index_; + input.prefix->address = addr.address; + input.prefix->family = addr.family; + input.prefix->len = addr.len; + input.cs_reserved = content_store_reserved_; + + int ret = + hicn_vapi_register_prod_app(VPPForwarderModule::sock_, &input, &output); + + if (ret < 0) { + throw errors::RuntimeException(hicn_vapi_get_error_string(ret)); + } + + inet6_address_ = *output.prod_addr; + + face_id1_ = output.face_id; + } else { + hicn_producer_set_route_params params; + params.prefix = &producer_prefix; + params.prefix->address = addr.address; + params.prefix->family = addr.family; + params.prefix->len = addr.len; + params.prod_addr = &producer_locator; + + int ret = hicn_vapi_register_route(VPPForwarderModule::sock_, ¶ms); + + if (ret < 0) { + throw errors::RuntimeException(hicn_vapi_get_error_string(ret)); + } + } +} + +void VPPForwarderModule::closeConnection() { + if (VPPForwarderModule::sock_) { + connector_->close(); + + if (is_consumer_) { + hicn_del_face_app_input_params params; + params.face_id = face_id1_; + hicn_vapi_face_cons_del(VPPForwarderModule::sock_, ¶ms); + params.face_id = face_id2_; + hicn_vapi_face_cons_del(VPPForwarderModule::sock_, ¶ms); + } else { + hicn_del_face_app_input_params params; + params.face_id = face_id1_; + hicn_vapi_face_prod_del(VPPForwarderModule::sock_, ¶ms); + } + + if (sw_if_index_ != uint32_t(~0)) { + int ret = + memif_vapi_delete_memif(VPPForwarderModule::sock_, sw_if_index_); + if (ret < 0) { + TRANSPORT_LOGE("Error deleting memif with sw idx %u.", sw_if_index_); + } + } + + vapi_disconnect_safe(); + VPPForwarderModule::sock_ = nullptr; + } +} + +extern "C" IoModule *create_module(void) { return new VPPForwarderModule(); } + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/memif/vpp_forwarder_module.h b/libtransport/src/io_modules/memif/vpp_forwarder_module.h new file mode 100644 index 000000000..8c4114fed --- /dev/null +++ b/libtransport/src/io_modules/memif/vpp_forwarder_module.h @@ -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. + */ + +#pragma once + +#include +#include + +#ifdef always_inline +#undef always_inline +#endif +extern "C" { +#include +}; + +namespace transport { + +namespace core { + +class MemifConnector; + +class VPPForwarderModule : public IoModule { + static constexpr std::uint16_t interface_mtu = 1500; + + public: + VPPForwarderModule(); + ~VPPForwarderModule(); + + void connect(bool is_consumer) override; + + void send(Packet &packet) override; + void send(const uint8_t *packet, std::size_t len) override; + + bool isConnected() override; + + void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::OnReconnectCallback &&reconnect_callback, + asio::io_service &io_service, + const std::string &app_name = "Libtransport") override; + + void registerRoute(const Prefix &prefix) override; + + std::uint32_t getMtu() override; + + bool isControlMessage(const uint8_t *message) override; + + void processControlMessageReply(utils::MemBuf &packet_buffer) override; + + void closeConnection() override; + + private: + uint32_t getMemifConfiguration(); + void consumerConnection(); + void producerConnection(); + + private: + MemifConnector *connector_; + uint32_t memif_id_; + uint32_t sw_if_index_; + // A consumer socket in vpp has two faces (ipv4 and ipv6) + uint32_t face_id1_; + uint32_t face_id2_; + bool is_consumer_; + vapi_ctx_t sock_; +}; + +extern "C" IoModule *create_module(void); + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/raw_socket/raw_socket_connector.cc b/libtransport/src/io_modules/raw_socket/raw_socket_connector.cc new file mode 100644 index 000000000..0bfcc2a58 --- /dev/null +++ b/libtransport/src/io_modules/raw_socket/raw_socket_connector.cc @@ -0,0 +1,201 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define MY_DEST_MAC0 0x0a +#define MY_DEST_MAC1 0x7b +#define MY_DEST_MAC2 0x7c +#define MY_DEST_MAC3 0x1c +#define MY_DEST_MAC4 0x4a +#define MY_DEST_MAC5 0x14 + +namespace transport { + +namespace core { + +RawSocketConnector::RawSocketConnector( + PacketReceivedCallback &&receive_callback, + OnReconnect &&on_reconnect_callback, asio::io_service &io_service, + std::string app_name) + : Connector(std::move(receive_callback), std::move(on_reconnect_callback)), + io_service_(io_service), + socket_(io_service_, raw_protocol(PF_PACKET, SOCK_RAW)), + // resolver_(io_service_), + timer_(io_service_), + read_msg_(packet_pool_.makePtr(nullptr)), + data_available_(false), + app_name_(app_name) { + memset(&link_layer_address_, 0, sizeof(link_layer_address_)); +} + +RawSocketConnector::~RawSocketConnector() {} + +void RawSocketConnector::connect(const std::string &interface_name, + const std::string &mac_address_str) { + state_ = ConnectorState::CONNECTING; + memset(ðernet_header_, 0, sizeof(ethernet_header_)); + struct ifreq ifr; + struct ifreq if_mac; + uint8_t mac_address[6]; + + utils::convertStringToMacAddress(mac_address_str, mac_address); + + // Get interface mac address + int fd = static_cast(socket_.native_handle()); + + /* Get the index of the interface to send on */ + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, interface_name.c_str(), interface_name.size()); + + // if (ioctl(fd, SIOCGIFINDEX, &if_idx) < 0) { + // perror("SIOCGIFINDEX"); + // } + + /* Get the MAC address of the interface to send on */ + memset(&if_mac, 0, sizeof(struct ifreq)); + strncpy(if_mac.ifr_name, interface_name.c_str(), interface_name.size()); + if (ioctl(fd, SIOCGIFHWADDR, &if_mac) < 0) { + perror("SIOCGIFHWADDR"); + throw errors::RuntimeException("Interface does not exist"); + } + + /* Ethernet header */ + for (int i = 0; i < 6; i++) { + ethernet_header_.ether_shost[i] = + ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[i]; + ethernet_header_.ether_dhost[i] = mac_address[i]; + } + + /* Ethertype field */ + ethernet_header_.ether_type = htons(ETH_P_IPV6); + + strcpy(ifr.ifr_name, interface_name.c_str()); + + if (0 == ioctl(fd, SIOCGIFHWADDR, &ifr)) { + memcpy(link_layer_address_.sll_addr, ifr.ifr_hwaddr.sa_data, 6); + } + + // memset(&ifr, 0, sizeof(ifr)); + // ioctl(fd, SIOCGIFFLAGS, &ifr); + // ifr.ifr_flags |= IFF_PROMISC; + // ioctl(fd, SIOCSIFFLAGS, &ifr); + + link_layer_address_.sll_family = AF_PACKET; + link_layer_address_.sll_protocol = htons(ETH_P_ALL); + link_layer_address_.sll_ifindex = if_nametoindex(interface_name.c_str()); + link_layer_address_.sll_hatype = 1; + link_layer_address_.sll_halen = 6; + + // startConnectionTimer(); + doConnect(); + doRecvPacket(); +} + +void RawSocketConnector::send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent) { + if (packet_sent != 0) { + socket_.async_send( + asio::buffer(packet, len), + [packet_sent](std::error_code ec, std::size_t /*length*/) { + packet_sent(); + }); + } else { + if (state_ == ConnectorState::CONNECTED) { + socket_.send(asio::buffer(packet, len)); + } + } +} + +void RawSocketConnector::send(const Packet::MemBufPtr &packet) { + io_service_.post([this, packet]() { + bool write_in_progress = !output_buffer_.empty(); + output_buffer_.push_back(std::move(packet)); + if (TRANSPORT_EXPECT_TRUE(state_ == ConnectorState::CONNECTED)) { + if (!write_in_progress) { + doSendPacket(); + } else { + // Tell the handle connect it has data to write + data_available_ = true; + } + } + }); +} + +void RawSocketConnector::close() { + io_service_.post([this]() { socket_.close(); }); +} + +void RawSocketConnector::doSendPacket() { + auto packet = output_buffer_.front().get(); + auto array = std::vector(); + + const utils::MemBuf *current = packet; + do { + array.push_back(asio::const_buffer(current->data(), current->length())); + current = current->next(); + } while (current != packet); + + socket_.async_send( + std::move(array), + [this /*, packet*/](std::error_code ec, std::size_t bytes_transferred) { + if (TRANSPORT_EXPECT_TRUE(!ec)) { + output_buffer_.pop_front(); + if (!output_buffer_.empty()) { + doSendPacket(); + } + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + } + }); +} + +void RawSocketConnector::doRecvPacket() { + read_msg_ = getPacket(); + socket_.async_receive( + asio::buffer(read_msg_->writableData(), packet_size), + [this](std::error_code ec, std::size_t bytes_transferred) mutable { + if (!ec) { + // Ignore packets that are not for us + uint8_t *dst_mac_address = const_cast(read_msg_->data()); + if (!std::memcmp(dst_mac_address, ethernet_header_.ether_shost, + ETHER_ADDR_LEN)) { + read_msg_->append(bytes_transferred); + read_msg_->trimStart(sizeof(struct ether_header)); + receive_callback_(std::move(read_msg_)); + } + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + } + doRecvPacket(); + }); +} + +void RawSocketConnector::doConnect() { + state_ = ConnectorState::CONNECTED; + socket_.bind(raw_endpoint(&link_layer_address_, sizeof(link_layer_address_))); +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/io_modules/raw_socket/raw_socket_connector.h b/libtransport/src/io_modules/raw_socket/raw_socket_connector.h new file mode 100644 index 000000000..aba4b1105 --- /dev/null +++ b/libtransport/src/io_modules/raw_socket/raw_socket_connector.h @@ -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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace transport { + +namespace core { + +using asio::generic::raw_protocol; +using raw_endpoint = asio::generic::basic_endpoint; + +class RawSocketConnector : public Connector { + public: + RawSocketConnector(PacketReceivedCallback &&receive_callback, + OnReconnect &&reconnect_callback, + asio::io_service &io_service, + std::string app_name = "Libtransport"); + + ~RawSocketConnector() override; + + void send(const Packet::MemBufPtr &packet) override; + + void send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent = 0) override; + + void close() override; + + void connect(const std::string &interface_name, + const std::string &mac_address_str); + + private: + void doConnect(); + + void doRecvPacket(); + + void doSendPacket(); + + private: + asio::io_service &io_service_; + raw_protocol::socket socket_; + + struct ether_header ethernet_header_; + + struct sockaddr_ll link_layer_address_; + + asio::steady_timer timer_; + + utils::ObjectPool::Ptr read_msg_; + + bool data_available_; + std::string app_name_; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/io_modules/raw_socket/raw_socket_interface.cc b/libtransport/src/io_modules/raw_socket/raw_socket_interface.cc new file mode 100644 index 000000000..dcf489f59 --- /dev/null +++ b/libtransport/src/io_modules/raw_socket/raw_socket_interface.cc @@ -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. + */ + +#include +#include + +#include + +namespace transport { + +namespace core { + +static std::string config_folder_path = "/etc/transport/interface.conf.d"; + +RawSocketInterface::RawSocketInterface(RawSocketConnector &connector) + : ForwarderInterface(connector) {} + +RawSocketInterface::~RawSocketInterface() {} + +void RawSocketInterface::connect(bool is_consumer) { + std::string complete_filename = + config_folder_path + std::string("/") + output_interface_; + + std::ifstream is(complete_filename); + std::string interface; + + if (is) { + is >> remote_mac_address_; + } + + // Get interface ip address + struct sockaddr_in6 address = {0}; + utils::retrieveInterfaceAddress(output_interface_, &address); + + std::memcpy(&inet6_address_.v6.as_u8, &address.sin6_addr, + sizeof(address.sin6_addr)); + connector_.connect(output_interface_, remote_mac_address_); +} + +void RawSocketInterface::registerRoute(Prefix &prefix) { return; } + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/raw_socket/raw_socket_interface.h b/libtransport/src/io_modules/raw_socket/raw_socket_interface.h new file mode 100644 index 000000000..7036cac7e --- /dev/null +++ b/libtransport/src/io_modules/raw_socket/raw_socket_interface.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 +#include +#include + +#include +#include + +namespace transport { + +namespace core { + +class RawSocketInterface + : public ForwarderInterface { + public: + typedef RawSocketConnector ConnectorType; + + RawSocketInterface(RawSocketConnector &connector); + + ~RawSocketInterface(); + + void connect(bool is_consumer); + + void registerRoute(Prefix &prefix); + + std::uint16_t getMtu() { return interface_mtu; } + + TRANSPORT_ALWAYS_INLINE static bool isControlMessageImpl( + const uint8_t *message) { + return false; + } + + TRANSPORT_ALWAYS_INLINE void processControlMessageReplyImpl( + Packet::MemBufPtr &&packet_buffer) {} + + TRANSPORT_ALWAYS_INLINE void closeConnection(){}; + + private: + static constexpr std::uint16_t interface_mtu = 1500; + std::string remote_mac_address_; +}; + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/udp/CMakeLists.txt b/libtransport/src/io_modules/udp/CMakeLists.txt new file mode 100644 index 000000000..1a43492dc --- /dev/null +++ b/libtransport/src/io_modules/udp/CMakeLists.txt @@ -0,0 +1,47 @@ +# Copyright (c) 2021 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + + +list(APPEND MODULE_HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_module.h + ${CMAKE_CURRENT_SOURCE_DIR}/udp_socket_connector.h +) + +list(APPEND MODULE_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_module.cc + ${CMAKE_CURRENT_SOURCE_DIR}/udp_socket_connector.cc +) + +# add_executable(hicnlight_module MACOSX_BUNDLE ${MODULE_SOURCE_FILES}) +# target_include_directories(hicnlight_module PRIVATE ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS}) +# set_target_properties(hicnlight_module PROPERTIES +# BUNDLE True +# MACOSX_BUNDLE_GUI_IDENTIFIER my.domain.style.identifier.hicnlight_module +# MACOSX_BUNDLE_BUNDLE_NAME hicnlight_module +# MACOSX_BUNDLE_BUNDLE_VERSION "0.1" +# MACOSX_BUNDLE_SHORT_VERSION_STRING "0.1" +# # MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/customtemplate.plist.in +# ) + +build_module(hicnlight_module + SHARED + SOURCES ${MODULE_SOURCE_FILES} + DEPENDS ${DEPENDENCIES} + COMPONENT lib${LIBTRANSPORT} + INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + # LIBRARY_ROOT_DIR "vpp_plugins" + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILE_FLAGS} +) diff --git a/libtransport/src/io_modules/udp/hicn_forwarder_module.cc b/libtransport/src/io_modules/udp/hicn_forwarder_module.cc new file mode 100644 index 000000000..ba08dd8c0 --- /dev/null +++ b/libtransport/src/io_modules/udp/hicn_forwarder_module.cc @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +union AddressLight { + uint32_t ipv4; + struct in6_addr ipv6; +}; + +typedef struct { + uint8_t message_type; + uint8_t command_id; + uint16_t length; + uint32_t seq_num; +} CommandHeader; + +typedef struct { + uint8_t message_type; + uint8_t command_id; + uint16_t length; + uint32_t seq_num; + char symbolic_or_connid[16]; + union AddressLight address; + uint16_t cost; + uint8_t address_type; + uint8_t len; +} RouteToSelfCommand; + +typedef struct { + uint8_t message_type; + uint8_t command_id; + uint16_t length; + uint32_t seq_num; + char symbolic_or_connid[16]; +} DeleteSelfConnectionCommand; + +namespace { +static constexpr uint8_t addr_inet = 1; +static constexpr uint8_t addr_inet6 = 2; +static constexpr uint8_t add_route_command = 3; +static constexpr uint8_t delete_connection_command = 5; +static constexpr uint8_t request_light = 0xc0; +static constexpr char identifier[] = "SELF"; + +void fillCommandHeader(CommandHeader *header) { + // Allocate and fill the header + header->message_type = request_light; + header->length = 1; +} + +RouteToSelfCommand createCommandRoute(std::unique_ptr &&addr, + uint8_t prefix_length) { + RouteToSelfCommand command = {0}; + + // check and set IP address + if (addr->sa_family == AF_INET) { + command.address_type = addr_inet; + command.address.ipv4 = ((sockaddr_in *)addr.get())->sin_addr.s_addr; + } else if (addr->sa_family == AF_INET6) { + command.address_type = addr_inet6; + command.address.ipv6 = ((sockaddr_in6 *)addr.get())->sin6_addr; + } + + // Fill remaining payload fields +#ifndef _WIN32 + strcpy(command.symbolic_or_connid, identifier); +#else + strcpy_s(command.symbolic_or_connid, 16, identifier); +#endif + command.cost = 1; + command.len = (uint8_t)prefix_length; + + // Allocate and fill the header + command.command_id = add_route_command; + fillCommandHeader((CommandHeader *)&command); + + return command; +} + +DeleteSelfConnectionCommand createCommandDeleteConnection() { + DeleteSelfConnectionCommand command = {0}; + fillCommandHeader((CommandHeader *)&command); + command.command_id = delete_connection_command; + +#ifndef _WIN32 + strcpy(command.symbolic_or_connid, identifier); +#else + strcpy_s(command.symbolic_or_connid, 16, identifier); +#endif + + return command; +} + +} // namespace + +namespace transport { + +namespace core { + +HicnForwarderModule::HicnForwarderModule() : IoModule(), connector_(nullptr) {} + +HicnForwarderModule::~HicnForwarderModule() {} + +void HicnForwarderModule::connect(bool is_consumer) { + connector_->connect(); + connector_->setRole(is_consumer ? Connector::Role::CONSUMER + : Connector::Role::PRODUCER); +} + +bool HicnForwarderModule::isConnected() { return connector_->isConnected(); } + +void HicnForwarderModule::send(Packet &packet) { + IoModule::send(packet); + packet.setChecksum(); + connector_->send(packet); +} + +void HicnForwarderModule::send(const uint8_t *packet, std::size_t len) { + counters_.tx_packets++; + counters_.tx_bytes += len; + + // Perfect forwarding + connector_->send(packet, len); +} + +void HicnForwarderModule::registerRoute(const Prefix &prefix) { + auto command = createCommandRoute(prefix.toSockaddr(), + (uint8_t)prefix.getPrefixLength()); + send((uint8_t *)&command, sizeof(RouteToSelfCommand)); +} + +void HicnForwarderModule::closeConnection() { + auto command = createCommandDeleteConnection(); + send((uint8_t *)&command, sizeof(DeleteSelfConnectionCommand)); + connector_->close(); +} + +void HicnForwarderModule::init( + Connector::PacketReceivedCallback &&receive_callback, + Connector::OnReconnectCallback &&reconnect_callback, + asio::io_service &io_service, const std::string &app_name) { + if (!connector_) { + connector_ = new UdpSocketConnector(std::move(receive_callback), nullptr, + nullptr, std::move(reconnect_callback), + io_service, app_name); + } +} + +void HicnForwarderModule::processControlMessageReply( + utils::MemBuf &packet_buffer) { + if (packet_buffer.data()[0] == nack_code) { + throw errors::RuntimeException( + "Received Nack message from hicn light forwarder."); + } +} + +std::uint32_t HicnForwarderModule::getMtu() { return interface_mtu; } + +bool HicnForwarderModule::isControlMessage(const uint8_t *message) { + return message[0] == ack_code || message[0] == nack_code; +} + +extern "C" IoModule *create_module(void) { return new HicnForwarderModule(); } + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/udp/hicn_forwarder_module.h b/libtransport/src/io_modules/udp/hicn_forwarder_module.h new file mode 100644 index 000000000..845db73bf --- /dev/null +++ b/libtransport/src/io_modules/udp/hicn_forwarder_module.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace transport { + +namespace core { + +class UdpSocketConnector; + +class HicnForwarderModule : public IoModule { + static constexpr uint8_t ack_code = 0xc2; + static constexpr uint8_t nack_code = 0xc3; + static constexpr std::uint16_t interface_mtu = 1500; + + public: + 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; + + HicnForwarderModule(); + + ~HicnForwarderModule(); + + void connect(bool is_consumer) override; + + void send(Packet &packet) override; + void send(const uint8_t *packet, std::size_t len) override; + + bool isConnected() override; + + void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::OnReconnectCallback &&reconnect_callback, + asio::io_service &io_service, + const std::string &app_name = "Libtransport") override; + + void registerRoute(const Prefix &prefix) override; + + std::uint32_t getMtu() override; + + bool isControlMessage(const uint8_t *message) override; + + void processControlMessageReply(utils::MemBuf &packet_buffer) override; + + void closeConnection() override; + + private: + UdpSocketConnector *connector_; +}; + +extern "C" IoModule *create_module(void); + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/udp/udp_socket_connector.cc b/libtransport/src/io_modules/udp/udp_socket_connector.cc new file mode 100644 index 000000000..456886a54 --- /dev/null +++ b/libtransport/src/io_modules/udp/udp_socket_connector.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. + */ + +#ifdef _WIN32 +#include +#endif + +#include +#include +#include +#include + +#include +#include + +namespace transport { + +namespace core { + +UdpSocketConnector::UdpSocketConnector( + PacketReceivedCallback &&receive_callback, PacketSentCallback &&packet_sent, + OnCloseCallback &&close_callback, OnReconnectCallback &&on_reconnect, + asio::io_service &io_service, std::string app_name) + : Connector(std::move(receive_callback), std::move(packet_sent), + std::move(close_callback), std::move(on_reconnect)), + io_service_(io_service), + socket_(io_service_), + resolver_(io_service_), + connection_timer_(io_service_), + read_msg_(std::make_pair(nullptr, 0)), + is_reconnection_(false), + data_available_(false), + app_name_(app_name) {} + +UdpSocketConnector::~UdpSocketConnector() {} + +void UdpSocketConnector::connect(std::string ip_address, std::string port) { + endpoint_iterator_ = resolver_.resolve( + {ip_address, port, asio::ip::resolver_query_base::numeric_service}); + + state_ = Connector::State::CONNECTING; + doConnect(); +} + +void UdpSocketConnector::send(const uint8_t *packet, std::size_t len) { + socket_.async_send(asio::buffer(packet, len), + [this](std::error_code ec, std::size_t /*length*/) { + if (sent_callback_) { + sent_callback_(this, ec); + } + }); +} + +void UdpSocketConnector::send(Packet &packet) { + io_service_.post([this, _packet{packet.shared_from_this()}]() { + bool write_in_progress = !output_buffer_.empty(); + output_buffer_.push_back(std::move(_packet)); + if (TRANSPORT_EXPECT_TRUE(state_ == Connector::State::CONNECTED)) { + if (!write_in_progress) { + doWrite(); + } + } else { + // Tell the handle connect it has data to write + data_available_ = true; + } + }); +} + +void UdpSocketConnector::close() { + if (io_service_.stopped()) { + doClose(); + } else { + io_service_.dispatch(std::bind(&UdpSocketConnector::doClose, this)); + } +} + +void UdpSocketConnector::doClose() { + if (state_ != Connector::State::CLOSED) { + state_ = Connector::State::CLOSED; + if (socket_.is_open()) { + socket_.shutdown(asio::ip::tcp::socket::shutdown_type::shutdown_both); + socket_.close(); + } + } +} + +void UdpSocketConnector::doWrite() { + auto packet = output_buffer_.front().get(); + auto array = std::vector(); + + const utils::MemBuf *current = packet; + do { + array.push_back(asio::const_buffer(current->data(), current->length())); + current = current->next(); + } while (current != packet); + + socket_.async_send(std::move(array), [this](std::error_code ec, + std::size_t length) { + if (TRANSPORT_EXPECT_TRUE(!ec)) { + output_buffer_.pop_front(); + if (!output_buffer_.empty()) { + doWrite(); + } + } else if (ec.value() == static_cast(std::errc::operation_canceled)) { + // The connection has been closed by the application. + return; + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + tryReconnect(); + } + }); +} + +void UdpSocketConnector::doRead() { + read_msg_ = getRawBuffer(); + socket_.async_receive( + asio::buffer(read_msg_.first, read_msg_.second), + [this](std::error_code ec, std::size_t length) { + if (TRANSPORT_EXPECT_TRUE(!ec)) { + auto packet = getPacketFromBuffer(read_msg_.first, length); + receive_callback_(this, *packet, std::make_error_code(std::errc(0))); + doRead(); + } else if (ec.value() == + static_cast(std::errc::operation_canceled)) { + // The connection has been closed by the application. + return; + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + tryReconnect(); + } + }); +} + +void UdpSocketConnector::tryReconnect() { + if (state_ == Connector::State::CONNECTED) { + TRANSPORT_LOGE("Connection lost. Trying to reconnect...\n"); + state_ = Connector::State::CONNECTING; + is_reconnection_ = true; + io_service_.post([this]() { + if (socket_.is_open()) { + socket_.shutdown(asio::ip::tcp::socket::shutdown_type::shutdown_both); + socket_.close(); + } + + doConnect(); + startConnectionTimer(); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + }); + } +} + +void UdpSocketConnector::doConnect() { + asio::async_connect( + socket_, endpoint_iterator_, + [this](std::error_code ec, udp::resolver::iterator) { + if (!ec) { + connection_timer_.cancel(); + state_ = Connector::State::CONNECTED; + doRead(); + + if (data_available_) { + data_available_ = false; + doWrite(); + } + + if (is_reconnection_) { + is_reconnection_ = false; + } + + on_reconnect_callback_(this); + } else { + doConnect(); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + }); +} + +bool UdpSocketConnector::checkConnected() { + return state_ == Connector::State::CONNECTED; +} + +void UdpSocketConnector::startConnectionTimer() { + connection_timer_.expires_from_now(std::chrono::seconds(60)); + connection_timer_.async_wait(std::bind(&UdpSocketConnector::handleDeadline, + this, std::placeholders::_1)); +} + +void UdpSocketConnector::handleDeadline(const std::error_code &ec) { + if (!ec) { + io_service_.post([this]() { + socket_.close(); + TRANSPORT_LOGE("Error connecting. Is the forwarder running?\n"); + }); + } +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/io_modules/udp/udp_socket_connector.h b/libtransport/src/io_modules/udp/udp_socket_connector.h new file mode 100644 index 000000000..8ab08e17a --- /dev/null +++ b/libtransport/src/io_modules/udp/udp_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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace transport { +namespace core { + +using asio::ip::udp; + +class UdpSocketConnector : public Connector { + public: + UdpSocketConnector(PacketReceivedCallback &&receive_callback, + PacketSentCallback &&packet_sent, + OnCloseCallback &&close_callback, + OnReconnectCallback &&on_reconnect, + asio::io_service &io_service, + std::string app_name = "Libtransport"); + + ~UdpSocketConnector() override; + + void send(Packet &packet) override; + + void send(const uint8_t *packet, std::size_t len) override; + + void close() override; + + void connect(std::string ip_address = "127.0.0.1", std::string port = "9695"); + + private: + void doConnect(); + + void doRead(); + + void doWrite(); + + void doClose(); + + bool checkConnected(); + + private: + void handleDeadline(const std::error_code &ec); + + void startConnectionTimer(); + + void tryReconnect(); + + asio::io_service &io_service_; + asio::ip::udp::socket socket_; + asio::ip::udp::resolver resolver_; + asio::ip::udp::resolver::iterator endpoint_iterator_; + asio::steady_timer connection_timer_; + + std::pair read_msg_; + + bool is_reconnection_; + bool data_available_; + + std::string app_name_; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/protocols/CMakeLists.txt b/libtransport/src/protocols/CMakeLists.txt index 8bfbdd6ad..eba8d1aab 100644 --- a/libtransport/src/protocols/CMakeLists.txt +++ b/libtransport/src/protocols/CMakeLists.txt @@ -21,16 +21,15 @@ list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/datagram_reassembly.h ${CMAKE_CURRENT_SOURCE_DIR}/byte_stream_reassembly.h ${CMAKE_CURRENT_SOURCE_DIR}/congestion_window_protocol.h - ${CMAKE_CURRENT_SOURCE_DIR}/packet_manager.h ${CMAKE_CURRENT_SOURCE_DIR}/rate_estimation.h - ${CMAKE_CURRENT_SOURCE_DIR}/protocol.h + ${CMAKE_CURRENT_SOURCE_DIR}/transport_protocol.h + ${CMAKE_CURRENT_SOURCE_DIR}/production_protocol.h + ${CMAKE_CURRENT_SOURCE_DIR}/prod_protocol_bytestream.h + ${CMAKE_CURRENT_SOURCE_DIR}/prod_protocol_rtc.h ${CMAKE_CURRENT_SOURCE_DIR}/raaqm.h ${CMAKE_CURRENT_SOURCE_DIR}/raaqm_data_path.h ${CMAKE_CURRENT_SOURCE_DIR}/cbr.h - ${CMAKE_CURRENT_SOURCE_DIR}/rtc.h - ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.h ${CMAKE_CURRENT_SOURCE_DIR}/errors.h - ${CMAKE_CURRENT_SOURCE_DIR}/verification_manager.h ${CMAKE_CURRENT_SOURCE_DIR}/data_processing_events.h ) @@ -41,15 +40,15 @@ list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/reassembly.cc ${CMAKE_CURRENT_SOURCE_DIR}/datagram_reassembly.cc ${CMAKE_CURRENT_SOURCE_DIR}/byte_stream_reassembly.cc - ${CMAKE_CURRENT_SOURCE_DIR}/protocol.cc + ${CMAKE_CURRENT_SOURCE_DIR}/transport_protocol.cc + ${CMAKE_CURRENT_SOURCE_DIR}/production_protocol.cc + ${CMAKE_CURRENT_SOURCE_DIR}/prod_protocol_bytestream.cc + ${CMAKE_CURRENT_SOURCE_DIR}/prod_protocol_rtc.cc ${CMAKE_CURRENT_SOURCE_DIR}/raaqm.cc ${CMAKE_CURRENT_SOURCE_DIR}/rate_estimation.cc ${CMAKE_CURRENT_SOURCE_DIR}/raaqm_data_path.cc ${CMAKE_CURRENT_SOURCE_DIR}/cbr.cc - ${CMAKE_CURRENT_SOURCE_DIR}/rtc.cc - ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.cc ${CMAKE_CURRENT_SOURCE_DIR}/errors.cc - ${CMAKE_CURRENT_SOURCE_DIR}/verification_manager.cc ) set(RAAQM_CONFIG_INSTALL_PREFIX @@ -71,5 +70,7 @@ install( COMPONENT lib${LIBTRANSPORT} ) +add_subdirectory(rtc) + set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) -set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) diff --git a/libtransport/src/protocols/byte_stream_reassembly.cc b/libtransport/src/protocols/byte_stream_reassembly.cc index 6662bec3f..d2bc961c4 100644 --- a/libtransport/src/protocols/byte_stream_reassembly.cc +++ b/libtransport/src/protocols/byte_stream_reassembly.cc @@ -20,7 +20,7 @@ #include #include #include -#include +#include namespace transport { @@ -45,11 +45,11 @@ void ByteStreamReassembly::reassemble( } } -void ByteStreamReassembly::reassemble(ContentObject::Ptr &&content_object) { - if (TRANSPORT_EXPECT_TRUE(content_object != nullptr) && - read_buffer_->capacity()) { - received_packets_.emplace(std::make_pair( - content_object->getName().getSuffix(), std::move(content_object))); +void ByteStreamReassembly::reassemble(ContentObject &content_object) { + if (TRANSPORT_EXPECT_TRUE(read_buffer_->capacity())) { + received_packets_.emplace( + std::make_pair(content_object.getName().getSuffix(), + content_object.shared_from_this())); assembleContent(); } } @@ -81,25 +81,32 @@ void ByteStreamReassembly::assembleContent() { } } -bool ByteStreamReassembly::copyContent(const ContentObject &content_object) { +bool ByteStreamReassembly::copyContent(ContentObject &content_object) { bool ret = false; - auto payload = content_object.getPayloadReference(); - auto payload_length = payload.second; - auto write_size = std::min(payload_length, read_buffer_->tailroom()); - auto additional_bytes = payload_length > read_buffer_->tailroom() - ? payload_length - read_buffer_->tailroom() - : 0; + content_object.trimStart(content_object.headerSize()); - std::memcpy(read_buffer_->writableTail(), payload.first, write_size); - read_buffer_->append(write_size); + utils::MemBuf *current = &content_object; - if (!read_buffer_->tailroom()) { - notifyApplication(); - std::memcpy(read_buffer_->writableTail(), payload.first + write_size, - additional_bytes); - read_buffer_->append(additional_bytes); - } + do { + auto payload_length = current->length(); + auto write_size = std::min(payload_length, read_buffer_->tailroom()); + auto additional_bytes = payload_length > read_buffer_->tailroom() + ? payload_length - read_buffer_->tailroom() + : 0; + + std::memcpy(read_buffer_->writableTail(), current->data(), write_size); + read_buffer_->append(write_size); + + if (!read_buffer_->tailroom()) { + notifyApplication(); + std::memcpy(read_buffer_->writableTail(), current->data() + write_size, + additional_bytes); + read_buffer_->append(additional_bytes); + } + + current = current->next(); + } while (current != &content_object); download_complete_ = index_manager_->getFinalSuffix() == content_object.getName().getSuffix(); diff --git a/libtransport/src/protocols/byte_stream_reassembly.h b/libtransport/src/protocols/byte_stream_reassembly.h index e4f62b3a8..c682d58cb 100644 --- a/libtransport/src/protocols/byte_stream_reassembly.h +++ b/libtransport/src/protocols/byte_stream_reassembly.h @@ -27,12 +27,12 @@ class ByteStreamReassembly : public Reassembly { TransportProtocol *transport_protocol); protected: - virtual void reassemble(core::ContentObject::Ptr &&content_object) override; + virtual void reassemble(core::ContentObject &content_object) override; virtual void reassemble( std::unique_ptr &&manifest) override; - bool copyContent(const core::ContentObject &content_object); + bool copyContent(core::ContentObject &content_object); virtual void reInitialize() override; diff --git a/libtransport/src/protocols/data_processing_events.h b/libtransport/src/protocols/data_processing_events.h index 8975c2b4a..5c8c16157 100644 --- a/libtransport/src/protocols/data_processing_events.h +++ b/libtransport/src/protocols/data_processing_events.h @@ -24,8 +24,7 @@ namespace protocol { class ContentObjectProcessingEventCallback { public: virtual ~ContentObjectProcessingEventCallback() = default; - virtual void onPacketDropped(core::Interest::Ptr &&i, - core::ContentObject::Ptr &&c) = 0; + virtual void onPacketDropped(core::Interest &i, core::ContentObject &c) = 0; virtual void onReassemblyFailed(std::uint32_t missing_segment) = 0; }; diff --git a/libtransport/src/protocols/datagram_reassembly.cc b/libtransport/src/protocols/datagram_reassembly.cc index abd7e984d..962c1e020 100644 --- a/libtransport/src/protocols/datagram_reassembly.cc +++ b/libtransport/src/protocols/datagram_reassembly.cc @@ -24,8 +24,8 @@ DatagramReassembly::DatagramReassembly( TransportProtocol* transport_protocol) : Reassembly(icn_socket, transport_protocol) {} -void DatagramReassembly::reassemble(core::ContentObject::Ptr&& content_object) { - read_buffer_ = content_object->getPayload(); +void DatagramReassembly::reassemble(core::ContentObject& content_object) { + read_buffer_ = content_object.getPayload(); Reassembly::notifyApplication(); } diff --git a/libtransport/src/protocols/datagram_reassembly.h b/libtransport/src/protocols/datagram_reassembly.h index 2427ae62f..3462212d3 100644 --- a/libtransport/src/protocols/datagram_reassembly.h +++ b/libtransport/src/protocols/datagram_reassembly.h @@ -26,7 +26,7 @@ class DatagramReassembly : public Reassembly { DatagramReassembly(implementation::ConsumerSocket *icn_socket, TransportProtocol *transport_protocol); - virtual void reassemble(core::ContentObject::Ptr &&content_object) override; + virtual void reassemble(core::ContentObject &content_object) override; virtual void reInitialize() override; virtual void reassemble( std::unique_ptr &&manifest) override { diff --git a/libtransport/src/protocols/errors.cc b/libtransport/src/protocols/errors.cc index eefb6f957..ae7b6e634 100644 --- a/libtransport/src/protocols/errors.cc +++ b/libtransport/src/protocols/errors.cc @@ -52,7 +52,9 @@ std::string protocol_category_impl::message(int ev) const { case protocol_error::session_aborted: { return "The session has been aborted by the application."; } - default: { return "Unknown protocol error"; } + default: { + return "Unknown protocol error"; + } } } diff --git a/libtransport/src/protocols/fec_base.h b/libtransport/src/protocols/fec_base.h new file mode 100644 index 000000000..a135c474f --- /dev/null +++ b/libtransport/src/protocols/fec_base.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include + +namespace transport { +namespace protocol { + +/** + * Interface classes to integrate FEC inside any producer transport protocol + */ +class ProducerFECBase { + public: + /** + * Callback, to be called by implementations as soon as a repair packet is + * ready. + */ + using RepairPacketsReady = + std::function &)>; + + /** + * Producers will call this function upon production of a new packet. + */ + virtual void onPacketProduced(const core::ContentObject &content_object) = 0; + + /** + * Set callback to signal production protocol the repair packet is ready. + */ + void setFECCallback(const RepairPacketsReady &on_repair_packet) { + rep_packet_ready_callback_ = on_repair_packet; + } + + protected: + RepairPacketsReady rep_packet_ready_callback_; +}; + +/** + * Interface classes to integrate FEC inside any consumer transport protocol + */ +class ConsumerFECBase { + public: + /** + * Callback, to be called by implemrntations as soon as a packet is recovered. + */ + using OnPacketsRecovered = + std::function &)>; + + /** + * Consumers will call this function when they receive a FEC packet. + */ + virtual void onFECPacket(const core::ContentObject &content_object) = 0; + + /** + * Consumers will call this function when they receive a data packet + */ + virtual void onDataPacket(const core::ContentObject &content_object) = 0; + + /** + * Set callback to signal consumer protocol the repair packet is ready. + */ + void setFECCallback(const OnPacketsRecovered &on_repair_packet) { + packet_recovered_callback_ = on_repair_packet; + } + + protected: + OnPacketsRecovered packet_recovered_callback_; +}; + +} // namespace protocol +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/protocols/incremental_indexer.cc b/libtransport/src/protocols/incremental_indexer.cc index 0872c4554..95daa0a3e 100644 --- a/libtransport/src/protocols/incremental_indexer.cc +++ b/libtransport/src/protocols/incremental_indexer.cc @@ -13,37 +13,38 @@ * limitations under the License. */ -#include - #include -#include +#include +#include +#include namespace transport { namespace protocol { -void IncrementalIndexer::onContentObject( - core::Interest::Ptr &&interest, core::ContentObject::Ptr &&content_object) { +void IncrementalIndexer::onContentObject(core::Interest &interest, + core::ContentObject &content_object) { using namespace interface; - TRANSPORT_LOGD("Receive content %s", content_object->getName().toString().c_str()); + TRANSPORT_LOGD("Received content %s", + content_object.getName().toString().c_str()); - if (TRANSPORT_EXPECT_FALSE(content_object->testRst())) { - final_suffix_ = content_object->getName().getSuffix(); + if (TRANSPORT_EXPECT_FALSE(content_object.testRst())) { + final_suffix_ = content_object.getName().getSuffix(); } - auto ret = verification_manager_->onPacketToVerify(*content_object); + auto ret = verifier_->verifyPackets(&content_object); switch (ret) { - case VerificationPolicy::ACCEPT_PACKET: { - reassembly_->reassemble(std::move(content_object)); + case auth::VerificationPolicy::ACCEPT: { + reassembly_->reassemble(content_object); break; } - case VerificationPolicy::DROP_PACKET: { - transport_protocol_->onPacketDropped(std::move(interest), - std::move(content_object)); + case auth::VerificationPolicy::UNKNOWN: + case auth::VerificationPolicy::DROP: { + transport_protocol_->onPacketDropped(interest, content_object); break; } - case VerificationPolicy::ABORT_SESSION: { + case auth::VerificationPolicy::ABORT: { transport_protocol_->onContentReassembled( make_error_code(protocol_error::session_aborted)); break; diff --git a/libtransport/src/protocols/incremental_indexer.h b/libtransport/src/protocols/incremental_indexer.h index 20c5e4759..d7760f8e6 100644 --- a/libtransport/src/protocols/incremental_indexer.h +++ b/libtransport/src/protocols/incremental_indexer.h @@ -15,13 +15,13 @@ #pragma once -#include -#include +#include +#include +#include #include - +#include #include #include -#include #include @@ -47,11 +47,12 @@ class IncrementalIndexer : public Indexer { first_suffix_(0), next_download_suffix_(0), next_reassembly_suffix_(0), - verification_manager_( - std::make_unique(icn_socket)) { + verifier_(nullptr) { if (reassembly_) { reassembly_->setIndexer(this); } + socket_->getSocketOption(implementation::GeneralTransportOptions::VERIFIER, + verifier_); } IncrementalIndexer(const IncrementalIndexer &) = delete; @@ -64,15 +65,14 @@ class IncrementalIndexer : public Indexer { first_suffix_(other.first_suffix_), next_download_suffix_(other.next_download_suffix_), next_reassembly_suffix_(other.next_reassembly_suffix_), - verification_manager_(std::move(other.verification_manager_)) { + verifier_(nullptr) { if (reassembly_) { reassembly_->setIndexer(this); } + socket_->getSocketOption(implementation::GeneralTransportOptions::VERIFIER, + verifier_); } - /** - * - */ virtual ~IncrementalIndexer() {} TRANSPORT_ALWAYS_INLINE virtual void reset( @@ -112,8 +112,8 @@ class IncrementalIndexer : public Indexer { return final_suffix_; } - void onContentObject(core::Interest::Ptr &&interest, - core::ContentObject::Ptr &&content_object) override; + void onContentObject(core::Interest &interest, + core::ContentObject &content_object) override; TRANSPORT_ALWAYS_INLINE void setReassembly(Reassembly *reassembly) { reassembly_ = reassembly; @@ -123,10 +123,6 @@ class IncrementalIndexer : public Indexer { } } - TRANSPORT_ALWAYS_INLINE bool onKeyToVerify() override { - return verification_manager_->onKeyToVerify(); - } - protected: implementation::ConsumerSocket *socket_; Reassembly *reassembly_; @@ -135,9 +131,8 @@ class IncrementalIndexer : public Indexer { uint32_t first_suffix_; uint32_t next_download_suffix_; uint32_t next_reassembly_suffix_; - std::unique_ptr verification_manager_; + std::shared_ptr verifier_; }; -} // end namespace protocol - -} // end namespace transport +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/protocols/indexer.cc b/libtransport/src/protocols/indexer.cc index ca12330a6..1379a609c 100644 --- a/libtransport/src/protocols/indexer.cc +++ b/libtransport/src/protocols/indexer.cc @@ -14,11 +14,9 @@ */ #include - #include #include #include -#include namespace transport { namespace protocol { @@ -32,16 +30,16 @@ IndexManager::IndexManager(implementation::ConsumerSocket *icn_socket, transport_(transport), reassembly_(reassembly) {} -void IndexManager::onContentObject(core::Interest::Ptr &&interest, - core::ContentObject::Ptr &&content_object) { +void IndexManager::onContentObject(core::Interest &interest, + core::ContentObject &content_object) { if (first_segment_received_) { - indexer_->onContentObject(std::move(interest), std::move(content_object)); + indexer_->onContentObject(interest, content_object); } else { - std::uint32_t segment_number = interest->getName().getSuffix(); + std::uint32_t segment_number = interest.getName().getSuffix(); if (segment_number == 0) { // Check if manifest - if (content_object->getPayloadType() == PayloadType::MANIFEST) { + if (content_object.getPayloadType() == core::PayloadType::MANIFEST) { IncrementalIndexer *indexer = static_cast(indexer_.release()); indexer_ = @@ -49,25 +47,21 @@ void IndexManager::onContentObject(core::Interest::Ptr &&interest, delete indexer; } - indexer_->onContentObject(std::move(interest), std::move(content_object)); + indexer_->onContentObject(interest, content_object); auto it = interest_data_set_.begin(); while (it != interest_data_set_.end()) { - indexer_->onContentObject( - std::move(const_cast(it->first)), - std::move(const_cast(it->second))); + indexer_->onContentObject(*it->first, *it->second); it = interest_data_set_.erase(it); } first_segment_received_ = true; } else { - interest_data_set_.emplace(std::move(interest), - std::move(content_object)); + interest_data_set_.emplace(interest.shared_from_this(), + content_object.shared_from_this()); } } } -bool IndexManager::onKeyToVerify() { return indexer_->onKeyToVerify(); } - void IndexManager::reset(std::uint32_t offset) { indexer_ = std::make_unique(icn_socket_, transport_, reassembly_); diff --git a/libtransport/src/protocols/indexer.h b/libtransport/src/protocols/indexer.h index 8213a1503..49e22a4cf 100644 --- a/libtransport/src/protocols/indexer.h +++ b/libtransport/src/protocols/indexer.h @@ -33,10 +33,8 @@ class TransportProtocol; class Indexer { public: - /** - * - */ virtual ~Indexer() = default; + /** * Retrieve from the manifest the next suffix to retrieve. */ @@ -55,10 +53,8 @@ class Indexer { virtual void reset(std::uint32_t offset = 0) = 0; - virtual void onContentObject(core::Interest::Ptr &&interest, - core::ContentObject::Ptr &&content_object) = 0; - - virtual bool onKeyToVerify() = 0; + virtual void onContentObject(core::Interest &interest, + core::ContentObject &content_object) = 0; }; class IndexManager : Indexer { @@ -86,10 +82,8 @@ class IndexManager : Indexer { void reset(std::uint32_t offset = 0) override; - void onContentObject(core::Interest::Ptr &&interest, - core::ContentObject::Ptr &&content_object) override; - - bool onKeyToVerify() override; + void onContentObject(core::Interest &interest, + core::ContentObject &content_object) override; private: std::unique_ptr indexer_; diff --git a/libtransport/src/protocols/manifest_incremental_indexer.cc b/libtransport/src/protocols/manifest_incremental_indexer.cc index da835b577..a6312ca90 100644 --- a/libtransport/src/protocols/manifest_incremental_indexer.cc +++ b/libtransport/src/protocols/manifest_incremental_indexer.cc @@ -14,9 +14,9 @@ */ #include - +#include #include -#include +#include #include #include @@ -36,41 +36,46 @@ ManifestIncrementalIndexer::ManifestIncrementalIndexer( 0)) {} void ManifestIncrementalIndexer::onContentObject( - core::Interest::Ptr &&interest, core::ContentObject::Ptr &&content_object) { - // Check if manifest or not - if (content_object->getPayloadType() == PayloadType::MANIFEST) { - TRANSPORT_LOGD("Receive content %s", content_object->getName().toString().c_str()); - onUntrustedManifest(std::move(interest), std::move(content_object)); - } else if (content_object->getPayloadType() == PayloadType::CONTENT_OBJECT) { - TRANSPORT_LOGD("Receive manifest %s", content_object->getName().toString().c_str()); - onUntrustedContentObject(std::move(interest), std::move(content_object)); - } -} - -void ManifestIncrementalIndexer::onUntrustedManifest( - core::Interest::Ptr &&interest, core::ContentObject::Ptr &&content_object) { - auto ret = verification_manager_->onPacketToVerify(*content_object); - - switch (ret) { - case VerificationPolicy::ACCEPT_PACKET: { - processTrustedManifest(std::move(content_object)); + core::Interest &interest, core::ContentObject &content_object) { + switch (content_object.getPayloadType()) { + case PayloadType::DATA: { + TRANSPORT_LOGD("Received content %s", + content_object.getName().toString().c_str()); + onUntrustedContentObject(interest, content_object); break; } - case VerificationPolicy::DROP_PACKET: - case VerificationPolicy::ABORT_SESSION: { - transport_protocol_->onContentReassembled( - make_error_code(protocol_error::session_aborted)); + case PayloadType::MANIFEST: { + TRANSPORT_LOGD("Received manifest %s", + content_object.getName().toString().c_str()); + onUntrustedManifest(interest, content_object); break; } + default: { + return; + } } } -void ManifestIncrementalIndexer::processTrustedManifest( - ContentObject::Ptr &&content_object) { +void ManifestIncrementalIndexer::onUntrustedManifest( + core::Interest &interest, core::ContentObject &content_object) { auto manifest = - std::make_unique(std::move(*content_object)); + std::make_unique(std::move(content_object)); + + auth::VerificationPolicy policy = verifier_->verifyPackets(manifest.get()); + manifest->decode(); + if (policy != auth::VerificationPolicy::ACCEPT) { + transport_protocol_->onContentReassembled( + make_error_code(protocol_error::session_aborted)); + return; + } + + processTrustedManifest(interest, std::move(manifest)); +} + +void ManifestIncrementalIndexer::processTrustedManifest( + core::Interest &interest, std::unique_ptr manifest) { if (TRANSPORT_EXPECT_FALSE(manifest->getVersion() != core::ManifestVersion::VERSION_1)) { throw errors::RuntimeException("Received manifest with unknown version."); @@ -78,23 +83,45 @@ void ManifestIncrementalIndexer::processTrustedManifest( switch (manifest->getManifestType()) { case core::ManifestType::INLINE_MANIFEST: { - auto _it = manifest->getSuffixList().begin(); - auto _end = manifest->getSuffixList().end(); - suffix_strategy_->setFinalSuffix(manifest->getFinalBlockNumber()); - for (; _it != _end; _it++) { - auto hash = - std::make_pair(std::vector(_it->second, _it->second + 32), - manifest->getHashAlgorithm()); + // The packets to verify with the received manifest + std::vector packets; + + // Convert the received manifest to a map of packet suffixes to hashes + std::unordered_map current_manifest = + core::ContentObjectManifest::getSuffixMap(manifest.get()); + + // Update 'suffix_map_' with new hashes from the received manifest and + // build 'packets' + for (auto it = current_manifest.begin(); it != current_manifest.end();) { + if (unverified_segments_.find(it->first) == + unverified_segments_.end()) { + suffix_map_[it->first] = std::move(it->second); + current_manifest.erase(it++); + continue; + } - if (!checkUnverifiedSegments(_it->first, hash)) { - suffix_hash_map_[_it->first] = std::move(hash); + packets.push_back(unverified_segments_[it->first].second.get()); + it++; + } + + // Verify unverified segments using the received manifest + std::vector policies = + verifier_->verifyPackets(packets, current_manifest); + + for (unsigned int i = 0; i < packets.size(); ++i) { + auth::Suffix suffix = packets[i]->getName().getSuffix(); + + if (policies[i] != auth::VerificationPolicy::UNKNOWN) { + unverified_segments_.erase(suffix); } + + applyPolicy(*unverified_segments_[suffix].first, + *unverified_segments_[suffix].second, policies[i]); } reassembly_->reassemble(std::move(manifest)); - break; } case core::ManifestType::FLIC_MANIFEST: { @@ -106,89 +133,47 @@ void ManifestIncrementalIndexer::processTrustedManifest( } } -bool ManifestIncrementalIndexer::checkUnverifiedSegments( - std::uint32_t suffix, const HashEntry &hash) { - auto it = unverified_segments_.find(suffix); - - if (it != unverified_segments_.end()) { - auto ret = verifyContentObject(hash, *it->second.second); - - switch (ret) { - case VerificationPolicy::ACCEPT_PACKET: { - reassembly_->reassemble(std::move(it->second.second)); - break; - } - case VerificationPolicy::DROP_PACKET: { - transport_protocol_->onPacketDropped(std::move(it->second.first), - std::move(it->second.second)); - break; - } - case VerificationPolicy::ABORT_SESSION: { - transport_protocol_->onContentReassembled( - make_error_code(protocol_error::session_aborted)); - break; - } +void ManifestIncrementalIndexer::onUntrustedContentObject( + Interest &interest, ContentObject &content_object) { + auth::Suffix suffix = content_object.getName().getSuffix(); + auth::VerificationPolicy policy = + verifier_->verifyPackets(&content_object, suffix_map_); + + switch (policy) { + case auth::VerificationPolicy::UNKNOWN: { + unverified_segments_[suffix] = std::make_pair( + interest.shared_from_this(), content_object.shared_from_this()); + break; + } + default: { + suffix_map_.erase(suffix); + break; } - - unverified_segments_.erase(it); - return true; - } - - return false; -} - -VerificationPolicy ManifestIncrementalIndexer::verifyContentObject( - const HashEntry &manifest_hash, const ContentObject &content_object) { - VerificationPolicy ret; - - auto hash_type = static_cast(manifest_hash.second); - auto data_packet_digest = content_object.computeDigest(manifest_hash.second); - auto data_packet_digest_bytes = - data_packet_digest.getDigest().data(); - const std::vector &manifest_digest_bytes = manifest_hash.first; - - if (utils::CryptoHash::compareBinaryDigest( - data_packet_digest_bytes, manifest_digest_bytes.data(), hash_type)) { - ret = VerificationPolicy::ACCEPT_PACKET; - } else { - ConsumerContentObjectVerificationFailedCallback - *verification_failed_callback = VOID_HANDLER; - socket_->getSocketOption(ConsumerCallbacksOptions::VERIFICATION_FAILED, - &verification_failed_callback); - ret = (*verification_failed_callback)( - *socket_->getInterface(), content_object, - make_error_code(protocol_error::integrity_verification_failed)); } - return ret; + applyPolicy(interest, content_object, policy); } -void ManifestIncrementalIndexer::onUntrustedContentObject( - Interest::Ptr &&i, ContentObject::Ptr &&c) { - auto suffix = c->getName().getSuffix(); - auto it = suffix_hash_map_.find(suffix); - - if (it != suffix_hash_map_.end()) { - auto ret = verifyContentObject(it->second, *c); - - switch (ret) { - case VerificationPolicy::ACCEPT_PACKET: { - suffix_hash_map_.erase(it); - reassembly_->reassemble(std::move(c)); - break; - } - case VerificationPolicy::DROP_PACKET: { - transport_protocol_->onPacketDropped(std::move(i), std::move(c)); - break; - } - case VerificationPolicy::ABORT_SESSION: { - transport_protocol_->onContentReassembled( - make_error_code(protocol_error::session_aborted)); - break; - } +void ManifestIncrementalIndexer::applyPolicy( + core::Interest &interest, core::ContentObject &content_object, + auth::VerificationPolicy policy) { + switch (policy) { + case auth::VerificationPolicy::ACCEPT: { + reassembly_->reassemble(content_object); + break; + } + case auth::VerificationPolicy::DROP: { + transport_protocol_->onPacketDropped(interest, content_object); + break; + } + case auth::VerificationPolicy::ABORT: { + transport_protocol_->onContentReassembled( + make_error_code(protocol_error::session_aborted)); + break; + } + default: { + break; } - } else { - unverified_segments_[suffix] = std::make_pair(std::move(i), std::move(c)); } } @@ -224,7 +209,7 @@ uint32_t ManifestIncrementalIndexer::getNextReassemblySegment() { void ManifestIncrementalIndexer::reset(std::uint32_t offset) { IncrementalIndexer::reset(offset); - suffix_hash_map_.clear(); + suffix_map_.clear(); unverified_segments_.clear(); SuffixQueue empty; std::swap(suffix_queue_, empty); diff --git a/libtransport/src/protocols/manifest_incremental_indexer.h b/libtransport/src/protocols/manifest_incremental_indexer.h index 38b01533e..1bb76eb87 100644 --- a/libtransport/src/protocols/manifest_incremental_indexer.h +++ b/libtransport/src/protocols/manifest_incremental_indexer.h @@ -15,6 +15,7 @@ #pragma once +#include #include #include #include @@ -22,7 +23,6 @@ #include namespace transport { - namespace protocol { class ManifestIncrementalIndexer : public IncrementalIndexer { @@ -30,7 +30,8 @@ class ManifestIncrementalIndexer : public IncrementalIndexer { public: using SuffixQueue = std::queue; - using HashEntry = std::pair, utils::CryptoHashType>; + using InterestContentPair = + std::pair; ManifestIncrementalIndexer(implementation::ConsumerSocket *icn_socket, TransportProtocol *transport, @@ -50,8 +51,8 @@ class ManifestIncrementalIndexer : public IncrementalIndexer { void reset(std::uint32_t offset = 0) override; - void onContentObject(core::Interest::Ptr &&interest, - core::ContentObject::Ptr &&content_object) override; + void onContentObject(core::Interest &interest, + core::ContentObject &content_object) override; uint32_t getNextSuffix() override; @@ -61,30 +62,24 @@ class ManifestIncrementalIndexer : public IncrementalIndexer { uint32_t getFinalSuffix() override; - private: - void onUntrustedManifest(core::Interest::Ptr &&interest, - core::ContentObject::Ptr &&content_object); - void onUntrustedContentObject(core::Interest::Ptr &&interest, - core::ContentObject::Ptr &&content_object); - void processTrustedManifest(core::ContentObject::Ptr &&content_object); - void onManifestReceived(core::Interest::Ptr &&i, - core::ContentObject::Ptr &&c); - void onManifestTimeout(core::Interest::Ptr &&i); - VerificationPolicy verifyContentObject( - const HashEntry &manifest_hash, - const core::ContentObject &content_object); - bool checkUnverifiedSegments(std::uint32_t suffix, const HashEntry &hash); - protected: std::unique_ptr suffix_strategy_; SuffixQueue suffix_queue_; // Hash verification - std::unordered_map suffix_hash_map_; + std::unordered_map suffix_map_; + std::unordered_map unverified_segments_; - std::unordered_map> - unverified_segments_; + private: + void onUntrustedManifest(core::Interest &interest, + core::ContentObject &content_object); + void processTrustedManifest(core::Interest &interest, + std::unique_ptr manifest); + void onUntrustedContentObject(core::Interest &interest, + core::ContentObject &content_object); + void applyPolicy(core::Interest &interest, + core::ContentObject &content_object, + auth::VerificationPolicy policy); }; } // end namespace protocol diff --git a/libtransport/src/protocols/prod_protocol_bytestream.cc b/libtransport/src/protocols/prod_protocol_bytestream.cc new file mode 100644 index 000000000..6bd989fe4 --- /dev/null +++ b/libtransport/src/protocols/prod_protocol_bytestream.cc @@ -0,0 +1,390 @@ +/* + * 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 +#include + +#include + +namespace transport { + +namespace protocol { + +using namespace core; +using namespace implementation; + +ByteStreamProductionProtocol::ByteStreamProductionProtocol( + implementation::ProducerSocket *icn_socket) + : ProductionProtocol(icn_socket) {} + +ByteStreamProductionProtocol::~ByteStreamProductionProtocol() { + stop(); + if (listening_thread_.joinable()) { + listening_thread_.join(); + } +} + +uint32_t ByteStreamProductionProtocol::produceDatagram( + const Name &content_name, std::unique_ptr &&buffer) { + throw errors::NotImplementedException(); +} + +uint32_t ByteStreamProductionProtocol::produceDatagram(const Name &content_name, + const uint8_t *buffer, + size_t buffer_size) { + throw errors::NotImplementedException(); +} + +uint32_t ByteStreamProductionProtocol::produceStream(const Name &content_name, + const uint8_t *buffer, + size_t buffer_size, + bool is_last, + uint32_t start_offset) { + if (!buffer_size) { + return 0; + } + + return produceStream(content_name, + utils::MemBuf::copyBuffer(buffer, buffer_size), is_last, + start_offset); +} + +uint32_t ByteStreamProductionProtocol::produceStream( + const Name &content_name, std::unique_ptr &&buffer, + bool is_last, uint32_t start_offset) { + if (TRANSPORT_EXPECT_FALSE(buffer->length() == 0)) { + return 0; + } + + Name name(content_name); + + // Get the atomic variables to ensure they keep the same value + // during the production + + // Total size of the data packet + uint32_t data_packet_size; + socket_->getSocketOption(GeneralTransportOptions::DATA_PACKET_SIZE, + data_packet_size); + + // Expiry time + uint32_t content_object_expiry_time; + socket_->getSocketOption(GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME, + content_object_expiry_time); + + // Hash algorithm + auth::CryptoHashType hash_algo; + socket_->getSocketOption(GeneralTransportOptions::HASH_ALGORITHM, hash_algo); + + // Use manifest + bool making_manifest; + socket_->getSocketOption(GeneralTransportOptions::MAKE_MANIFEST, + making_manifest); + + // Suffix calculation strategy + core::NextSegmentCalculationStrategy _suffix_strategy; + socket_->getSocketOption(GeneralTransportOptions::SUFFIX_STRATEGY, + _suffix_strategy); + auto suffix_strategy = utils::SuffixStrategyFactory::getSuffixStrategy( + _suffix_strategy, start_offset); + + std::shared_ptr signer; + socket_->getSocketOption(GeneralTransportOptions::SIGNER, signer); + + auto buffer_size = buffer->length(); + int bytes_segmented = 0; + std::size_t header_size; + std::size_t manifest_header_size = 0; + std::size_t signature_length = 0; + std::uint32_t final_block_number = start_offset; + uint64_t free_space_for_content = 0; + + core::Packet::Format format; + std::shared_ptr manifest; + bool is_last_manifest = false; + + // TODO Manifest may still be used for indexing + if (making_manifest && !signer) { + TRANSPORT_LOGE("Making manifests without setting producer identity."); + } + + core::Packet::Format hf_format = core::Packet::Format::HF_UNSPEC; + core::Packet::Format hf_format_ah = core::Packet::Format::HF_UNSPEC; + + if (name.getType() == HNT_CONTIGUOUS_V4 || name.getType() == HNT_IOV_V4) { + hf_format = core::Packet::Format::HF_INET_TCP; + hf_format_ah = core::Packet::Format::HF_INET_TCP_AH; + } else if (name.getType() == HNT_CONTIGUOUS_V6 || + name.getType() == HNT_IOV_V6) { + hf_format = core::Packet::Format::HF_INET6_TCP; + hf_format_ah = core::Packet::Format::HF_INET6_TCP_AH; + } else { + throw errors::RuntimeException("Unknown name format."); + } + + format = hf_format; + if (making_manifest) { + manifest_header_size = core::Packet::getHeaderSizeFromFormat( + signer ? hf_format_ah : hf_format, + signer ? signer->getSignatureSize() : 0); + } else if (signer) { + format = hf_format_ah; + signature_length = signer->getSignatureSize(); + } + + header_size = core::Packet::getHeaderSizeFromFormat(format, signature_length); + free_space_for_content = data_packet_size - header_size; + uint32_t number_of_segments = + uint32_t(std::ceil(double(buffer_size) / double(free_space_for_content))); + if (free_space_for_content * number_of_segments < buffer_size) { + number_of_segments++; + } + + // TODO allocate space for all the headers + if (making_manifest) { + uint32_t segment_in_manifest = static_cast( + std::floor(double(data_packet_size - manifest_header_size - + ContentObjectManifest::getManifestHeaderSize()) / + ContentObjectManifest::getManifestEntrySize()) - + 1.0); + uint32_t number_of_manifests = static_cast( + std::ceil(float(number_of_segments) / segment_in_manifest)); + final_block_number += number_of_segments + number_of_manifests - 1; + + manifest.reset(ContentObjectManifest::createManifest( + name.setSuffix(suffix_strategy->getNextManifestSuffix()), + core::ManifestVersion::VERSION_1, core::ManifestType::INLINE_MANIFEST, + hash_algo, is_last_manifest, name, _suffix_strategy, + signer ? signer->getSignatureSize() : 0)); + manifest->setLifetime(content_object_expiry_time); + + if (is_last) { + manifest->setFinalBlockNumber(final_block_number); + } else { + manifest->setFinalBlockNumber(utils::SuffixStrategy::INVALID_SUFFIX); + } + } + + for (unsigned int packaged_segments = 0; + packaged_segments < number_of_segments; packaged_segments++) { + if (making_manifest) { + if (manifest->estimateManifestSize(2) > + data_packet_size - manifest_header_size) { + manifest->encode(); + + // If identity set, sign manifest + if (signer) { + signer->signPacket(manifest.get()); + } + + // Send the current manifest + passContentObjectToCallbacks(manifest); + + TRANSPORT_LOGD("Send manifest %s", + manifest->getName().toString().c_str()); + + // Send content objects stored in the queue + while (!content_queue_.empty()) { + passContentObjectToCallbacks(content_queue_.front()); + TRANSPORT_LOGD("Send content %s", + content_queue_.front()->getName().toString().c_str()); + content_queue_.pop(); + } + + // Create new manifest. The reference to the last manifest has been + // acquired in the passContentObjectToCallbacks function, so we can + // safely release this reference + manifest.reset(ContentObjectManifest::createManifest( + name.setSuffix(suffix_strategy->getNextManifestSuffix()), + core::ManifestVersion::VERSION_1, + core::ManifestType::INLINE_MANIFEST, hash_algo, is_last_manifest, + name, _suffix_strategy, signer ? signer->getSignatureSize() : 0)); + + manifest->setLifetime(content_object_expiry_time); + manifest->setFinalBlockNumber( + is_last ? final_block_number + : utils::SuffixStrategy::INVALID_SUFFIX); + } + } + + auto content_suffix = suffix_strategy->getNextContentSuffix(); + auto content_object = std::make_shared( + name.setSuffix(content_suffix), format, + signer && !making_manifest ? signer->getSignatureSize() : 0); + content_object->setLifetime(content_object_expiry_time); + + auto b = buffer->cloneOne(); + b->trimStart(free_space_for_content * packaged_segments); + b->trimEnd(b->length()); + + if (TRANSPORT_EXPECT_FALSE(packaged_segments == number_of_segments - 1)) { + b->append(buffer_size - bytes_segmented); + bytes_segmented += (int)(buffer_size - bytes_segmented); + + if (is_last && making_manifest) { + is_last_manifest = true; + } else if (is_last) { + content_object->setRst(); + } + + } else { + b->append(free_space_for_content); + bytes_segmented += (int)(free_space_for_content); + } + + content_object->appendPayload(std::move(b)); + + if (making_manifest) { + using namespace std::chrono_literals; + auth::CryptoHash hash = content_object->computeDigest(hash_algo); + manifest->addSuffixHash(content_suffix, hash); + content_queue_.push(content_object); + } else { + if (signer) { + signer->signPacket(content_object.get()); + } + passContentObjectToCallbacks(content_object); + TRANSPORT_LOGD("Send content %s", + content_object->getName().toString().c_str()); + } + } + + if (making_manifest) { + if (is_last_manifest) { + manifest->setFinalManifest(is_last_manifest); + } + + manifest->encode(); + + if (signer) { + signer->signPacket(manifest.get()); + } + + passContentObjectToCallbacks(manifest); + TRANSPORT_LOGD("Send manifest %s", manifest->getName().toString().c_str()); + + while (!content_queue_.empty()) { + passContentObjectToCallbacks(content_queue_.front()); + TRANSPORT_LOGD("Send content %s", + content_queue_.front()->getName().toString().c_str()); + content_queue_.pop(); + } + } + + portal_->getIoService().post([this]() { + std::shared_ptr co; + while (object_queue_for_callbacks_.pop(co)) { + if (*on_new_segment_) { + on_new_segment_->operator()(*socket_->getInterface(), *co); + } + + if (*on_content_object_to_sign_) { + on_content_object_to_sign_->operator()(*socket_->getInterface(), *co); + } + + if (*on_content_object_in_output_buffer_) { + on_content_object_in_output_buffer_->operator()( + *socket_->getInterface(), *co); + } + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), *co); + } + } + }); + + portal_->getIoService().dispatch([this, buffer_size]() { + if (*on_content_produced_) { + on_content_produced_->operator()(*socket_->getInterface(), + std::make_error_code(std::errc(0)), + buffer_size); + } + }); + + return suffix_strategy->getTotalCount(); +} + +void ByteStreamProductionProtocol::scheduleSendBurst() { + portal_->getIoService().post([this]() { + std::shared_ptr co; + + for (uint32_t i = 0; i < burst_size; i++) { + if (object_queue_for_callbacks_.pop(co)) { + if (*on_new_segment_) { + on_new_segment_->operator()(*socket_->getInterface(), *co); + } + + if (*on_content_object_to_sign_) { + on_content_object_to_sign_->operator()(*socket_->getInterface(), *co); + } + + if (*on_content_object_in_output_buffer_) { + on_content_object_in_output_buffer_->operator()( + *socket_->getInterface(), *co); + } + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), *co); + } + } else { + break; + } + } + }); +} + +void ByteStreamProductionProtocol::passContentObjectToCallbacks( + const std::shared_ptr &content_object) { + output_buffer_.insert(content_object); + portal_->sendContentObject(*content_object); + object_queue_for_callbacks_.push(std::move(content_object)); + + if (object_queue_for_callbacks_.size() >= burst_size) { + scheduleSendBurst(); + } +} + +void ByteStreamProductionProtocol::onInterest(Interest &interest) { + TRANSPORT_LOGD("Received interest for %s", + interest.getName().toString().c_str()); + if (*on_interest_input_) { + on_interest_input_->operator()(*socket_->getInterface(), interest); + } + + const std::shared_ptr content_object = + output_buffer_.find(interest); + + if (content_object) { + if (*on_interest_satisfied_output_buffer_) { + on_interest_satisfied_output_buffer_->operator()(*socket_->getInterface(), + interest); + } + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), + *content_object); + } + + portal_->sendContentObject(*content_object); + } else { + if (*on_interest_process_) { + on_interest_process_->operator()(*socket_->getInterface(), interest); + } + } +} + +void ByteStreamProductionProtocol::onError(std::error_code ec) {} + +} // namespace protocol +} // end namespace transport diff --git a/libtransport/src/protocols/prod_protocol_bytestream.h b/libtransport/src/protocols/prod_protocol_bytestream.h new file mode 100644 index 000000000..cf36b90a5 --- /dev/null +++ b/libtransport/src/protocols/prod_protocol_bytestream.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 +#include + +#include +#include + +namespace transport { + +namespace protocol { + +using namespace core; + +class ByteStreamProductionProtocol : public ProductionProtocol { + static constexpr uint32_t burst_size = 256; + + public: + ByteStreamProductionProtocol(implementation::ProducerSocket *icn_socket); + + ~ByteStreamProductionProtocol() override; + + using ProductionProtocol::start; + using ProductionProtocol::stop; + + uint32_t produceStream(const Name &content_name, + std::unique_ptr &&buffer, + bool is_last = true, + uint32_t start_offset = 0) override; + uint32_t produceStream(const Name &content_name, const uint8_t *buffer, + size_t buffer_size, bool is_last = true, + uint32_t start_offset = 0) override; + uint32_t produceDatagram(const Name &content_name, + std::unique_ptr &&buffer) override; + uint32_t produceDatagram(const Name &content_name, const uint8_t *buffer, + size_t buffer_size) override; + + protected: + // Consumer Callback + // void reset() override; + void onInterest(core::Interest &i) override; + void onError(std::error_code ec) override; + + private: + void passContentObjectToCallbacks( + const std::shared_ptr &content_object); + void scheduleSendBurst(); + + private: + // While manifests are being built, contents are stored in a queue + std::queue> content_queue_; + utils::CircularFifo, 2048> + object_queue_for_callbacks_; +}; + +} // end namespace protocol +} // end namespace transport diff --git a/libtransport/src/protocols/prod_protocol_rtc.cc b/libtransport/src/protocols/prod_protocol_rtc.cc new file mode 100644 index 000000000..8081923e3 --- /dev/null +++ b/libtransport/src/protocols/prod_protocol_rtc.cc @@ -0,0 +1,481 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include + +namespace transport { +namespace protocol { + +RTCProductionProtocol::RTCProductionProtocol( + implementation::ProducerSocket *icn_socket) + : ProductionProtocol(icn_socket), + current_seg_(1), + produced_bytes_(0), + produced_packets_(0), + max_packet_production_(1), + bytes_production_rate_(0), + packets_production_rate_(0), + last_round_(std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count()), + allow_delayed_nacks_(false), + queue_timer_on_(false), + consumer_in_sync_(false), + on_consumer_in_sync_(nullptr) { + srand((unsigned int)time(NULL)); + prod_label_ = rand() % 256; + interests_queue_timer_ = + std::make_unique(portal_->getIoService()); + round_timer_ = std::make_unique(portal_->getIoService()); + setOutputBufferSize(10000); + scheduleRoundTimer(); +} + +RTCProductionProtocol::~RTCProductionProtocol() {} + +void RTCProductionProtocol::registerNamespaceWithNetwork( + const Prefix &producer_namespace) { + ProductionProtocol::registerNamespaceWithNetwork(producer_namespace); + + flow_name_ = producer_namespace.getName(); + auto family = flow_name_.getAddressFamily(); + + switch (family) { + case AF_INET6: + header_size_ = (uint32_t)Packet::getHeaderSizeFromFormat(HF_INET6_TCP); + break; + case AF_INET: + header_size_ = (uint32_t)Packet::getHeaderSizeFromFormat(HF_INET_TCP); + break; + default: + throw errors::RuntimeException("Unknown name format."); + } +} + +void RTCProductionProtocol::scheduleRoundTimer() { + round_timer_->expires_from_now( + std::chrono::milliseconds(rtc::PRODUCER_STATS_INTERVAL)); + round_timer_->async_wait([this](std::error_code ec) { + if (ec) return; + updateStats(); + }); +} + +void RTCProductionProtocol::updateStats() { + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + uint64_t duration = now - last_round_; + if (duration == 0) duration = 1; + double per_second = rtc::MILLI_IN_A_SEC / duration; + + uint32_t prev_packets_production_rate = packets_production_rate_; + + bytes_production_rate_ = ceil((double)produced_bytes_ * per_second); + packets_production_rate_ = ceil((double)produced_packets_ * per_second); + + TRANSPORT_LOGD("Updating production rate: produced_bytes_ = %u bps = %u", + produced_bytes_, bytes_production_rate_); + + // update the production rate as soon as it increases by 10% with respect to + // the last round + max_packet_production_ = + produced_packets_ + ceil((double)produced_packets_ * 0.1); + if (max_packet_production_ < rtc::WIN_MIN) + max_packet_production_ = rtc::WIN_MIN; + + if (packets_production_rate_ != 0) { + allow_delayed_nacks_ = false; + } else if (prev_packets_production_rate == 0) { + // at least 2 rounds with production rate = 0 + allow_delayed_nacks_ = true; + } + + // check if the production rate is decreased. if yes send nacks if needed + if (prev_packets_production_rate < packets_production_rate_) { + sendNacksForPendingInterests(); + } + + produced_bytes_ = 0; + produced_packets_ = 0; + last_round_ = now; + scheduleRoundTimer(); +} + +uint32_t RTCProductionProtocol::produceStream( + const Name &content_name, std::unique_ptr &&buffer, + bool is_last, uint32_t start_offset) { + throw errors::NotImplementedException(); +} + +uint32_t RTCProductionProtocol::produceStream(const Name &content_name, + const uint8_t *buffer, + size_t buffer_size, bool is_last, + uint32_t start_offset) { + throw errors::NotImplementedException(); +} + +void RTCProductionProtocol::produce(ContentObject &content_object) { + throw errors::NotImplementedException(); +} + +uint32_t RTCProductionProtocol::produceDatagram( + const Name &content_name, std::unique_ptr &&buffer) { + std::size_t buffer_size = buffer->length(); + if (TRANSPORT_EXPECT_FALSE(buffer_size == 0)) return 0; + + uint32_t data_packet_size; + socket_->getSocketOption(interface::GeneralTransportOptions::DATA_PACKET_SIZE, + data_packet_size); + + if (TRANSPORT_EXPECT_FALSE((buffer_size + header_size_ + + rtc::DATA_HEADER_SIZE) > data_packet_size)) { + return 0; + } + + auto content_object = + core::PacketManager<>::getInstance().getPacket(); + // add rtc header to the payload + struct rtc::data_packet_t header; + content_object->appendPayload((const uint8_t *)&header, + rtc::DATA_HEADER_SIZE); + content_object->appendPayload(buffer->data(), buffer->length()); + + std::shared_ptr co = std::move(content_object); + + // schedule actual sending on internal thread + portal_->getIoService().dispatch( + [this, content_object{std::move(co)}, content_name]() mutable { + produceInternal(std::move(content_object), content_name); + }); + + return 1; +} + +void RTCProductionProtocol::produceInternal( + std::shared_ptr &&content_object, const Name &content_name) { + // set rtc header + struct rtc::data_packet_t *data_pkt = + (struct rtc::data_packet_t *)content_object->getPayload()->data(); + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + data_pkt->setTimestamp(now); + data_pkt->setProductionRate(bytes_production_rate_); + + // set hicn stuff + Name n(content_name); + content_object->setName(n.setSuffix(current_seg_)); + content_object->setLifetime(500); // XXX this should be set by the APP + content_object->setPathLabel(prod_label_); + + // update stats + produced_bytes_ += + content_object->headerSize() + content_object->payloadSize(); + produced_packets_++; + + if (produced_packets_ >= max_packet_production_) { + // in this case all the pending interests may be used to accomodate the + // sudden increase in the production rate. calling the updateStats we will + // notify all the clients + round_timer_->cancel(); + updateStats(); + } + + TRANSPORT_LOGD("Sending content object: %s", n.toString().c_str()); + + output_buffer_.insert(content_object); + + if (*on_content_object_in_output_buffer_) { + on_content_object_in_output_buffer_->operator()(*socket_->getInterface(), + *content_object); + } + + portal_->sendContentObject(*content_object); + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), + *content_object); + } + + // remove interests from the interest cache if it exists + removeFromInterestQueue(current_seg_); + + current_seg_ = (current_seg_ + 1) % rtc::MIN_PROBE_SEQ; +} + +void RTCProductionProtocol::onInterest(Interest &interest) { + uint32_t interest_seg = interest.getName().getSuffix(); + uint32_t lifetime = interest.getLifetime(); + + if (interest_seg == 0) { + // first packet from the consumer, reset sync state + consumer_in_sync_ = false; + } + + if (*on_interest_input_) { + on_interest_input_->operator()(*socket_->getInterface(), interest); + } + + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + + if (interest_seg > rtc::MIN_PROBE_SEQ) { + TRANSPORT_LOGD("received probe %u", interest_seg); + sendNack(interest_seg); + return; + } + + TRANSPORT_LOGD("received interest %u", interest_seg); + + const std::shared_ptr content_object = + output_buffer_.find(interest); + + if (content_object) { + if (*on_interest_satisfied_output_buffer_) { + on_interest_satisfied_output_buffer_->operator()(*socket_->getInterface(), + interest); + } + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), + *content_object); + } + + TRANSPORT_LOGD("Send content %u (onInterest)", + content_object->getName().getSuffix()); + portal_->sendContentObject(*content_object); + return; + } else { + if (*on_interest_process_) { + on_interest_process_->operator()(*socket_->getInterface(), interest); + } + } + + // if the production rate 0 use delayed nacks + if (allow_delayed_nacks_ && interest_seg >= current_seg_) { + uint64_t next_timer = ~0; + if (!timers_map_.empty()) { + next_timer = timers_map_.begin()->first; + } + + uint64_t expiration = now + rtc::SENTINEL_TIMER_INTERVAL; + addToInterestQueue(interest_seg, expiration); + + // here we have at least one interest in the queue, we need to start or + // update the timer + if (!queue_timer_on_) { + // set timeout + queue_timer_on_ = true; + scheduleQueueTimer(timers_map_.begin()->first - now); + } else { + // re-schedule the timer because a new interest will expires sooner + if (next_timer > timers_map_.begin()->first) { + interests_queue_timer_->cancel(); + scheduleQueueTimer(timers_map_.begin()->first - now); + } + } + return; + } + + if (queue_timer_on_) { + // the producer is producing. Send nacks to packets that will expire before + // the data production and remove the timer + queue_timer_on_ = false; + interests_queue_timer_->cancel(); + sendNacksForPendingInterests(); + } + + uint32_t max_gap = (uint32_t)floor( + (double)((double)((double)lifetime * + rtc::INTEREST_LIFETIME_REDUCTION_FACTOR / + rtc::MILLI_IN_A_SEC) * + (double)packets_production_rate_)); + + if (interest_seg < current_seg_ || interest_seg > (max_gap + current_seg_)) { + sendNack(interest_seg); + } else { + if (!consumer_in_sync_ && on_consumer_in_sync_) { + // we consider the remote consumer to be in sync as soon as it covers 70% + // of the production window with interests + uint32_t perc = ceil((double)max_gap * 0.7); + if (interest_seg > (perc + current_seg_)) { + consumer_in_sync_ = true; + on_consumer_in_sync_(*socket_->getInterface(), interest); + } + } + uint64_t expiration = + now + floor((double)lifetime * rtc::INTEREST_LIFETIME_REDUCTION_FACTOR); + addToInterestQueue(interest_seg, expiration); + } +} + +void RTCProductionProtocol::onError(std::error_code ec) {} + +void RTCProductionProtocol::scheduleQueueTimer(uint64_t wait) { + interests_queue_timer_->expires_from_now(std::chrono::milliseconds(wait)); + interests_queue_timer_->async_wait([this](std::error_code ec) { + if (ec) return; + interestQueueTimer(); + }); +} + +void RTCProductionProtocol::addToInterestQueue(uint32_t interest_seg, + uint64_t expiration) { + // check if the seq number exists already + auto it_seqs = seqs_map_.find(interest_seg); + if (it_seqs != seqs_map_.end()) { + // the seq already exists + if (expiration < it_seqs->second) { + // we need to update the timer becasue we got a smaller one + // 1) remove the entry from the multimap + // 2) update this entry + auto range = timers_map_.equal_range(it_seqs->second); + for (auto it_timers = range.first; it_timers != range.second; + it_timers++) { + if (it_timers->second == it_seqs->first) { + timers_map_.erase(it_timers); + break; + } + } + timers_map_.insert( + std::pair(expiration, interest_seg)); + it_seqs->second = expiration; + } else { + // nothing to do here + return; + } + } else { + // add the new seq + timers_map_.insert(std::pair(expiration, interest_seg)); + seqs_map_.insert(std::pair(interest_seg, expiration)); + } +} + +void RTCProductionProtocol::sendNacksForPendingInterests() { + std::unordered_set to_remove; + + uint32_t packet_gap = 100000; // set it to a high value (100sec) + if (packets_production_rate_ != 0) + packet_gap = ceil(rtc::MILLI_IN_A_SEC / (double)packets_production_rate_); + + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + + for (auto it = seqs_map_.begin(); it != seqs_map_.end(); it++) { + if (it->first > current_seg_) { + uint64_t production_time = + ((it->first - current_seg_) * packet_gap) + now; + if (production_time >= it->second) { + sendNack(it->first); + to_remove.insert(it->first); + } + } + } + + // delete nacked interests + for (auto it = to_remove.begin(); it != to_remove.end(); it++) { + removeFromInterestQueue(*it); + } +} + +void RTCProductionProtocol::removeFromInterestQueue(uint32_t interest_seg) { + auto seq_it = seqs_map_.find(interest_seg); + if (seq_it != seqs_map_.end()) { + auto range = timers_map_.equal_range(seq_it->second); + for (auto it_timers = range.first; it_timers != range.second; it_timers++) { + if (it_timers->second == seq_it->first) { + timers_map_.erase(it_timers); + break; + } + } + seqs_map_.erase(seq_it); + } +} + +void RTCProductionProtocol::interestQueueTimer() { + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + + for (auto it_timers = timers_map_.begin(); it_timers != timers_map_.end();) { + uint64_t expire = it_timers->first; + if (expire <= now) { + uint32_t seq = it_timers->second; + sendNack(seq); + // remove the interest from the other map + seqs_map_.erase(seq); + it_timers = timers_map_.erase(it_timers); + } else { + // stop, we are done! + break; + } + } + if (timers_map_.empty()) { + queue_timer_on_ = false; + } else { + queue_timer_on_ = true; + scheduleQueueTimer(timers_map_.begin()->first - now); + } +} + +void RTCProductionProtocol::sendNack(uint32_t sequence) { + auto nack = core::PacketManager<>::getInstance().getPacket(); + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + uint32_t next_packet = current_seg_; + uint32_t prod_rate = bytes_production_rate_; + + struct rtc::nack_packet_t header; + header.setTimestamp(now); + header.setProductionRate(prod_rate); + header.setProductionSegement(next_packet); + nack->appendPayload((const uint8_t *)&header, rtc::NACK_HEADER_SIZE); + + Name n(flow_name_); + n.setSuffix(sequence); + nack->setName(n); + nack->setLifetime(0); + nack->setPathLabel(prod_label_); + + if (!consumer_in_sync_ && on_consumer_in_sync_ && + sequence < rtc::MIN_PROBE_SEQ && sequence > next_packet) { + consumer_in_sync_ = true; + auto interest = core::PacketManager<>::getInstance().getPacket(); + interest->setName(n); + on_consumer_in_sync_(*socket_->getInterface(), *interest); + } + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), *nack); + } + + TRANSPORT_LOGD("Send nack %u", sequence); + portal_->sendContentObject(*nack); +} + +} // namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/prod_protocol_rtc.h b/libtransport/src/protocols/prod_protocol_rtc.h new file mode 100644 index 000000000..f3584f74a --- /dev/null +++ b/libtransport/src/protocols/prod_protocol_rtc.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 +#include + +#include +#include +#include + +namespace transport { +namespace protocol { + +class RTCProductionProtocol : public ProductionProtocol { + public: + RTCProductionProtocol(implementation::ProducerSocket *icn_socket); + ~RTCProductionProtocol() override; + + using ProductionProtocol::start; + using ProductionProtocol::stop; + + void produce(ContentObject &content_object) override; + uint32_t produceStream(const Name &content_name, + std::unique_ptr &&buffer, + bool is_last = true, + uint32_t start_offset = 0) override; + uint32_t produceStream(const Name &content_name, const uint8_t *buffer, + size_t buffer_size, bool is_last = true, + uint32_t start_offset = 0) override; + uint32_t produceDatagram(const Name &content_name, + std::unique_ptr &&buffer) override; + uint32_t produceDatagram(const Name &content_name, const uint8_t *buffer, + size_t buffer_size) override { + return produceDatagram(content_name, utils::MemBuf::wrapBuffer( + buffer, buffer_size, buffer_size)); + } + + void registerNamespaceWithNetwork(const Prefix &producer_namespace) override; + + void setConsumerInSyncCallback( + interface::ProducerInterestCallback &&callback) { + on_consumer_in_sync_ = std::move(callback); + } + + private: + // packet handlers + void onInterest(Interest &interest) override; + void onError(std::error_code ec) override; + void produceInternal(std::shared_ptr &&content_object, + const Name &content_name); + void sendNack(uint32_t sequence); + + // stats + void updateStats(); + void scheduleRoundTimer(); + + // pending intersts functions + void addToInterestQueue(uint32_t interest_seg, uint64_t expiration); + void sendNacksForPendingInterests(); + void removeFromInterestQueue(uint32_t interest_seg); + void scheduleQueueTimer(uint64_t wait); + void interestQueueTimer(); + + core::Name flow_name_; + + uint32_t current_seg_; // seq id of the next packet produced + uint32_t prod_label_; // path lable of the producer + uint16_t header_size_; // hicn header size + + uint32_t produced_bytes_; // bytes produced in the last round + uint32_t produced_packets_; // packet produed in the last round + + uint32_t max_packet_production_; // never exceed this number of packets + // without update stats + + uint32_t bytes_production_rate_; // bytes per sec + uint32_t packets_production_rate_; // pps + + std::unique_ptr round_timer_; + uint64_t last_round_; + + // delayed nacks are used by the producer to avoid to send too + // many nacks we the producer rate is 0. however, if the producer moves + // from a production rate higher than 0 to 0 the first round the dealyed + // should be avoided in order to notify the consumer as fast as possible + // of the new rate. + bool allow_delayed_nacks_; + + // queue for the received interests + // this map maps the expiration time of an interest to + // its sequence number. the map is sorted by timeouts + // the same timeout may be used for multiple sequence numbers + // but for each sequence number we store only the smallest + // expiry time. In this way the mapping from seqs_map_ to + // timers_map_ is unique + std::multimap timers_map_; + + // this map does the opposite, this map is not ordered + std::unordered_map seqs_map_; + bool queue_timer_on_; + std::unique_ptr interests_queue_timer_; + + // this callback is called when the remote consumer is in sync with high + // probability. it is called only the first time that the switch happen. + // XXX this makes sense only in P2P mode, while in standard mode is + // impossible to know the state of the consumers so it should not be used. + bool consumer_in_sync_; + interface::ProducerInterestCallback on_consumer_in_sync_; +}; + +} // namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/production_protocol.cc b/libtransport/src/protocols/production_protocol.cc new file mode 100644 index 000000000..8addf52d1 --- /dev/null +++ b/libtransport/src/protocols/production_protocol.cc @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace transport { + +namespace protocol { + +using namespace interface; + +ProductionProtocol::ProductionProtocol( + implementation::ProducerSocket *icn_socket) + : socket_(icn_socket), + is_running_(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) { + socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal_); + // TODO add statistics for producer + // socket_->getSocketOption(OtherOptions::STATISTICS, &stats_); +} + +ProductionProtocol::~ProductionProtocol() { + if (!is_async_ && is_running_) { + stop(); + } + + if (listening_thread_.joinable()) { + listening_thread_.join(); + } +} + +int ProductionProtocol::start() { + socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_INPUT, + &on_interest_input_); + socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_DROP, + &on_interest_dropped_input_buffer_); + socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_PASS, + &on_interest_inserted_input_buffer_); + socket_->getSocketOption(ProducerCallbacksOptions::CACHE_HIT, + &on_interest_satisfied_output_buffer_); + socket_->getSocketOption(ProducerCallbacksOptions::CACHE_MISS, + &on_interest_process_); + socket_->getSocketOption(ProducerCallbacksOptions::NEW_CONTENT_OBJECT, + &on_new_segment_); + socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_READY, + &on_content_object_in_output_buffer_); + socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_OUTPUT, + &on_content_object_output_); + socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_TO_SIGN, + &on_content_object_to_sign_); + socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_PRODUCED, + &on_content_produced_); + + socket_->getSocketOption(GeneralTransportOptions::ASYNC_MODE, is_async_); + + 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); + } + } + + is_running_ = true; + + if (!is_async_) { + listening_thread_ = std::thread([this]() { portal_->runEventsLoop(); }); + } + + return 0; +} + +void ProductionProtocol::stop() { + is_running_ = false; + + if (!is_async_) { + portal_->stopEventsLoop(); + } else { + portal_->clear(); + } +} + +void ProductionProtocol::produce(ContentObject &content_object) { + if (*on_content_object_in_output_buffer_) { + on_content_object_in_output_buffer_->operator()(*socket_->getInterface(), + content_object); + } + + output_buffer_.insert(std::static_pointer_cast( + content_object.shared_from_this())); + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), + content_object); + } + + portal_->sendContentObject(content_object); +} + +void ProductionProtocol::registerNamespaceWithNetwork( + const Prefix &producer_namespace) { + served_namespaces_.push_back(producer_namespace); +} + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/production_protocol.h b/libtransport/src/protocols/production_protocol.h new file mode 100644 index 000000000..780972321 --- /dev/null +++ b/libtransport/src/protocols/production_protocol.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 + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace transport { + +namespace protocol { + +using namespace core; + +class ProductionProtocol : public Portal::ProducerCallback { + public: + ProductionProtocol(implementation::ProducerSocket *icn_socket); + virtual ~ProductionProtocol(); + + bool isRunning() { return is_running_; } + + virtual int start(); + virtual void stop(); + + virtual void produce(ContentObject &content_object); + virtual uint32_t produceStream(const Name &content_name, + std::unique_ptr &&buffer, + bool is_last = true, + uint32_t start_offset = 0) = 0; + virtual uint32_t produceStream(const Name &content_name, + const uint8_t *buffer, size_t buffer_size, + bool is_last = true, + uint32_t start_offset = 0) = 0; + virtual uint32_t produceDatagram(const Name &content_name, + std::unique_ptr &&buffer) = 0; + virtual uint32_t produceDatagram(const Name &content_name, + const uint8_t *buffer, + size_t buffer_size) = 0; + + void setOutputBufferSize(std::size_t size) { output_buffer_.setLimit(size); } + std::size_t getOutputBufferSize() { return output_buffer_.getLimit(); } + + virtual void registerNamespaceWithNetwork(const Prefix &producer_namespace); + const std::list &getNamespaces() const { return served_namespaces_; } + + protected: + // Producer callback + virtual void onInterest(core::Interest &i) override = 0; + virtual void onError(std::error_code ec) override{}; + + protected: + implementation::ProducerSocket *socket_; + + // Thread pool responsible for IO operations (send data / receive interests) + std::vector io_threads_; + + // TODO remove this thread + std::thread listening_thread_; + std::shared_ptr portal_; + std::atomic is_running_; + interface::ProductionStatistics *stats_; + + // Callbacks + interface::ProducerInterestCallback *on_interest_input_; + interface::ProducerInterestCallback *on_interest_dropped_input_buffer_; + interface::ProducerInterestCallback *on_interest_inserted_input_buffer_; + interface::ProducerInterestCallback *on_interest_satisfied_output_buffer_; + interface::ProducerInterestCallback *on_interest_process_; + + interface::ProducerContentObjectCallback *on_new_segment_; + interface::ProducerContentObjectCallback *on_content_object_to_sign_; + interface::ProducerContentObjectCallback *on_content_object_in_output_buffer_; + interface::ProducerContentObjectCallback *on_content_object_output_; + interface::ProducerContentObjectCallback + *on_content_object_evicted_from_output_buffer_; + + interface::ProducerContentCallback *on_content_produced_; + + // Output buffer + utils::ContentStore output_buffer_; + + // List ot routes served by current producer protocol + std::list served_namespaces_; + + bool is_async_; +}; + +} // end namespace protocol +} // end namespace transport diff --git a/libtransport/src/protocols/raaqm.cc b/libtransport/src/protocols/raaqm.cc index 5023adf2e..bc8500227 100644 --- a/libtransport/src/protocols/raaqm.cc +++ b/libtransport/src/protocols/raaqm.cc @@ -13,6 +13,7 @@ * limitations under the License. */ +#include #include #include #include @@ -126,10 +127,6 @@ void RaaqmTransportProtocol::reset() { } } -bool RaaqmTransportProtocol::verifyKeyPackets() { - return index_manager_->onKeyToVerify(); -} - void RaaqmTransportProtocol::increaseWindow() { // return; double max_window_size = 0.; @@ -325,8 +322,8 @@ void RaaqmTransportProtocol::init() { is.close(); } -void RaaqmTransportProtocol::onContentObject( - Interest::Ptr &&interest, ContentObject::Ptr &&content_object) { +void RaaqmTransportProtocol::onContentObject(Interest &interest, + ContentObject &content_object) { // Check whether makes sense to continue if (TRANSPORT_EXPECT_FALSE(!is_running_)) { return; @@ -334,54 +331,53 @@ void RaaqmTransportProtocol::onContentObject( // Call application-defined callbacks if (*on_content_object_input_) { - (*on_content_object_input_)(*socket_->getInterface(), *content_object); + (*on_content_object_input_)(*socket_->getInterface(), content_object); } if (*on_interest_satisfied_) { - (*on_interest_satisfied_)(*socket_->getInterface(), *interest); + (*on_interest_satisfied_)(*socket_->getInterface(), interest); } - if (content_object->getPayloadType() == PayloadType::CONTENT_OBJECT) { - stats_->updateBytesRecv(content_object->payloadSize()); + if (content_object.getPayloadType() == PayloadType::DATA) { + stats_->updateBytesRecv(content_object.payloadSize()); } - onContentSegment(std::move(interest), std::move(content_object)); + onContentSegment(interest, content_object); scheduleNextInterests(); } -void RaaqmTransportProtocol::onContentSegment( - Interest::Ptr &&interest, ContentObject::Ptr &&content_object) { - uint32_t incremental_suffix = content_object->getName().getSuffix(); +void RaaqmTransportProtocol::onContentSegment(Interest &interest, + ContentObject &content_object) { + uint32_t incremental_suffix = content_object.getName().getSuffix(); // Decrease in-flight interests interests_in_flight_--; // Update stats if (!interest_retransmissions_[incremental_suffix & mask]) { - afterContentReception(*interest, *content_object); + afterContentReception(interest, content_object); } - index_manager_->onContentObject(std::move(interest), - std::move(content_object)); + index_manager_->onContentObject(interest, content_object); } -void RaaqmTransportProtocol::onPacketDropped( - Interest::Ptr &&interest, ContentObject::Ptr &&content_object) { +void RaaqmTransportProtocol::onPacketDropped(Interest &interest, + ContentObject &content_object) { uint32_t max_rtx = 0; socket_->getSocketOption(GeneralTransportOptions::MAX_INTEREST_RETX, max_rtx); - uint64_t segment = interest->getName().getSuffix(); + uint64_t segment = interest.getName().getSuffix(); if (TRANSPORT_EXPECT_TRUE(interest_retransmissions_[segment & mask] < max_rtx)) { stats_->updateRetxCount(1); if (*on_interest_retransmission_) { - (*on_interest_retransmission_)(*socket_->getInterface(), *interest); + (*on_interest_retransmission_)(*socket_->getInterface(), interest); } if (*on_interest_output_) { - (*on_interest_output_)(*socket_->getInterface(), *interest); + (*on_interest_output_)(*socket_->getInterface(), interest); } if (!is_running_) { @@ -389,7 +385,7 @@ void RaaqmTransportProtocol::onPacketDropped( } interest_retransmissions_[segment & mask]++; - interest_to_retransmit_.push(std::move(interest)); + interest_to_retransmit_.push(interest.shared_from_this()); } else { TRANSPORT_LOGE( "Stop: received not trusted packet %llu times", @@ -477,6 +473,11 @@ void RaaqmTransportProtocol::scheduleNextInterests() { sendInterest(std::move(interest_to_retransmit_.front())); interest_to_retransmit_.pop(); } else { + if (TRANSPORT_EXPECT_FALSE(!is_running_ && !is_first_)) { + TRANSPORT_LOGI("Adios"); + break; + } + index = index_manager_->getNextSuffix(); if (index == IndexManager::invalid_index) { break; @@ -487,8 +488,8 @@ void RaaqmTransportProtocol::scheduleNextInterests() { } } -bool RaaqmTransportProtocol::sendInterest(std::uint64_t next_suffix) { - auto interest = getPacket(); +void RaaqmTransportProtocol::sendInterest(std::uint64_t next_suffix) { + auto interest = core::PacketManager<>::getInstance().getPacket(); core::Name *name; socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name); name->setSuffix((uint32_t)next_suffix); @@ -502,19 +503,12 @@ bool RaaqmTransportProtocol::sendInterest(std::uint64_t next_suffix) { if (*on_interest_output_) { on_interest_output_->operator()(*socket_->getInterface(), *interest); } - - if (TRANSPORT_EXPECT_FALSE(!is_running_ && !is_first_)) { - return false; - } - // This is set to ~0 so that the next interest_retransmissions_ + 1, // performed by sendInterest, will result in 0 interest_retransmissions_[next_suffix & mask] = ~0; interest_timepoints_[next_suffix & mask] = utils::SteadyClock::now(); sendInterest(std::move(interest)); - - return true; } void RaaqmTransportProtocol::sendInterest(Interest::Ptr &&interest) { diff --git a/libtransport/src/protocols/raaqm.h b/libtransport/src/protocols/raaqm.h index fce4194d4..be477d39f 100644 --- a/libtransport/src/protocols/raaqm.h +++ b/libtransport/src/protocols/raaqm.h @@ -18,9 +18,9 @@ #include #include #include -#include #include #include +#include #include #include @@ -42,8 +42,6 @@ class RaaqmTransportProtocol : public TransportProtocol, void reset() override; - virtual bool verifyKeyPackets() override; - protected: static constexpr uint32_t buffer_size = 1 << interface::default_values::log_2_default_buffer_size; @@ -64,13 +62,12 @@ class RaaqmTransportProtocol : public TransportProtocol, private: void init(); - void onContentObject(Interest::Ptr &&i, ContentObject::Ptr &&c) override; + void onContentObject(Interest &i, ContentObject &c) override; - void onContentSegment(Interest::Ptr &&interest, - ContentObject::Ptr &&content_object); + void onContentSegment(Interest &interest, ContentObject &content_object); - void onPacketDropped(Interest::Ptr &&interest, - ContentObject::Ptr &&content_object) override; + void onPacketDropped(Interest &interest, + ContentObject &content_object) override; void onReassemblyFailed(std::uint32_t missing_segment) override; @@ -78,7 +75,7 @@ class RaaqmTransportProtocol : public TransportProtocol, virtual void scheduleNextInterests() override; - bool sendInterest(std::uint64_t next_suffix); + void sendInterest(std::uint64_t next_suffix); void sendInterest(Interest::Ptr &&interest); diff --git a/libtransport/src/protocols/raaqm_data_path.cc b/libtransport/src/protocols/raaqm_data_path.cc index 8bbbadcf2..f2c21b9ef 100644 --- a/libtransport/src/protocols/raaqm_data_path.cc +++ b/libtransport/src/protocols/raaqm_data_path.cc @@ -14,7 +14,6 @@ */ #include - #include namespace transport { diff --git a/libtransport/src/protocols/raaqm_data_path.h b/libtransport/src/protocols/raaqm_data_path.h index 3f037bc76..c0b53a690 100644 --- a/libtransport/src/protocols/raaqm_data_path.h +++ b/libtransport/src/protocols/raaqm_data_path.h @@ -16,7 +16,6 @@ #pragma once #include - #include #include diff --git a/libtransport/src/protocols/rate_estimation.cc b/libtransport/src/protocols/rate_estimation.cc index a2cf1aefe..5ca925760 100644 --- a/libtransport/src/protocols/rate_estimation.cc +++ b/libtransport/src/protocols/rate_estimation.cc @@ -15,7 +15,6 @@ #include #include - #include #include diff --git a/libtransport/src/protocols/rate_estimation.h b/libtransport/src/protocols/rate_estimation.h index 17f39e0b9..42ae74194 100644 --- a/libtransport/src/protocols/rate_estimation.h +++ b/libtransport/src/protocols/rate_estimation.h @@ -16,7 +16,6 @@ #pragma once #include - #include #include diff --git a/libtransport/src/protocols/reassembly.cc b/libtransport/src/protocols/reassembly.cc index c6602153c..0e59832dc 100644 --- a/libtransport/src/protocols/reassembly.cc +++ b/libtransport/src/protocols/reassembly.cc @@ -16,7 +16,6 @@ #include #include #include - #include #include #include diff --git a/libtransport/src/protocols/reassembly.h b/libtransport/src/protocols/reassembly.h index fdc9f2a05..385122c53 100644 --- a/libtransport/src/protocols/reassembly.h +++ b/libtransport/src/protocols/reassembly.h @@ -46,7 +46,7 @@ class Reassembly { virtual ~Reassembly() = default; - virtual void reassemble(core::ContentObject::Ptr &&content_object) = 0; + virtual void reassemble(core::ContentObject &content_object) = 0; virtual void reassemble( std::unique_ptr &&manifest) = 0; virtual void reInitialize() = 0; diff --git a/libtransport/src/protocols/rtc/CMakeLists.txt b/libtransport/src/protocols/rtc/CMakeLists.txt new file mode 100644 index 000000000..77f065d0e --- /dev/null +++ b/libtransport/src/protocols/rtc/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}/rtc.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_state.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_ldr.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_consts.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_queue.h + ${CMAKE_CURRENT_SOURCE_DIR}/probe_handler.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_packet.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rtc.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_state.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_ldr.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_queue.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.cc + ${CMAKE_CURRENT_SOURCE_DIR}/probe_handler.cc +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) diff --git a/libtransport/src/protocols/rtc/congestion_detection.cc b/libtransport/src/protocols/rtc/congestion_detection.cc new file mode 100644 index 000000000..e2d44ae66 --- /dev/null +++ b/libtransport/src/protocols/rtc/congestion_detection.cc @@ -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. + */ + +#include +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +CongestionDetection::CongestionDetection() + : cc_estimator_(), last_processed_chunk_() {} + +CongestionDetection::~CongestionDetection() {} + +void CongestionDetection::updateStats() { + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + + if (chunks_number_.empty()) return; + + uint32_t chunk_number = chunks_number_.front(); + + while (chunks_[chunk_number].getReceivedTime() + HICN_CC_STATS_MAX_DELAY_MS < + now || + chunks_[chunk_number].isComplete()) { + if (chunk_number == last_processed_chunk_.getFrameSeqNum() + 1) { + chunks_[chunk_number].setPreviousSentTime( + last_processed_chunk_.getSentTime()); + + chunks_[chunk_number].setPreviousReceivedTime( + last_processed_chunk_.getReceivedTime()); + cc_estimator_.Update(chunks_[chunk_number].getReceivedDelta(), + chunks_[chunk_number].getSentDelta(), + chunks_[chunk_number].getSentTime(), + chunks_[chunk_number].getReceivedTime(), + chunks_[chunk_number].getFrameSize(), true); + + } else { + TRANSPORT_LOGD( + "CongestionDetection::updateStats frame %u but not the \ + previous one, last one was %u currentFrame %u", + chunk_number, last_processed_chunk_.getFrameSeqNum(), + chunks_[chunk_number].getFrameSeqNum()); + } + + last_processed_chunk_ = chunks_[chunk_number]; + + chunks_.erase(chunk_number); + + chunks_number_.pop(); + if (chunks_number_.empty()) break; + + chunk_number = chunks_number_.front(); + } +} + +void CongestionDetection::addPacket(const core::ContentObject &content_object) { + auto payload = content_object.getPayload(); + uint32_t payload_size = (uint32_t)payload->length(); + uint32_t segmentNumber = content_object.getName().getSuffix(); + // uint32_t pkt = segmentNumber & modMask_; + uint64_t *sentTimePtr = (uint64_t *)payload->data(); + + // this is just for testing with hiperf, assuming a frame is 10 pkts + // in the final version, the split should be based on the timestamp in the pkt + uint32_t frameNum = (int)(segmentNumber / HICN_CC_STATS_CHUNK_SIZE); + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + + if (chunks_.find(frameNum) == chunks_.end()) { + // new chunk of pkts or out of order + if (last_processed_chunk_.getFrameSeqNum() > frameNum) + return; // out of order and we already processed the chunk + + chunks_[frameNum] = FrameStats(frameNum, HICN_CC_STATS_CHUNK_SIZE); + chunks_number_.push(frameNum); + } + + chunks_[frameNum].addPacket(*sentTimePtr, now, payload_size); +} + +} // namespace rtc +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/protocols/rtc/congestion_detection.h b/libtransport/src/protocols/rtc/congestion_detection.h new file mode 100644 index 000000000..17f4aa54c --- /dev/null +++ b/libtransport/src/protocols/rtc/congestion_detection.h @@ -0,0 +1,138 @@ +/* + * 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 +#include + +#include +#include + +#define HICN_CC_STATS_CHUNK_SIZE 10 +#define HICN_CC_STATS_MAX_DELAY_MS 100 + +namespace transport { + +namespace protocol { + +namespace rtc { + +class FrameStats { + public: + FrameStats() + : frame_num_(0), + sent_time_(0), + received_time_(0), + previous_sent_time_(0), + previous_received_time_(0), + size_(0), + received_pkt_m(0), + burst_size_m(HICN_CC_STATS_CHUNK_SIZE){}; + + FrameStats(uint32_t burst_size) + : frame_num_(0), + sent_time_(0), + received_time_(0), + previous_sent_time_(0), + previous_received_time_(0), + size_(0), + received_pkt_m(0), + burst_size_m(burst_size){}; + + FrameStats(uint32_t frame_num, uint32_t burst_size) + : frame_num_(frame_num), + sent_time_(0), + received_time_(0), + previous_sent_time_(0), + previous_received_time_(0), + size_(0), + received_pkt_m(0), + burst_size_m(burst_size){}; + + FrameStats(uint32_t frame_num, uint64_t sent_time, uint64_t received_time, + uint32_t size, FrameStats previousFrame, uint32_t burst_size) + : frame_num_(frame_num), + sent_time_(sent_time), + received_time_(received_time), + previous_sent_time_(previousFrame.getSentTime()), + previous_received_time_(previousFrame.getReceivedTime()), + size_(size), + received_pkt_m(1), + burst_size_m(burst_size){}; + + void addPacket(uint64_t sent_time, uint64_t received_time, uint32_t size) { + size_ += size; + sent_time_ = + (sent_time_ == 0) ? sent_time : std::min(sent_time_, sent_time); + received_time_ = std::max(received_time, received_time_); + received_pkt_m++; + } + + bool isComplete() { return received_pkt_m == burst_size_m; } + + uint32_t getFrameSeqNum() const { return frame_num_; } + uint64_t getSentTime() const { return sent_time_; } + uint64_t getReceivedTime() const { return received_time_; } + uint32_t getFrameSize() const { return size_; } + + void setPreviousReceivedTime(uint64_t time) { + previous_received_time_ = time; + } + void setPreviousSentTime(uint64_t time) { previous_sent_time_ = time; } + + // todo manage first frame + double getReceivedDelta() { + return static_cast(received_time_ - previous_received_time_); + } + double getSentDelta() { + return static_cast(sent_time_ - previous_sent_time_); + } + + private: + uint32_t frame_num_; + uint64_t sent_time_; + uint64_t received_time_; + + uint64_t previous_sent_time_; + uint64_t previous_received_time_; + uint32_t size_; + + uint32_t received_pkt_m; + uint32_t burst_size_m; +}; + +class CongestionDetection { + public: + CongestionDetection(); + ~CongestionDetection(); + + void addPacket(const core::ContentObject &content_object); + + BandwidthUsage getState() { return cc_estimator_.State(); } + + void updateStats(); + + private: + TrendlineEstimator cc_estimator_; + std::map chunks_; + std::queue chunks_number_; + + FrameStats last_processed_chunk_; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/probe_handler.cc b/libtransport/src/protocols/rtc/probe_handler.cc new file mode 100644 index 000000000..efba362d4 --- /dev/null +++ b/libtransport/src/protocols/rtc/probe_handler.cc @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +ProbeHandler::ProbeHandler(SendProbeCallback &&send_callback, + asio::io_service &io_service) + : probe_interval_(0), + max_probes_(0), + sent_probes_(0), + probe_timer_(std::make_unique(io_service)), + rand_eng_((std::random_device())()), + distr_(MIN_RTT_PROBE_SEQ, MAX_RTT_PROBE_SEQ), + send_probe_callback_(std::move(send_callback)) {} + +ProbeHandler::~ProbeHandler() {} + +uint64_t ProbeHandler::getRtt(uint32_t seq) { + auto it = pending_probes_.find(seq); + + if (it == pending_probes_.end()) return 0; + + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + uint64_t rtt = now - it->second; + if(rtt < 1) rtt = 1; + + pending_probes_.erase(it); + + return rtt; +} + +void ProbeHandler::setProbes(uint32_t probe_interval, uint32_t max_probes) { + stopProbes(); + probe_interval_ = probe_interval; + max_probes_ = max_probes; +} + +void ProbeHandler::stopProbes() { + probe_interval_ = 0; + max_probes_ = 0; + sent_probes_ = 0; + probe_timer_->cancel(); +} + +void ProbeHandler::sendProbes() { + if (probe_interval_ == 0) return; + if (max_probes_ != 0 && sent_probes_ >= max_probes_) return; + + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + + uint32_t seq = distr_(rand_eng_); + pending_probes_.insert(std::pair(seq, now)); + send_probe_callback_(seq); + sent_probes_++; + + // clean up + // a probe may get lost. if the pending_probes_ size becomes bigger than + // MAX_PENDING_PROBES remove all the probes older than a seconds + if (pending_probes_.size() > MAX_PENDING_PROBES) { + for (auto it = pending_probes_.begin(); it != pending_probes_.end();) { + if ((now - it->second) > 1000) + it = pending_probes_.erase(it); + else + it++; + } + } + + if (probe_interval_ == 0) return; + + std::weak_ptr self(shared_from_this()); + probe_timer_->expires_from_now(std::chrono::microseconds(probe_interval_)); + probe_timer_->async_wait([self](std::error_code ec) { + if (ec) return; + if (auto s = self.lock()) { + s->sendProbes(); + } + }); +} + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/probe_handler.h b/libtransport/src/protocols/rtc/probe_handler.h new file mode 100644 index 000000000..b8ed84445 --- /dev/null +++ b/libtransport/src/protocols/rtc/probe_handler.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include + +#include +#include +#include +#include +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +class ProbeHandler : public std::enable_shared_from_this { + public: + using SendProbeCallback = std::function; + + public: + ProbeHandler(SendProbeCallback &&send_callback, + asio::io_service &io_service); + + ~ProbeHandler(); + + // if the function returns 0 the probe is not valaid + uint64_t getRtt(uint32_t seq); + + // reset the probes parameters. it stop the current probing. + // to restar call sendProbes. + // probe_interval = 0 means that no event will be scheduled + // max_probe = 0 means no limit to the number of probe to send + void setProbes(uint32_t probe_interval, uint32_t max_probes); + + // stop to schedule probes + void stopProbes(); + + void sendProbes(); + + private: + uint32_t probe_interval_; // us + uint32_t max_probes_; // packets + uint32_t sent_probes_; // packets + + std::unique_ptr probe_timer_; + + // map from seqnumber to timestamp + std::unordered_map pending_probes_; + + // random generator + std::default_random_engine rand_eng_; + std::uniform_int_distribution distr_; + + SendProbeCallback send_probe_callback_; +}; + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc.cc b/libtransport/src/protocols/rtc/rtc.cc new file mode 100644 index 000000000..bb95ab686 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc.cc @@ -0,0 +1,607 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +using namespace interface; + +RTCTransportProtocol::RTCTransportProtocol( + implementation::ConsumerSocket *icn_socket) + : TransportProtocol(icn_socket, nullptr), + DatagramReassembly(icn_socket, this), + number_(0) { + icn_socket->getSocketOption(PORTAL, portal_); + round_timer_ = std::make_unique(portal_->getIoService()); + scheduler_timer_ = + std::make_unique(portal_->getIoService()); +} + +RTCTransportProtocol::~RTCTransportProtocol() {} + +void RTCTransportProtocol::resume() { + if (is_running_) return; + + is_running_ = true; + + newRound(); + + portal_->runEventsLoop(); + is_running_ = false; +} + +// private +void RTCTransportProtocol::initParams() { + portal_->setConsumerCallback(this); + + rc_ = std::make_shared(); + ldr_ = std::make_shared( + std::bind(&RTCTransportProtocol::sendRtxInterest, this, + std::placeholders::_1), + portal_->getIoService()); + + state_ = std::make_shared( + std::bind(&RTCTransportProtocol::sendProbeInterest, this, + std::placeholders::_1), + std::bind(&RTCTransportProtocol::discoveredRtt, this), + portal_->getIoService()); + + rc_->setState(state_); + // TODO: for the moment we keep the congestion control disabled + // rc_->tunrOnRateControl(); + ldr_->setState(state_); + + // protocol state + start_send_interest_ = false; + current_state_ = SyncState::catch_up; + + // Cancel timer + number_++; + round_timer_->cancel(); + scheduler_timer_->cancel(); + scheduler_timer_on_ = false; + + // delete all timeouts and future nacks + timeouts_or_nacks_.clear(); + + // cwin vars + current_sync_win_ = INITIAL_WIN; + max_sync_win_ = INITIAL_WIN_MAX; + + // names/packets var + next_segment_ = 0; + + socket_->setSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, + RTC_INTEREST_LIFETIME); +} + +// private +void RTCTransportProtocol::reset() { + TRANSPORT_LOGD("reset called"); + initParams(); + newRound(); +} + +void RTCTransportProtocol::inactiveProducer() { + // when the producer is inactive we reset the consumer state + // cwin vars + current_sync_win_ = INITIAL_WIN; + max_sync_win_ = INITIAL_WIN_MAX; + + TRANSPORT_LOGD("Current window: %u, max_sync_win_: %u", current_sync_win_, + max_sync_win_); + + // names/packets var + next_segment_ = 0; + + ldr_->clear(); +} + +void RTCTransportProtocol::newRound() { + round_timer_->expires_from_now(std::chrono::milliseconds(ROUND_LEN)); + // TODO pass weak_ptr here + round_timer_->async_wait([this, n{number_}](std::error_code ec) { + if (ec) return; + + if (n != number_) { + return; + } + + // saving counters that will be reset on new round + uint32_t sent_retx = state_->getSentRtxInRound(); + uint32_t received_bytes = state_->getReceivedBytesInRound(); + uint32_t sent_interest = state_->getSentInterestInRound(); + uint32_t lost_data = state_->getLostData(); + uint32_t recovered_losses = state_->getRecoveredLosses(); + uint32_t received_nacks = state_->getReceivedNacksInRound(); + + bool in_sync = (current_state_ == SyncState::in_sync); + state_->onNewRound((double)ROUND_LEN, in_sync); + rc_->onNewRound((double)ROUND_LEN); + + // update sync state if needed + if (current_state_ == SyncState::in_sync) { + double cache_rate = state_->getPacketFromCacheRatio(); + if (cache_rate > MAX_DATA_FROM_CACHE) { + current_state_ = SyncState::catch_up; + } + } else { + double target_rate = state_->getProducerRate() * PRODUCTION_RATE_FRACTION; + double received_rate = state_->getReceivedRate(); + uint32_t round_without_nacks = state_->getRoundsWithoutNacks(); + double cache_ratio = state_->getPacketFromCacheRatio(); + if (round_without_nacks >= ROUNDS_IN_SYNC_BEFORE_SWITCH && + received_rate >= target_rate && cache_ratio < MAX_DATA_FROM_CACHE) { + current_state_ = SyncState::in_sync; + } + } + + TRANSPORT_LOGD("Calling updateSyncWindow in newRound function"); + updateSyncWindow(); + + sendStatsToApp(sent_retx, received_bytes, sent_interest, lost_data, + recovered_losses, received_nacks); + newRound(); + }); +} + +void RTCTransportProtocol::discoveredRtt() { + start_send_interest_ = true; + ldr_->turnOnRTX(); + updateSyncWindow(); +} + +void RTCTransportProtocol::computeMaxSyncWindow() { + double production_rate = state_->getProducerRate(); + double packet_size = state_->getAveragePacketSize(); + if (production_rate == 0.0 || packet_size == 0.0) { + // the consumer has no info about the producer, + // keep the previous maxCWin + TRANSPORT_LOGD( + "Returning in computeMaxSyncWindow because: prod_rate: %d || " + "packet_size: %d", + (int)(production_rate == 0.0), (int)(packet_size == 0.0)); + return; + } + + uint32_t lifetime = default_values::interest_lifetime; + socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, + lifetime); + double lifetime_ms = (double)lifetime / MILLI_IN_A_SEC; + + + max_sync_win_ = + (uint32_t)ceil((production_rate * lifetime_ms * + INTEREST_LIFETIME_REDUCTION_FACTOR) / packet_size); + + max_sync_win_ = std::min(max_sync_win_, rc_->getCongesionWindow()); +} + +void RTCTransportProtocol::updateSyncWindow() { + computeMaxSyncWindow(); + + if (max_sync_win_ == INITIAL_WIN_MAX) { + if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive())) return; + + current_sync_win_ = INITIAL_WIN; + scheduleNextInterests(); + return; + } + + double prod_rate = state_->getProducerRate(); + double rtt = (double)state_->getRTT() / MILLI_IN_A_SEC; + double packet_size = state_->getAveragePacketSize(); + + // if some of the info are not available do not update the current win + if (prod_rate != 0.0 && rtt != 0.0 && packet_size != 0.0) { + current_sync_win_ = (uint32_t)ceil(prod_rate * rtt / packet_size); + current_sync_win_ += + ceil(prod_rate * (PRODUCER_BUFFER_MS / MILLI_IN_A_SEC) / packet_size); + + if(current_state_ == SyncState::catch_up) { + current_sync_win_ = current_sync_win_ * CATCH_UP_WIN_INCREMENT; + } + + current_sync_win_ = std::min(current_sync_win_, max_sync_win_); + current_sync_win_ = std::max(current_sync_win_, WIN_MIN); + } + + scheduleNextInterests(); +} + +void RTCTransportProtocol::decreaseSyncWindow() { + // called on future nack + // we have a new sample of the production rate, so update max win first + computeMaxSyncWindow(); + current_sync_win_--; + current_sync_win_ = std::max(current_sync_win_, WIN_MIN); + scheduleNextInterests(); +} + +void RTCTransportProtocol::sendInterest(Name *interest_name) { + TRANSPORT_LOGD("Sending interest for name %s", + interest_name->toString().c_str()); + + auto interest = core::PacketManager<>::getInstance().getPacket(); + interest->setName(*interest_name); + + uint32_t lifetime = default_values::interest_lifetime; + socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, + lifetime); + interest->setLifetime(uint32_t(lifetime)); + + if (*on_interest_output_) { + (*on_interest_output_)(*socket_->getInterface(), *interest); + } + + if (TRANSPORT_EXPECT_FALSE(!is_running_ && !is_first_)) { + return; + } + + portal_->sendInterest(std::move(interest)); +} + +void RTCTransportProtocol::sendRtxInterest(uint32_t seq) { + if (!is_running_ && !is_first_) return; + + if(!start_send_interest_) return; + + Name *interest_name = nullptr; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, + &interest_name); + + TRANSPORT_LOGD("send rtx %u", seq); + interest_name->setSuffix(seq); + sendInterest(interest_name); +} + +void RTCTransportProtocol::sendProbeInterest(uint32_t seq) { + if (!is_running_ && !is_first_) return; + + Name *interest_name = nullptr; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, + &interest_name); + + TRANSPORT_LOGD("send probe %u", seq); + interest_name->setSuffix(seq); + sendInterest(interest_name); +} + +void RTCTransportProtocol::scheduleNextInterests() { + TRANSPORT_LOGD("Schedule next interests"); + + if (!is_running_ && !is_first_) return; + + if(!start_send_interest_) return; // RTT discovering phase is not finished so + // do not start to send interests + + if (scheduler_timer_on_) return; // wait befor send other interests + + if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive())) { + TRANSPORT_LOGD("Inactive producer."); + // here we keep seding the same interest until the producer + // does not start again + if (next_segment_ != 0) { + // the producer just become inactive, reset the state + inactiveProducer(); + } + + Name *interest_name = nullptr; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, + &interest_name); + + TRANSPORT_LOGD("send interest %u", next_segment_); + interest_name->setSuffix(next_segment_); + + if (portal_->interestIsPending(*interest_name)) { + // if interest 0 is already pending we return + return; + } + + sendInterest(interest_name); + state_->onSendNewInterest(interest_name); + return; + } + + TRANSPORT_LOGD("Pending interest number: %d -- current_sync_win_: %d", + state_->getPendingInterestNumber(), current_sync_win_); + + // skip nacked pacekts + if (next_segment_ <= state_->getLastSeqNacked()) { + next_segment_ = state_->getLastSeqNacked() + 1; + } + + // skipe received packets + if (next_segment_ <= state_->getHighestSeqReceivedInOrder()) { + next_segment_ = state_->getHighestSeqReceivedInOrder() + 1; + } + + uint32_t sent_interests = 0; + while ((state_->getPendingInterestNumber() < current_sync_win_) && + (sent_interests < MAX_INTERESTS_IN_BATCH)) { + TRANSPORT_LOGD("In while loop. Window size: %u", current_sync_win_); + Name *interest_name = nullptr; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, + &interest_name); + + interest_name->setSuffix(next_segment_); + + // send the packet only if: + // 1) it is not pending yet (not true for rtx) + // 2) the packet is not received or lost + // 3) is not in the rtx list + if (portal_->interestIsPending(*interest_name) || + state_->isReceivedOrLost(next_segment_) != PacketState::UNKNOWN || + ldr_->isRtx(next_segment_)) { + TRANSPORT_LOGD( + "skip interest %u because: pending %u, recv %u, rtx %u", + next_segment_, (portal_->interestIsPending(*interest_name)), + (state_->isReceivedOrLost(next_segment_) != PacketState::UNKNOWN), + (ldr_->isRtx(next_segment_))); + next_segment_ = (next_segment_ + 1) % MIN_PROBE_SEQ; + continue; + } + + + sent_interests++; + TRANSPORT_LOGD("send interest %u", next_segment_); + sendInterest(interest_name); + state_->onSendNewInterest(interest_name); + + next_segment_ = (next_segment_ + 1) % MIN_PROBE_SEQ; + } + + if (state_->getPendingInterestNumber() < current_sync_win_) { + // we still have space in the window but we already sent a batch of + // MAX_INTERESTS_IN_BATCH interest. for the following ones wait one + // WAIT_BETWEEN_INTEREST_BATCHES to avoid local packets drop + + scheduler_timer_on_ = true; + scheduler_timer_->expires_from_now( + std::chrono::microseconds(WAIT_BETWEEN_INTEREST_BATCHES)); + scheduler_timer_->async_wait([this](std::error_code ec) { + if (ec) return; + if (!scheduler_timer_on_) return; + + scheduler_timer_on_ = false; + scheduleNextInterests(); + }); + } +} + +void RTCTransportProtocol::onTimeout(Interest::Ptr &&interest) { + uint32_t segment_number = interest->getName().getSuffix(); + + TRANSPORT_LOGD("timeout for packet %u", segment_number); + + if (segment_number >= MIN_PROBE_SEQ) { + // this is a timeout on a probe, do nothing + return; + } + + timeouts_or_nacks_.insert(segment_number); + + if (TRANSPORT_EXPECT_TRUE(state_->isProducerActive()) && + segment_number <= state_->getHighestSeqReceivedInOrder()) { + // we retransmit packets only if the producer is active, otherwise we + // use timeouts to avoid to send too much traffic + // + // a timeout is sent using RTX only if it is an old packet. if it is for a + // seq number that we didn't reach yet, we send the packet using the normal + // schedule next interest + TRANSPORT_LOGD("handle timeout for packet %u using rtx", segment_number); + ldr_->onTimeout(segment_number); + state_->onTimeout(segment_number); + scheduleNextInterests(); + return; + } + + TRANSPORT_LOGD("handle timeout for packet %u using normal interests", + segment_number); + + if (segment_number < next_segment_) { + // this is a timeout for a packet that will be generated in the future but + // we are asking for higher sequence numbers. we need to go back like in the + // case of future nacks + TRANSPORT_LOGD("on timeout next seg = %u, jump to %u", + next_segment_, segment_number); + next_segment_ = segment_number; + } + + state_->onTimeout(segment_number); + scheduleNextInterests(); +} + +void RTCTransportProtocol::onNack(const ContentObject &content_object) { + struct nack_packet_t *nack = + (struct nack_packet_t *)content_object.getPayload()->data(); + uint32_t production_seg = nack->getProductionSegement(); + uint32_t nack_segment = content_object.getName().getSuffix(); + bool is_rtx = ldr_->isRtx(nack_segment); + + // check if the packet got a timeout + + TRANSPORT_LOGD("Nack received %u. Production segment: %u", nack_segment, + production_seg); + + bool compute_stats = true; + auto tn_it = timeouts_or_nacks_.find(nack_segment); + if (tn_it != timeouts_or_nacks_.end() || is_rtx) { + compute_stats = false; + // remove packets from timeouts_or_nacks only in case of a past nack + } + + state_->onNackPacketReceived(content_object, compute_stats); + ldr_->onNackPacketReceived(content_object); + + // both in case of past and future nack we set next_segment_ equal to the + // production segment in the nack. In case of past nack we will skip unneded + // interest (this is already done in the scheduleNextInterest in any case) + // while in case of future nacks we can go back in time and ask again for the + // content that generated the nack + TRANSPORT_LOGD("on nack next seg = %u, jump to %u", + next_segment_, production_seg); + next_segment_ = production_seg; + + if (production_seg > nack_segment) { + // remove the nack is it exists + if (tn_it != timeouts_or_nacks_.end()) timeouts_or_nacks_.erase(tn_it); + + // the client is asking for content in the past + // switch to catch up state and increase the window + // this is true only if the packet is not an RTX + if (!is_rtx) current_state_ = SyncState::catch_up; + + updateSyncWindow(); + } else { + // if production_seg == nack_segment we consider this a future nack, since + // production_seg is not yet created. this may happen in case of low + // production rate (e.g. ping at 1pps) + + // if a future nack was also retransmitted add it to the timeout_or_nacks + // set + if (is_rtx) timeouts_or_nacks_.insert(nack_segment); + + // the client is asking for content in the future + // switch to in sync state and decrease the window + current_state_ = SyncState::in_sync; + decreaseSyncWindow(); + } +} + +void RTCTransportProtocol::onProbe(const ContentObject &content_object) { + bool valid = state_->onProbePacketReceived(content_object); + if(!valid) return; + + struct nack_packet_t *probe = + (struct nack_packet_t *)content_object.getPayload()->data(); + uint32_t production_seg = probe->getProductionSegement(); + + // as for the nacks set next_segment_ + TRANSPORT_LOGD("on probe next seg = %u, jump to %u", + next_segment_, production_seg); + next_segment_ = production_seg; + + ldr_->onProbePacketReceived(content_object); + updateSyncWindow(); +} + +void RTCTransportProtocol::onContentObject(Interest &interest, + ContentObject &content_object) { + TRANSPORT_LOGD("Received content object of size: %zu", + content_object.payloadSize()); + uint32_t payload_size = content_object.payloadSize(); + uint32_t segment_number = content_object.getName().getSuffix(); + + if (segment_number >= MIN_PROBE_SEQ) { + TRANSPORT_LOGD("Received probe %u", segment_number); + if (*on_content_object_input_) { + (*on_content_object_input_)(*socket_->getInterface(), content_object); + } + onProbe(content_object); + return; + } + + if (payload_size == NACK_HEADER_SIZE) { + TRANSPORT_LOGD("Received nack %u", segment_number); + if (*on_content_object_input_) { + (*on_content_object_input_)(*socket_->getInterface(), content_object); + } + onNack(content_object); + return; + } + + TRANSPORT_LOGD("Received content %u", segment_number); + + rc_->onDataPacketReceived(content_object); + bool compute_stats = true; + auto tn_it = timeouts_or_nacks_.find(segment_number); + if (tn_it != timeouts_or_nacks_.end()) { + compute_stats = false; + timeouts_or_nacks_.erase(tn_it); + } + if (ldr_->isRtx(segment_number)) { + compute_stats = false; + } + + // check if the packet was already received + PacketState state = state_->isReceivedOrLost(segment_number); + state_->onDataPacketReceived(content_object, compute_stats); + ldr_->onDataPacketReceived(content_object); + + // if the stat for this seq number is received do not send the packet to app + if (state != PacketState::RECEIVED) { + if (*on_content_object_input_) { + (*on_content_object_input_)(*socket_->getInterface(), content_object); + } + reassemble(content_object); + } else { + TRANSPORT_LOGD("Received duplicated content %u, drop it", segment_number); + } + + updateSyncWindow(); +} + +void RTCTransportProtocol::sendStatsToApp( + uint32_t retx_count, uint32_t received_bytes, uint32_t sent_interests, + uint32_t lost_data, uint32_t recovered_losses, uint32_t received_nacks) { + if (*stats_summary_) { + // Send the stats to the app + stats_->updateQueuingDelay(state_->getQueuing()); + + // stats_->updateInterestFecTx(0); //todo must be implemented + // stats_->updateBytesFecRecv(0); //todo must be implemented + + stats_->updateRetxCount(retx_count); + stats_->updateBytesRecv(received_bytes); + stats_->updateInterestTx(sent_interests); + stats_->updateReceivedNacks(received_nacks); + + stats_->updateAverageWindowSize(current_sync_win_); + stats_->updateLossRatio(state_->getLossRate()); + stats_->updateAverageRtt(state_->getRTT()); + stats_->updateLostData(lost_data); + stats_->updateRecoveredData(recovered_losses); + stats_->updateCCState((unsigned int)current_state_ ? 1 : 0); + (*stats_summary_)(*socket_->getInterface(), *stats_); + } +} + +void RTCTransportProtocol::reassemble(ContentObject &content_object) { + auto read_buffer = content_object.getPayload(); + TRANSPORT_LOGD("Size of payload: %zu", read_buffer->length()); + read_buffer->trimStart(DATA_HEADER_SIZE); + Reassembly::read_buffer_ = std::move(read_buffer); + Reassembly::notifyApplication(); +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc.h b/libtransport/src/protocols/rtc/rtc.h new file mode 100644 index 000000000..596887067 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RTCTransportProtocol : public TransportProtocol, + public DatagramReassembly { + public: + RTCTransportProtocol(implementation::ConsumerSocket *icnet_socket); + + ~RTCTransportProtocol(); + + using TransportProtocol::start; + + using TransportProtocol::stop; + + void resume() override; + + private: + enum class SyncState { catch_up = 0, in_sync = 1, last }; + + private: + // setup functions + void initParams(); + void reset() override; + + void inactiveProducer(); + + // protocol functions + void discoveredRtt(); + void newRound(); + + // window functions + void computeMaxSyncWindow(); + void updateSyncWindow(); + void decreaseSyncWindow(); + + // packet functions + void sendInterest(Name *interest_name); + void sendRtxInterest(uint32_t seq); + void sendProbeInterest(uint32_t seq); + void scheduleNextInterests() override; + void onTimeout(Interest::Ptr &&interest) override; + void onNack(const ContentObject &content_object); + void onProbe(const ContentObject &content_object); + void reassemble(ContentObject &content_object) override; + void onContentObject(Interest &interest, + ContentObject &content_object) override; + void onPacketDropped(Interest &interest, + ContentObject &content_object) override {} + void onReassemblyFailed(std::uint32_t missing_segment) override {} + + // interaction with app functions + void sendStatsToApp(uint32_t retx_count, uint32_t received_bytes, + uint32_t sent_interests, uint32_t lost_data, + uint32_t recovered_losses, uint32_t received_nacks); + // protocol state + bool start_send_interest_; + SyncState current_state_; + // cwin vars + uint32_t current_sync_win_; + uint32_t max_sync_win_; + + // controller var + std::unique_ptr round_timer_; + std::unique_ptr scheduler_timer_; + bool scheduler_timer_on_; + + // timeouts + std::unordered_set timeouts_or_nacks_; + + // names/packets var + uint32_t next_segment_; + + std::shared_ptr state_; + std::shared_ptr rc_; + std::shared_ptr ldr_; + + uint32_t number_; +}; + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_consts.h b/libtransport/src/protocols/rtc/rtc_consts.h new file mode 100644 index 000000000..0cf9516ab --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_consts.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +// used in rtc +// protocol consts +const uint32_t ROUND_LEN = 200; +// ms interval of time on which +// we take decisions / measurements +const double INTEREST_LIFETIME_REDUCTION_FACTOR = 0.8; +// how big (in ms) should be the buffer at the producer. +// increasing this number we increase the time that an +// interest will wait for the data packet to be produced +// at the producer socket +const uint32_t PRODUCER_BUFFER_MS = 200; // ms + +// interest scheduler +const uint32_t MAX_INTERESTS_IN_BATCH = 5; +const uint32_t WAIT_BETWEEN_INTEREST_BATCHES = 1000; // usec + +// packet const +const uint32_t HICN_HEADER_SIZE = 40 + 20; // IPv6 + TCP bytes +const uint32_t RTC_INTEREST_LIFETIME = 1000; + +// probes sequence range +const uint32_t MIN_PROBE_SEQ = 0xefffffff; +const uint32_t MIN_RTT_PROBE_SEQ = MIN_PROBE_SEQ; +const uint32_t MAX_RTT_PROBE_SEQ = 0xffffffff - 1; +// RTT_PROBE_INTERVAL will be used during the section while +// INIT_RTT_PROBE_INTERVAL is used at the beginning to +// quickily estimate the RTT +const uint32_t RTT_PROBE_INTERVAL = 200000; // us +const uint32_t INIT_RTT_PROBE_INTERVAL = 500; // us +const uint32_t INIT_RTT_PROBES = 40; // number of probes to init RTT +// if the produdcer is not yet started we need to probe multple times +// to get an answer. we wait 100ms between each try +const uint32_t INIT_RTT_PROBE_RESTART = 100; // ms +// once we get the first probe we wait at most 60ms for the others +const uint32_t INIT_RTT_PROBE_WAIT = 30; // ms +// we reuires at least 5 probes to be recevied +const uint32_t INIT_RTT_MIN_PROBES_TO_RECV = 5; //ms +const uint32_t MAX_PENDING_PROBES = 10; + + +// congestion +const double MAX_QUEUING_DELAY = 100.0; // ms + +// data from cache +const double MAX_DATA_FROM_CACHE = 0.25; // 25% + +// window const +const uint32_t INITIAL_WIN = 5; // pkts +const uint32_t INITIAL_WIN_MAX = 1000000; // pkts +const uint32_t WIN_MIN = 5; // pkts +const double CATCH_UP_WIN_INCREMENT = 1.2; +// used in rate control +const double WIN_DECREASE_FACTOR = 0.5; +const double WIN_INCREASE_FACTOR = 1.5; + +// round in congestion +const double ROUNDS_BEFORE_TAKE_ACTION = 5; + +// used in state +const uint8_t ROUNDS_IN_SYNC_BEFORE_SWITCH = 3; +const double PRODUCTION_RATE_FRACTION = 0.8; + +const uint32_t INIT_PACKET_SIZE = 1200; + +const double MOVING_AVG_ALPHA = 0.8; + +const double MILLI_IN_A_SEC = 1000.0; +const double MICRO_IN_A_SEC = 1000000.0; + +const double MAX_CACHED_PACKETS = 262144; // 2^18 + // about 50 sec of traffic at 50Mbps + // with 1200 bytes packets + +const uint32_t MAX_ROUND_WHIOUT_PACKETS = + (20 * MILLI_IN_A_SEC) / ROUND_LEN; // 20 sec in rounds; + +// used in ldr +const uint32_t RTC_MAX_RTX = 100; +const uint32_t RTC_MAX_AGE = 60000; // in ms +const uint64_t MAX_TIMER_RTX = ~0; +const uint32_t SENTINEL_TIMER_INTERVAL = 100; // ms +const uint32_t MAX_RTX_WITH_SENTINEL = 10; // packets +const double CATCH_UP_RTT_INCREMENT = 1.2; + +// used by producer +const uint32_t PRODUCER_STATS_INTERVAL = 200; // ms +const uint32_t MIN_PRODUCTION_RATE = 10; // pps + // min prod rate + // set running several test + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_data_path.cc b/libtransport/src/protocols/rtc/rtc_data_path.cc new file mode 100644 index 000000000..c098088a3 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_data_path.cc @@ -0,0 +1,197 @@ +/* + * 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 +#include + +#include +#include +#include + +#define MAX_ROUNDS_WITHOUT_PKTS 10 // 2sec + +namespace transport { + +namespace protocol { + +namespace rtc { + +RTCDataPath::RTCDataPath(uint32_t path_id) + : path_id_(path_id), + min_rtt(UINT_MAX), + prev_min_rtt(UINT_MAX), + min_owd(INT_MAX), // this is computed like in LEDBAT, so it is not the + // real OWD, but the measured one, that depends on the + // clock of sender and receiver. the only meaningful + // value is is the queueing delay. for this reason we + // keep both RTT (for the windowd calculation) and OWD + // (for congestion/quality control) + prev_min_owd(INT_MAX), + avg_owd(DBL_MAX), + queuing_delay(DBL_MAX), + jitter_(0.0), + last_owd_(0), + largest_recv_seq_(0), + largest_recv_seq_time_(0), + avg_inter_arrival_(DBL_MAX), + received_nacks_(false), + received_packets_(false), + rounds_without_packets_(0), + last_received_data_packet_(0), + RTT_history_(HISTORY_LEN), + OWD_history_(HISTORY_LEN){}; + +void RTCDataPath::insertRttSample(uint64_t rtt) { + // for the rtt we only keep track of the min one + if (rtt < min_rtt) min_rtt = rtt; + last_received_data_packet_ = + std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); +} + +void RTCDataPath::insertOwdSample(int64_t owd) { + // for owd we use both min and avg + if (owd < min_owd) min_owd = owd; + + if (avg_owd != DBL_MAX) + avg_owd = (avg_owd * (1 - ALPHA_RTC)) + (owd * ALPHA_RTC); + else { + avg_owd = owd; + } + + int64_t queueVal = owd - std::min(getMinOwd(), min_owd); + + if (queuing_delay != DBL_MAX) + queuing_delay = (queuing_delay * (1 - ALPHA_RTC)) + (queueVal * ALPHA_RTC); + else { + queuing_delay = queueVal; + } + + // keep track of the jitter computed as for RTP (RFC 3550) + int64_t diff = std::abs(owd - last_owd_); + last_owd_ = owd; + jitter_ += (1.0 / 16.0) * ((double)diff - jitter_); + + // owd is computed only for valid data packets so we count only + // this for decide if we recevie traffic or not + received_packets_ = true; +} + +void RTCDataPath::computeInterArrivalGap(uint32_t segment_number) { + // got packet in sequence, compute gap + if (largest_recv_seq_ == (segment_number - 1)) { + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + uint64_t delta = now - largest_recv_seq_time_; + largest_recv_seq_ = segment_number; + largest_recv_seq_time_ = now; + if (avg_inter_arrival_ == DBL_MAX) + avg_inter_arrival_ = delta; + else + avg_inter_arrival_ = + (avg_inter_arrival_ * (1 - ALPHA_RTC)) + (delta * ALPHA_RTC); + return; + } + + // ooo packet, update the stasts if needed + if (largest_recv_seq_ <= segment_number) { + largest_recv_seq_ = segment_number; + largest_recv_seq_time_ = + std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + } +} + +void RTCDataPath::receivedNack() { received_nacks_ = true; } + +double RTCDataPath::getInterArrivalGap() { + if (avg_inter_arrival_ == DBL_MAX) return 0; + return avg_inter_arrival_; +} + +bool RTCDataPath::isActive() { + if (received_nacks_ && rounds_without_packets_ < MAX_ROUNDS_WITHOUT_PKTS) + return true; + return false; +} + +bool RTCDataPath::pathToProducer() { + if (received_nacks_) return true; + return false; +} + +void RTCDataPath::roundEnd() { + // reset min_rtt and add it to the history + if (min_rtt != UINT_MAX) { + prev_min_rtt = min_rtt; + } else { + // this may happen if we do not receive any packet + // from this path in the last round. in this case + // we use the measure from the previuos round + min_rtt = prev_min_rtt; + } + + if (min_rtt == 0) min_rtt = 1; + + RTT_history_.pushBack(min_rtt); + min_rtt = UINT_MAX; + + // do the same for min owd + if (min_owd != INT_MAX) { + prev_min_owd = min_owd; + } else { + min_owd = prev_min_owd; + } + + if (min_owd != INT_MAX) { + OWD_history_.pushBack(min_owd); + min_owd = INT_MAX; + } + + if (!received_packets_) + rounds_without_packets_++; + else + rounds_without_packets_ = 0; + received_packets_ = false; +} + +uint32_t RTCDataPath::getPathId() { return path_id_; } + +double RTCDataPath::getQueuingDealy() { return queuing_delay; } + +uint64_t RTCDataPath::getMinRtt() { + if (RTT_history_.size() != 0) return RTT_history_.begin(); + return 0; +} + +int64_t RTCDataPath::getMinOwd() { + if (OWD_history_.size() != 0) return OWD_history_.begin(); + return 0; +} + +double RTCDataPath::getJitter() { return jitter_; } + +uint64_t RTCDataPath::getLastPacketTS() { return last_received_data_packet_; } + +void RTCDataPath::clearRtt() { RTT_history_.clear(); } + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_data_path.h b/libtransport/src/protocols/rtc/rtc_data_path.h new file mode 100644 index 000000000..c5c37fc0d --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_data_path.h @@ -0,0 +1,97 @@ +/* + * 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 +#include + +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +const double ALPHA_RTC = 0.125; +const uint32_t HISTORY_LEN = 20; // 4 sec + +class RTCDataPath { + public: + RTCDataPath(uint32_t path_id); + + public: + void insertRttSample(uint64_t rtt); + void insertOwdSample(int64_t owd); + void computeInterArrivalGap(uint32_t segment_number); + void receivedNack(); + + uint32_t getPathId(); + uint64_t getMinRtt(); + double getQueuingDealy(); + double getInterArrivalGap(); + double getJitter(); + bool isActive(); + bool pathToProducer(); + uint64_t getLastPacketTS(); + + void clearRtt(); + + void roundEnd(); + + private: + uint32_t path_id_; + + 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; + + double jitter_; + int64_t last_owd_; + + uint32_t largest_recv_seq_; + uint64_t largest_recv_seq_time_; + double avg_inter_arrival_; + + // flags to check if a path is active + // we considere a path active if it reaches a producer + //(not a cache) --aka we got at least one nack on this path-- + // and if we receives packets + bool received_nacks_; + bool received_packets_; + uint8_t rounds_without_packets_; // if we don't get any packet + // for MAX_ROUNDS_WITHOUT_PKTS + // we consider the path inactive + uint64_t last_received_data_packet_; // timestamp for the last data received + // on this path + + utils::MinFilter RTT_history_; + utils::MinFilter OWD_history_; +}; + +} // namespace rtc + +} // namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_ldr.cc b/libtransport/src/protocols/rtc/rtc_ldr.cc new file mode 100644 index 000000000..e91b29c04 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_ldr.cc @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +RTCLossDetectionAndRecovery::RTCLossDetectionAndRecovery( + SendRtxCallback &&callback, asio::io_service &io_service) + : rtx_on_(false), + next_rtx_timer_(MAX_TIMER_RTX), + last_event_(0), + sentinel_timer_interval_(MAX_TIMER_RTX), + send_rtx_callback_(std::move(callback)) { + timer_ = std::make_unique(io_service); + sentinel_timer_ = std::make_unique(io_service); +} + +RTCLossDetectionAndRecovery::~RTCLossDetectionAndRecovery() {} + +void RTCLossDetectionAndRecovery::turnOnRTX() { + rtx_on_ = true; + scheduleSentinelTimer(state_->getRTT() * CATCH_UP_RTT_INCREMENT); +} + +void RTCLossDetectionAndRecovery::turnOffRTX() { + rtx_on_ = false; + clear(); +} + +void RTCLossDetectionAndRecovery::onTimeout(uint32_t seq) { + // always add timeouts to the RTX list to avoid to send the same packet as if + // it was not a rtx + addToRetransmissions(seq, seq + 1); + last_event_ = getNow(); +} + +void RTCLossDetectionAndRecovery::onDataPacketReceived( + const core::ContentObject &content_object) { + last_event_ = getNow(); + + uint32_t seq = content_object.getName().getSuffix(); + if (deleteRtx(seq)) { + state_->onPacketRecovered(seq); + } else { + if (TRANSPORT_EXPECT_FALSE(!rtx_on_)) return; // do not add if RTX is off + TRANSPORT_LOGD("received data. add from %u to %u ", + state_->getHighestSeqReceivedInOrder() + 1, seq); + addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1, seq); + } +} + +void RTCLossDetectionAndRecovery::onNackPacketReceived( + const core::ContentObject &nack) { + last_event_ = getNow(); + + uint32_t seq = nack.getName().getSuffix(); + + if (TRANSPORT_EXPECT_FALSE(!rtx_on_)) return; // do not add if RTX is off + + struct nack_packet_t *nack_pkt = + (struct nack_packet_t *)nack.getPayload()->data(); + uint32_t production_seq = nack_pkt->getProductionSegement(); + + if (production_seq > seq) { + // this is a past nack, all data before productionSeq are lost. if + // productionSeq > state_->getHighestSeqReceivedInOrder() is impossible to + // recover any packet. If this is not the case we can try to recover the + // packets between state_->getHighestSeqReceivedInOrder() and productionSeq. + // e.g.: the client receives packets 8 10 11 9 where 9 is a nack with + // productionSeq = 14. 9 is lost but we can try to recover packets 12 13 and + // 14 that are not arrived yet + deleteRtx(seq); + TRANSPORT_LOGD("received past nack. add from %u to %u ", + state_->getHighestSeqReceivedInOrder() + 1, production_seq); + addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1, + production_seq); + } else { + // future nack. here there should be a gap between the last data received + // and this packet and is it possible to recover the packets between the + // last received data and the production seq. we should not use the seq + // number of the nack since we know that is too early to ask for this seq + // number + // e.g.: // e.g.: the client receives packets 10 11 12 20 where 20 is a nack + // with productionSeq = 18. this says that all the packets between 12 and 18 + // may got lost and we should ask them + deleteRtx(seq); + TRANSPORT_LOGD("received futrue nack. add from %u to %u ", + state_->getHighestSeqReceivedInOrder() + 1, production_seq); + addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1, + production_seq); + } +} + +void RTCLossDetectionAndRecovery::onProbePacketReceived( + const core::ContentObject &probe) { + // we don't log the reception of a probe packet for the sentinel timer because + // probes are not taken into account into the sync window. we use them as + // future nacks to detect possible packets lost + if (TRANSPORT_EXPECT_FALSE(!rtx_on_)) return; // do not add if RTX is off + struct nack_packet_t *probe_pkt = + (struct nack_packet_t *)probe.getPayload()->data(); + uint32_t production_seq = probe_pkt->getProductionSegement(); + TRANSPORT_LOGD("received probe. add from %u to %u ", + state_->getHighestSeqReceivedInOrder() + 1, production_seq); + addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1, + production_seq); +} + +void RTCLossDetectionAndRecovery::clear() { + rtx_state_.clear(); + rtx_timers_.clear(); + sentinel_timer_->cancel(); + if (next_rtx_timer_ != MAX_TIMER_RTX) { + next_rtx_timer_ = MAX_TIMER_RTX; + timer_->cancel(); + } +} + +void RTCLossDetectionAndRecovery::addToRetransmissions(uint32_t start, + uint32_t stop) { + // skip nacked packets + if (start <= state_->getLastSeqNacked()) { + start = state_->getLastSeqNacked() + 1; + } + + // skip received or lost packets + if (start <= state_->getHighestSeqReceivedInOrder()) { + start = state_->getHighestSeqReceivedInOrder() + 1; + } + + for (uint32_t seq = start; seq < stop; seq++) { + if (!isRtx(seq) && // is not already an rtx + // is not received or lost + state_->isReceivedOrLost(seq) == PacketState::UNKNOWN) { + // add rtx + rtxState state; + state.first_send_ = state_->getInterestSentTime(seq); + if (state.first_send_ == 0) // this interest was never sent before + state.first_send_ = getNow(); + state.next_send_ = computeNextSend(seq, true); + state.rtx_count_ = 0; + TRANSPORT_LOGD("add %u to retransmissions. next rtx is %lu ", seq, + (state.next_send_ - getNow())); + rtx_state_.insert(std::pair(seq, state)); + rtx_timers_.insert(std::pair(state.next_send_, seq)); + } + } + scheduleNextRtx(); +} + +uint64_t RTCLossDetectionAndRecovery::computeNextSend(uint32_t seq, + bool new_rtx) { + uint64_t now = getNow(); + if (new_rtx) { + // for the new rtx we wait one estimated IAT after the loss detection. this + // is bacause, assuming that packets arrive with a constant IAT, we should + // get a new packet every IAT + double prod_rate = state_->getProducerRate(); + uint32_t estimated_iat = SENTINEL_TIMER_INTERVAL; + uint32_t jitter = 0; + + if (prod_rate != 0) { + double packet_size = state_->getAveragePacketSize(); + estimated_iat = ceil(1000.0 / (prod_rate / packet_size)); + jitter = ceil(state_->getJitter()); + } + + uint32_t wait = estimated_iat + jitter; + TRANSPORT_LOGD("first rtx for %u in %u ms, rtt = %lu ait = %u jttr = %u", + seq, wait, state_->getRTT(), estimated_iat, jitter); + + return now + wait; + } else { + // wait one RTT + // however if the IAT is larger than the RTT, wait one IAT + uint32_t wait = SENTINEL_TIMER_INTERVAL; + + double prod_rate = state_->getProducerRate(); + if (prod_rate == 0) { + return now + SENTINEL_TIMER_INTERVAL; + } + + double packet_size = state_->getAveragePacketSize(); + uint32_t estimated_iat = ceil(1000.0 / (prod_rate / packet_size)); + + uint64_t rtt = state_->getRTT(); + if (rtt == 0) rtt = SENTINEL_TIMER_INTERVAL; + wait = rtt; + + if (estimated_iat > rtt) wait = estimated_iat; + + uint32_t jitter = ceil(state_->getJitter()); + wait += jitter; + + // it may happen that the channel is congested and we have some additional + // queuing delay to take into account + uint32_t queue = ceil(state_->getQueuing()); + wait += queue; + + TRANSPORT_LOGD( + "next rtx for %u in %u ms, rtt = %lu ait = %u jttr = %u queue = %u", + seq, wait, state_->getRTT(), estimated_iat, jitter, queue); + + return now + wait; + } +} + +void RTCLossDetectionAndRecovery::retransmit() { + if (rtx_timers_.size() == 0) return; + + uint64_t now = getNow(); + + auto it = rtx_timers_.begin(); + std::unordered_set lost_pkt; + uint32_t sent_counter = 0; + while (it != rtx_timers_.end() && it->first <= now && + sent_counter < MAX_INTERESTS_IN_BATCH) { + uint32_t seq = it->second; + auto rtx_it = + rtx_state_.find(seq); // this should always return a valid iter + if (rtx_it->second.rtx_count_ >= RTC_MAX_RTX || + (now - rtx_it->second.first_send_) >= RTC_MAX_AGE || + seq < state_->getLastSeqNacked()) { + // max rtx reached or packet too old or packet nacked, this packet is lost + TRANSPORT_LOGD( + "packet %u lost because 1) max rtx: %u 2) max age: %u 3) naked: %u", + seq, (rtx_it->second.rtx_count_ >= RTC_MAX_RTX), + ((now - rtx_it->second.first_send_) >= RTC_MAX_AGE), + (seq < state_->getLastSeqNacked())); + lost_pkt.insert(seq); + it++; + } else { + // resend the packet + state_->onRetransmission(seq); + double prod_rate = state_->getProducerRate(); + if (prod_rate != 0) rtx_it->second.rtx_count_++; + rtx_it->second.next_send_ = computeNextSend(seq, false); + it = rtx_timers_.erase(it); + rtx_timers_.insert( + std::pair(rtx_it->second.next_send_, seq)); + TRANSPORT_LOGD("send rtx for sequence %u, next send in %lu", seq, + (rtx_it->second.next_send_ - now)); + send_rtx_callback_(seq); + sent_counter++; + } + } + + // remove packets if needed + for (auto lost_it = lost_pkt.begin(); lost_it != lost_pkt.end(); lost_it++) { + uint32_t seq = *lost_it; + state_->onPacketLost(seq); + deleteRtx(seq); + } +} + +void RTCLossDetectionAndRecovery::scheduleNextRtx() { + if (rtx_timers_.size() == 0) { + // all the rtx were removed, reset timer + next_rtx_timer_ = MAX_TIMER_RTX; + return; + } + + // check if timer is alreay set + if (next_rtx_timer_ != MAX_TIMER_RTX) { + // a new check for rtx is already scheduled + if (next_rtx_timer_ > rtx_timers_.begin()->first) { + // we need to re-schedule it + timer_->cancel(); + } else { + // wait for the next timer + return; + } + } + + // set a new timer + next_rtx_timer_ = rtx_timers_.begin()->first; + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + uint64_t wait = 1; + if (next_rtx_timer_ != MAX_TIMER_RTX && next_rtx_timer_ > now) + wait = next_rtx_timer_ - now; + + std::weak_ptr self(shared_from_this()); + timer_->expires_from_now(std::chrono::milliseconds(wait)); + timer_->async_wait([self](std::error_code ec) { + if (ec) return; + if (auto s = self.lock()) { + s->retransmit(); + s->next_rtx_timer_ = MAX_TIMER_RTX; + s->scheduleNextRtx(); + } + }); +} + +bool RTCLossDetectionAndRecovery::deleteRtx(uint32_t seq) { + auto it_rtx = rtx_state_.find(seq); + if (it_rtx == rtx_state_.end()) return false; // rtx not found + + uint64_t ts = it_rtx->second.next_send_; + auto it_timers = rtx_timers_.find(ts); + while (it_timers != rtx_timers_.end() && it_timers->first == ts) { + if (it_timers->second == seq) { + rtx_timers_.erase(it_timers); + break; + } + it_timers++; + } + + bool lost = it_rtx->second.rtx_count_ > 0; + rtx_state_.erase(it_rtx); + + return lost; +} + +void RTCLossDetectionAndRecovery::scheduleSentinelTimer( + uint64_t expires_from_now) { + std::weak_ptr self(shared_from_this()); + sentinel_timer_->expires_from_now( + std::chrono::milliseconds(expires_from_now)); + sentinel_timer_->async_wait([self](std::error_code ec) { + if (ec) return; + if (auto s = self.lock()) { + s->sentinelTimer(); + } + }); +} + +void RTCLossDetectionAndRecovery::sentinelTimer() { + uint64_t now = getNow(); + + bool expired = false; + bool sent = false; + if ((now - last_event_) >= sentinel_timer_interval_) { + // at least a sentinel_timer_interval_ elapsed since last event + expired = true; + if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive())) { + // this happens at the beginning (or if the producer stops for some + // reason) we need to keep sending interest 0 until we get an answer + TRANSPORT_LOGD( + "sentinel timer: the producer is not active, send packet 0"); + state_->onRetransmission(0); + send_rtx_callback_(0); + } else { + TRANSPORT_LOGD( + "sentinel timer: the producer is active, send the 10 oldest packets"); + sent = true; + uint32_t rtx = 0; + auto it = state_->getPendingInterestsMapBegin(); + auto end = state_->getPendingInterestsMapEnd(); + while (it != end && rtx < MAX_RTX_WITH_SENTINEL) { + uint32_t seq = it->first; + TRANSPORT_LOGD("sentinel timer, add %u to the rtx list", seq); + addToRetransmissions(seq, seq + 1); + rtx++; + it++; + } + } + } else { + // sentinel timer did not expire because we registered at least one event + } + + uint32_t next_timer; + double prod_rate = state_->getProducerRate(); + if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive()) || prod_rate == 0) { + TRANSPORT_LOGD("next timer in %u", SENTINEL_TIMER_INTERVAL); + next_timer = SENTINEL_TIMER_INTERVAL; + } else { + double prod_rate = state_->getProducerRate(); + double packet_size = state_->getAveragePacketSize(); + uint32_t estimated_iat = ceil(1000.0 / (prod_rate / packet_size)); + uint32_t jitter = ceil(state_->getJitter()); + + // try to reduce the number of timers if the estimated IAT is too small + next_timer = std::max((estimated_iat + jitter) * 20, (uint32_t)1); + TRANSPORT_LOGD("next sentinel in %u ms, rate: %f, iat: %u, jitter: %u", + next_timer, ((prod_rate * 8.0) / 1000000.0), estimated_iat, + jitter); + + if (!expired) { + // discount the amout of time that is already passed + uint32_t discount = now - last_event_; + if (next_timer > discount) { + next_timer = next_timer - discount; + } else { + // in this case we trigger the timer in 1 ms + next_timer = 1; + } + TRANSPORT_LOGD("timer after discout: %u", next_timer); + } else if (sent) { + // wait at least one producer stats interval + owd to check if the + // production rate is reducing. + uint32_t min_wait = PRODUCER_STATS_INTERVAL + ceil(state_->getQueuing()); + next_timer = std::max(next_timer, min_wait); + TRANSPORT_LOGD("wait for updates from prod, next timer: %u", next_timer); + } + } + + scheduleSentinelTimer(next_timer); +} + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_ldr.h b/libtransport/src/protocols/rtc/rtc_ldr.h new file mode 100644 index 000000000..c0912303b --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_ldr.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RTCLossDetectionAndRecovery + : public std::enable_shared_from_this { + struct rtx_state_ { + uint64_t first_send_; + uint64_t next_send_; + uint32_t rtx_count_; + }; + + using rtxState = struct rtx_state_; + using SendRtxCallback = std::function; + + public: + RTCLossDetectionAndRecovery(SendRtxCallback &&callback, + asio::io_service &io_service); + + ~RTCLossDetectionAndRecovery(); + + void setState(std::shared_ptr state) { state_ = state; } + void turnOnRTX(); + void turnOffRTX(); + + void onTimeout(uint32_t seq); + void onDataPacketReceived(const core::ContentObject &content_object); + void onNackPacketReceived(const core::ContentObject &nack); + void onProbePacketReceived(const core::ContentObject &probe); + + void clear(); + + bool isRtx(uint32_t seq) { + if (rtx_state_.find(seq) != rtx_state_.end()) return true; + return false; + } + + private: + void addToRetransmissions(uint32_t start, uint32_t stop); + uint64_t computeNextSend(uint32_t seq, bool new_rtx); + void retransmit(); + void scheduleNextRtx(); + bool deleteRtx(uint32_t seq); + void scheduleSentinelTimer(uint64_t expires_from_now); + void sentinelTimer(); + + uint64_t getNow() { + using namespace std::chrono; + uint64_t now = + duration_cast(steady_clock::now().time_since_epoch()) + .count(); + return now; + } + + // this map keeps track of the retransmitted interest, ordered from the oldest + // to the newest one. the state contains the timer of the first send of the + // interest (from pendingIntetests_), the timer of the next send (key of the + // multimap) and the number of rtx + std::map rtx_state_; + // this map stored the rtx by timer. The key is the time at which the rtx + // should be sent, and the val is the interest seq number + std::multimap rtx_timers_; + + bool rtx_on_; + uint64_t next_rtx_timer_; + uint64_t last_event_; + uint64_t sentinel_timer_interval_; + std::unique_ptr timer_; + std::unique_ptr sentinel_timer_; + std::shared_ptr state_; + + SendRtxCallback send_rtx_callback_; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_packet.h b/libtransport/src/protocols/rtc/rtc_packet.h new file mode 100644 index 000000000..abb1323a3 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_packet.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + */ + +/* data packet + * +-----------------------------------------+ + * | uint64_t: timestamp | + * | | + * +-----------------------------------------+ + * | uint32_t: prod rate (bytes per sec) | + * +-----------------------------------------+ + * | payload | + * | ... | + */ + +/* nack packet + * +-----------------------------------------+ + * | uint64_t: timestamp | + * | | + * +-----------------------------------------+ + * | uint32_t: prod rate (bytes per sec) | + * +-----------------------------------------+ + * | uint32_t: current seg in production | + * +-----------------------------------------+ + */ + +#pragma once +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +inline uint64_t _ntohll(const uint64_t *input) { + uint64_t return_val; + uint8_t *tmp = (uint8_t *)&return_val; + + tmp[0] = *input >> 56; + tmp[1] = *input >> 48; + tmp[2] = *input >> 40; + tmp[3] = *input >> 32; + tmp[4] = *input >> 24; + tmp[5] = *input >> 16; + tmp[6] = *input >> 8; + tmp[7] = *input >> 0; + + return return_val; +} + +inline uint64_t _htonll(const uint64_t *input) { return (_ntohll(input)); } + +const uint32_t DATA_HEADER_SIZE = 12; // bytes + // XXX: sizeof(data_packet_t) is 16 + // beacuse of padding +const uint32_t NACK_HEADER_SIZE = 16; + +struct data_packet_t { + uint64_t timestamp; + uint32_t prod_rate; + + inline uint64_t getTimestamp() const { return _ntohll(×tamp); } + inline void setTimestamp(uint64_t time) { timestamp = _htonll(&time); } + + inline uint32_t getProductionRate() const { return ntohl(prod_rate); } + inline void setProductionRate(uint32_t rate) { prod_rate = htonl(rate); } +}; + +struct nack_packet_t { + uint64_t timestamp; + uint32_t prod_rate; + uint32_t prod_seg; + + inline uint64_t getTimestamp() const { return _ntohll(×tamp); } + inline void setTimestamp(uint64_t time) { timestamp = _htonll(&time); } + + inline uint32_t getProductionRate() const { return ntohl(prod_rate); } + inline void setProductionRate(uint32_t rate) { prod_rate = htonl(rate); } + + inline uint32_t getProductionSegement() const { return ntohl(prod_seg); } + inline void setProductionSegement(uint32_t seg) { prod_seg = htonl(seg); } +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rc.h b/libtransport/src/protocols/rtc/rtc_rc.h new file mode 100644 index 000000000..34d090092 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RTCRateControl : public std::enable_shared_from_this { + public: + RTCRateControl() + : rc_on_(false), + congestion_win_(1000000), // init the win to a large number + congestion_state_(CongestionState::Normal), + protocol_state_(nullptr) {} + + virtual ~RTCRateControl() = default; + + void turnOnRateControl() { rc_on_ = true; } + void setState(std::shared_ptr state) { protocol_state_ = state; }; + uint32_t getCongesionWindow() { return congestion_win_; }; + + virtual void onNewRound(double round_len) = 0; + virtual void onDataPacketReceived( + const core::ContentObject &content_object) = 0; + + protected: + enum class CongestionState { Normal = 0, Underuse = 1, Congested = 2, Last }; + + protected: + bool rc_on_; + uint32_t congestion_win_; + CongestionState congestion_state_; + + std::shared_ptr protocol_state_; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rc_frame.cc b/libtransport/src/protocols/rtc/rtc_rc_frame.cc new file mode 100644 index 000000000..b577b5bea --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc_frame.cc @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +RTCRateControlFrame::RTCRateControlFrame() : cc_detector_() {} + +RTCRateControlFrame::~RTCRateControlFrame() {} + +void RTCRateControlFrame::onNewRound(double round_len) { + if (!rc_on_) return; + + CongestionState prev_congestion_state = congestion_state_; + cc_detector_.updateStats(); + congestion_state_ = (CongestionState)cc_detector_.getState(); + + if (congestion_state_ == CongestionState::Congested) { + if (prev_congestion_state == CongestionState::Normal) { + // congestion detected, notify app and init congestion win + double prod_rate = protocol_state_->getReceivedRate(); + double rtt = (double)protocol_state_->getRTT() / MILLI_IN_A_SEC; + double packet_size = protocol_state_->getAveragePacketSize(); + + if (prod_rate == 0.0 || rtt == 0.0 || packet_size == 0.0) { + // TODO do something + return; + } + + congestion_win_ = (uint32_t)ceil(prod_rate * rtt / packet_size); + } + uint32_t win = congestion_win_ * WIN_DECREASE_FACTOR; + congestion_win_ = std::max(win, WIN_MIN); + return; + } +} + +void RTCRateControlFrame::onDataPacketReceived( + const core::ContentObject &content_object) { + if (!rc_on_) return; + + uint32_t seq = content_object.getName().getSuffix(); + if (!protocol_state_->isPending(seq)) return; + + cc_detector_.addPacket(content_object); +} + +void RTCRateControlFrame::receivedBwProbeTrain(uint64_t firts_probe_ts, + uint64_t last_probe_ts, + uint32_t total_probes) { + // TODO + return; +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rc_frame.h b/libtransport/src/protocols/rtc/rtc_rc_frame.h new file mode 100644 index 000000000..25d5ddbb6 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc_frame.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RTCRateControlFrame : public RTCRateControl { + public: + RTCRateControlFrame(); + + ~RTCRateControlFrame(); + + void onNewRound(double round_len); + void onDataPacketReceived(const core::ContentObject &content_object); + + void receivedBwProbeTrain(uint64_t firts_probe_ts, uint64_t last_probe_ts, + uint32_t total_probes); + + private: + CongestionDetection cc_detector_; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rc_queue.cc b/libtransport/src/protocols/rtc/rtc_rc_queue.cc new file mode 100644 index 000000000..a1c89e329 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc_queue.cc @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +RTCRateControlQueue::RTCRateControlQueue() + : rounds_since_last_drop_(0), + rounds_without_congestion_(0), + last_queue_(0) {} + +RTCRateControlQueue::~RTCRateControlQueue() {} + +void RTCRateControlQueue::onNewRound(double round_len) { + if (!rc_on_) return; + + double received_rate = protocol_state_->getReceivedRate(); + double target_rate = + protocol_state_->getProducerRate() * PRODUCTION_RATE_FRACTION; + double rtt = (double)protocol_state_->getRTT() / MILLI_IN_A_SEC; + double packet_size = protocol_state_->getAveragePacketSize(); + double queue = protocol_state_->getQueuing(); + + if (rtt == 0.0) return; // no info from the producer + + CongestionState prev_congestion_state = congestion_state_; + + if (prev_congestion_state == CongestionState::Normal && + received_rate >= target_rate) { + // if the queue is high in this case we are most likelly fighting with + // a TCP flow and there is enough bandwidth to match the producer rate + congestion_state_ = CongestionState::Normal; + } else if (queue > MAX_QUEUING_DELAY || last_queue_ == queue) { + // here we detect congestion. in the case that last_queue == queue + // the consumer didn't receive any packet from the producer so we + // consider this case as congestion + // TODO: wath happen in case of high loss rate? + congestion_state_ = CongestionState::Congested; + } else { + // nothing bad is happening + congestion_state_ = CongestionState::Normal; + } + + last_queue_ = queue; + + if (congestion_state_ == CongestionState::Congested) { + if (prev_congestion_state == CongestionState::Normal) { + // init the congetion window using the received rate + congestion_win_ = (uint32_t)ceil(received_rate * rtt / packet_size); + rounds_since_last_drop_ = ROUNDS_BEFORE_TAKE_ACTION + 1; + } + + if (rounds_since_last_drop_ >= ROUNDS_BEFORE_TAKE_ACTION) { + uint32_t win = congestion_win_ * WIN_DECREASE_FACTOR; + congestion_win_ = std::max(win, WIN_MIN); + rounds_since_last_drop_ = 0; + return; + } + + rounds_since_last_drop_++; + } + + if (congestion_state_ == CongestionState::Normal) { + if (prev_congestion_state == CongestionState::Congested) { + rounds_without_congestion_ = 0; + } + + rounds_without_congestion_++; + if (rounds_without_congestion_ < ROUNDS_BEFORE_TAKE_ACTION) return; + + congestion_win_ = congestion_win_ * WIN_INCREASE_FACTOR; + congestion_win_ = std::min(congestion_win_, INITIAL_WIN_MAX); + } +} + +void RTCRateControlQueue::onDataPacketReceived( + const core::ContentObject &content_object) { + // nothing to do + return; +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rc_queue.h b/libtransport/src/protocols/rtc/rtc_rc_queue.h new file mode 100644 index 000000000..407354d43 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc_queue.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RTCRateControlQueue : public RTCRateControl { + public: + RTCRateControlQueue(); + + ~RTCRateControlQueue(); + + void onNewRound(double round_len); + void onDataPacketReceived(const core::ContentObject &content_object); + + auto shared_from_this() { return utils::shared_from(this); } + + private: + uint32_t rounds_since_last_drop_; + uint32_t rounds_without_congestion_; + double last_queue_; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_state.cc b/libtransport/src/protocols/rtc/rtc_state.cc new file mode 100644 index 000000000..eabf8942c --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_state.cc @@ -0,0 +1,560 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +RTCState::RTCState(ProbeHandler::SendProbeCallback &&rtt_probes_callback, + DiscoveredRttCallback &&discovered_rtt_callback, + asio::io_service &io_service) + : rtt_probes_(std::make_shared( + std::move(rtt_probes_callback), io_service)), + discovered_rtt_callback_(std::move(discovered_rtt_callback)) { + init_rtt_timer_ = std::make_unique(io_service); + initParams(); +} + +RTCState::~RTCState() {} + +void RTCState::initParams() { + // packets counters (total) + sent_interests_ = 0; + sent_rtx_ = 0; + received_data_ = 0; + received_nacks_ = 0; + received_timeouts_ = 0; + received_probes_ = 0; + + // loss counters + packets_lost_ = 0; + losses_recovered_ = 0; + first_seq_in_round_ = 0; + highest_seq_received_ = 0; + highest_seq_received_in_order_ = 0; + last_seq_nacked_ = 0; + loss_rate_ = 0.0; + residual_loss_rate_ = 0.0; + + // bw counters + received_bytes_ = 0; + avg_packet_size_ = INIT_PACKET_SIZE; + production_rate_ = 0.0; + received_rate_ = 0.0; + + // nack counter + nack_on_last_round_ = false; + received_nacks_last_round_ = 0; + + // packets counter + received_packets_last_round_ = 0; + received_data_last_round_ = 0; + received_data_from_cache_ = 0; + data_from_cache_rate_ = 0; + sent_interests_last_round_ = 0; + sent_rtx_last_round_ = 0; + + // round conunters + rounds_ = 0; + rounds_without_nacks_ = 0; + rounds_without_packets_ = 0; + + last_production_seq_ = 0; + producer_is_active_ = false; + last_prod_update_ = 0; + + // paths stats + path_table_.clear(); + main_path_ = nullptr; + + // packet received + received_or_lost_packets_.clear(); + + // pending interests + pending_interests_.clear(); + + // init rtt + first_interest_sent_ = ~0; + init_rtt_ = false; + rtt_probes_->setProbes(INIT_RTT_PROBE_INTERVAL, INIT_RTT_PROBES); + rtt_probes_->sendProbes(); + setInitRttTimer(INIT_RTT_PROBE_RESTART); +} + +// packet events +void RTCState::onSendNewInterest(const core::Name *interest_name) { + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + uint32_t seq = interest_name->getSuffix(); + pending_interests_.insert(std::pair(seq, now)); + + if(sent_interests_ == 0) first_interest_sent_ = now; + + sent_interests_++; + sent_interests_last_round_++; +} + +void RTCState::onTimeout(uint32_t seq) { + auto it = pending_interests_.find(seq); + if (it != pending_interests_.end()) { + pending_interests_.erase(it); + } + received_timeouts_++; +} + +void RTCState::onRetransmission(uint32_t seq) { + // remove the interest for the pendingInterest map only after the first rtx. + // in this way we can handle the ooo packets that come in late as normla + // packet. we consider a packet lost only if we sent at least an RTX for it. + // XXX this may become problematic if we stop the RTX transmissions + auto it = pending_interests_.find(seq); + if (it != pending_interests_.end()) { + pending_interests_.erase(it); + packets_lost_++; + } + sent_rtx_++; + sent_rtx_last_round_++; +} + +void RTCState::onDataPacketReceived(const core::ContentObject &content_object, + bool compute_stats) { + uint32_t seq = content_object.getName().getSuffix(); + if (compute_stats) { + updatePathStats(content_object, false); + received_data_last_round_++; + } + received_data_++; + + struct data_packet_t *data_pkt = + (struct data_packet_t *)content_object.getPayload()->data(); + uint64_t production_time = data_pkt->getTimestamp(); + if (last_prod_update_ < production_time) { + last_prod_update_ = production_time; + uint32_t production_rate = data_pkt->getProductionRate(); + production_rate_ = (double)production_rate; + } + + updatePacketSize(content_object); + updateReceivedBytes(content_object); + addRecvOrLost(seq, PacketState::RECEIVED); + + if (seq > highest_seq_received_) highest_seq_received_ = seq; + + // the producer is responding + // it is generating valid data packets so we consider it active + producer_is_active_ = true; + + received_packets_last_round_++; +} + +void RTCState::onNackPacketReceived(const core::ContentObject &nack, + bool compute_stats) { + uint32_t seq = nack.getName().getSuffix(); + struct nack_packet_t *nack_pkt = + (struct nack_packet_t *)nack.getPayload()->data(); + uint64_t production_time = nack_pkt->getTimestamp(); + uint32_t production_seq = nack_pkt->getProductionSegement(); + uint32_t production_rate = nack_pkt->getProductionRate(); + + if (TRANSPORT_EXPECT_FALSE(main_path_ == nullptr) || + last_prod_update_ < production_time) { + // update production rate + last_prod_update_ = production_time; + last_production_seq_ = production_seq; + production_rate_ = (double)production_rate; + } + + if (compute_stats) { + // this is not an RTX + updatePathStats(nack, true); + nack_on_last_round_ = true; + } + + // for statistics pourpose we log all nacks, also the one received for + // retransmitted packets + received_nacks_++; + received_nacks_last_round_++; + + if (production_seq > seq) { + // old nack, seq is lost + // update last nacked + if (last_seq_nacked_ < seq) last_seq_nacked_ = seq; + TRANSPORT_LOGD("lost packet %u beacuse of a past nack", seq); + onPacketLost(seq); + } else if (seq > production_seq) { + // future nack + // remove the nack from the pending interest map + // (the packet is not received/lost yet) + pending_interests_.erase(seq); + } else { + // this should be a quite rear event. simply remove the + // packet from the pending interest list + pending_interests_.erase(seq); + } + + // the producer is responding + // we consider it active only if the production rate is not 0 + // or the production sequence number is not 1 + if (production_rate_ != 0 || production_seq != 1) { + producer_is_active_ = true; + } + + received_packets_last_round_++; +} + +void RTCState::onPacketLost(uint32_t seq) { + TRANSPORT_LOGD("packet %u is lost", seq); + auto it = pending_interests_.find(seq); + if (it != pending_interests_.end()) { + // this packet was never retransmitted so it does + // not appear in the loss count + packets_lost_++; + } + addRecvOrLost(seq, PacketState::LOST); +} + +void RTCState::onPacketRecovered(uint32_t seq) { + losses_recovered_++; + addRecvOrLost(seq, PacketState::RECEIVED); +} + +bool RTCState::onProbePacketReceived(const core::ContentObject &probe) { + uint32_t seq = probe.getName().getSuffix(); + uint64_t rtt; + + rtt = rtt_probes_->getRtt(seq); + + if (rtt == 0) return false; // this is not a valid probe + + // like for data and nacks update the path stats. Here the RTT is computed + // by the probe handler. Both probes for rtt and bw are good to esimate + // info on the path + uint32_t path_label = probe.getPathLabel(); + + auto path_it = path_table_.find(path_label); + + // update production rate and last_seq_nacked like in case of a nack + struct nack_packet_t *probe_pkt = + (struct nack_packet_t *)probe.getPayload()->data(); + uint64_t sender_timestamp = probe_pkt->getTimestamp(); + uint32_t production_seq = probe_pkt->getProductionSegement(); + uint32_t production_rate = probe_pkt->getProductionRate(); + + + if (path_it == path_table_.end()) { + // found a new path + std::shared_ptr newPath = + std::make_shared(path_label); + auto ret = path_table_.insert( + std::pair>(path_label, newPath)); + path_it = ret.first; + } + + auto path = path_it->second; + + path->insertRttSample(rtt); + path->receivedNack(); + + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + + int64_t OWD = now - sender_timestamp; + path->insertOwdSample(OWD); + + if (last_prod_update_ < sender_timestamp) { + last_production_seq_ = production_seq; + last_prod_update_ = sender_timestamp; + production_rate_ = (double)production_rate; + } + + // the producer is responding + // we consider it active only if the production rate is not 0 + // or the production sequence numner is not 1 + if (production_rate_ != 0 || production_seq != 1) { + producer_is_active_ = true; + } + + // check for init RTT. if received_probes_ is equal to 0 schedule a timer to + // wait for the INIT_RTT_PROBES. in this way if some probes get lost we don't + // wait forever + received_probes_++; + + if(!init_rtt_ && received_probes_ <= INIT_RTT_PROBES){ + if(received_probes_ == 1){ + // we got the first probe, wait at most INIT_RTT_PROBE_WAIT sec for the others + main_path_ = path; + setInitRttTimer(INIT_RTT_PROBE_WAIT); + } + if(received_probes_ == INIT_RTT_PROBES) { + // we are done + init_rtt_timer_->cancel(); + checkInitRttTimer(); + } + } + + received_packets_last_round_++; + + // ignore probes sent before the first interest + if((now - rtt) <= first_interest_sent_) return false; + return true; +} + +void RTCState::onNewRound(double round_len, bool in_sync) { + // XXX + // here we take into account only the single path case so we assume that we + // don't use two paths in parellel for this single flow + + if (path_table_.empty()) return; + + double bytes_per_sec = + ((double)received_bytes_ * (MILLI_IN_A_SEC / round_len)); + if(received_rate_ == 0) + received_rate_ = bytes_per_sec; + else + received_rate_ = (received_rate_ * MOVING_AVG_ALPHA) + + ((1 - MOVING_AVG_ALPHA) * bytes_per_sec); + + // search for an active path. There should be only one active path (meaning a + // path that leads to the producer socket -no cache- and from which we are + // currently getting data packets) at any time. However it may happen that + // there are mulitple active paths in case of mobility (the old path will + // remain active for a short ammount of time). The main path is selected as + // the active path from where the consumer received the latest data packet + + uint64_t last_packet_ts = 0; + main_path_ = nullptr; + + for (auto it = path_table_.begin(); it != path_table_.end(); it++) { + it->second->roundEnd(); + if (it->second->isActive()) { + uint64_t ts = it->second->getLastPacketTS(); + if (ts > last_packet_ts) { + last_packet_ts = ts; + main_path_ = it->second; + } + } + } + + if (in_sync) updateLossRate(); + + // handle nacks + if (!nack_on_last_round_ && received_bytes_ > 0) { + rounds_without_nacks_++; + } else { + rounds_without_nacks_ = 0; + } + + // check if the producer is active + if (received_packets_last_round_ != 0) { + rounds_without_packets_ = 0; + } else { + rounds_without_packets_++; + if (rounds_without_packets_ >= MAX_ROUND_WHIOUT_PACKETS && + producer_is_active_ != false) { + initParams(); + } + } + + // compute cache/producer ratio + if (received_data_last_round_ != 0) { + double new_rate = + (double)received_data_from_cache_ / (double)received_data_last_round_; + data_from_cache_rate_ = data_from_cache_rate_ * MOVING_AVG_ALPHA + + (new_rate * (1 - MOVING_AVG_ALPHA)); + } + + // reset counters + received_bytes_ = 0; + packets_lost_ = 0; + losses_recovered_ = 0; + first_seq_in_round_ = highest_seq_received_; + + nack_on_last_round_ = false; + received_nacks_last_round_ = 0; + + received_packets_last_round_ = 0; + received_data_last_round_ = 0; + received_data_from_cache_ = 0; + sent_interests_last_round_ = 0; + sent_rtx_last_round_ = 0; + + rounds_++; +} + +void RTCState::updateReceivedBytes(const core::ContentObject &content_object) { + received_bytes_ += + (uint32_t)(content_object.headerSize() + content_object.payloadSize()); +} + +void RTCState::updatePacketSize(const core::ContentObject &content_object) { + uint32_t pkt_size = + (uint32_t)(content_object.headerSize() + content_object.payloadSize()); + avg_packet_size_ = (MOVING_AVG_ALPHA * avg_packet_size_) + + ((1 - MOVING_AVG_ALPHA) * pkt_size); +} + +void RTCState::updatePathStats(const core::ContentObject &content_object, + bool is_nack) { + // get packet path + uint32_t path_label = content_object.getPathLabel(); + auto path_it = path_table_.find(path_label); + + if (path_it == path_table_.end()) { + // found a new path + std::shared_ptr newPath = + std::make_shared(path_label); + auto ret = path_table_.insert( + std::pair>(path_label, newPath)); + path_it = ret.first; + } + + auto path = path_it->second; + + // compute rtt + uint32_t seq = content_object.getName().getSuffix(); + uint64_t interest_sent_time = getInterestSentTime(seq); + if (interest_sent_time == 0) + return; // this should not happen, + // it means that we are processing an interest + // that is not pending + + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + + uint64_t RTT = now - interest_sent_time; + + path->insertRttSample(RTT); + + // compute OWD (the first part of the nack and data packet header are the + // same, so we cast to data data packet) + struct data_packet_t *packet = + (struct data_packet_t *)content_object.getPayload()->data(); + uint64_t sender_timestamp = packet->getTimestamp(); + int64_t OWD = now - sender_timestamp; + path->insertOwdSample(OWD); + + // compute IAT or set path to producer + if (!is_nack) { + // compute the iat only for the content packets + uint32_t segment_number = content_object.getName().getSuffix(); + path->computeInterArrivalGap(segment_number); + if (!path->pathToProducer()) received_data_from_cache_++; + } else { + path->receivedNack(); + } +} + +void RTCState::updateLossRate() { + loss_rate_ = 0.0; + residual_loss_rate_ = 0.0; + + uint32_t number_theorically_received_packets_ = + highest_seq_received_ - first_seq_in_round_; + + // in this case no new packet was recevied after the previuos round, avoid + // division by 0 + if (number_theorically_received_packets_ == 0) return; + + loss_rate_ = (double)((double)(packets_lost_) / + (double)number_theorically_received_packets_); + + residual_loss_rate_ = (double)((double)(packets_lost_ - losses_recovered_) / + (double)number_theorically_received_packets_); + + if (residual_loss_rate_ < 0) residual_loss_rate_ = 0; +} + +void RTCState::addRecvOrLost(uint32_t seq, PacketState state) { + pending_interests_.erase(seq); + if (received_or_lost_packets_.size() >= MAX_CACHED_PACKETS) { + received_or_lost_packets_.erase(received_or_lost_packets_.begin()); + } + // notice that it may happen that a packet that we consider lost arrives after + // some time, in this case we simply overwrite the packet state. + received_or_lost_packets_[seq] = state; + + // keep track of the last packet received/lost + // without holes. + if (highest_seq_received_in_order_ < last_seq_nacked_) { + highest_seq_received_in_order_ = last_seq_nacked_; + } + + if ((highest_seq_received_in_order_ + 1) == seq) { + highest_seq_received_in_order_ = seq; + } else if (seq <= highest_seq_received_in_order_) { + // here we do nothing + } else if (seq > highest_seq_received_in_order_) { + // 1) there is a gap in the sequence so we do not update largest_in_seq_ + // 2) all the packets from largest_in_seq_ to seq are in + // received_or_lost_packets_ an we upate largest_in_seq_ + + for (uint32_t i = highest_seq_received_in_order_ + 1; i <= seq; i++) { + if (received_or_lost_packets_.find(i) == + received_or_lost_packets_.end()) { + break; + } + // this packet is in order so we can update the + // highest_seq_received_in_order_ + highest_seq_received_in_order_ = i; + } + } +} + +void RTCState::setInitRttTimer(uint32_t wait){ + init_rtt_timer_->cancel(); + init_rtt_timer_->expires_from_now(std::chrono::milliseconds(wait)); + init_rtt_timer_->async_wait([this](std::error_code ec) { + if(ec) return; + checkInitRttTimer(); + }); +} + +void RTCState::checkInitRttTimer() { + if(received_probes_ < INIT_RTT_MIN_PROBES_TO_RECV){ + // we didn't received enough probes, restart + received_probes_ = 0; + rtt_probes_->setProbes(INIT_RTT_PROBE_INTERVAL, INIT_RTT_PROBES); + rtt_probes_->sendProbes(); + setInitRttTimer(INIT_RTT_PROBE_RESTART); + return; + } + init_rtt_ = true; + main_path_->roundEnd(); + rtt_probes_->setProbes(RTT_PROBE_INTERVAL, 0); + rtt_probes_->sendProbes(); + + // init last_seq_nacked_. skip packets that may come from the cache + double prod_rate = getProducerRate(); + double rtt = (double)getRTT() / MILLI_IN_A_SEC; + double packet_size = getAveragePacketSize(); + uint32_t pkt_in_rtt_ = std::floor(((prod_rate / packet_size) * rtt) * 0.8); + last_seq_nacked_ = last_production_seq_ + pkt_in_rtt_; + + discovered_rtt_callback_(); +} + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_state.h b/libtransport/src/protocols/rtc/rtc_state.h new file mode 100644 index 000000000..943a0a113 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_state.h @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +enum class PacketState : uint8_t { RECEIVED, LOST, UNKNOWN }; + +class RTCState : std::enable_shared_from_this { + public: + using DiscoveredRttCallback = std::function; + public: + RTCState(ProbeHandler::SendProbeCallback &&rtt_probes_callback, + DiscoveredRttCallback &&discovered_rtt_callback, + asio::io_service &io_service); + + ~RTCState(); + + // packet events + void onSendNewInterest(const core::Name *interest_name); + void onTimeout(uint32_t seq); + void onRetransmission(uint32_t seq); + void onDataPacketReceived(const core::ContentObject &content_object, + bool compute_stats); + void onNackPacketReceived(const core::ContentObject &nack, + bool compute_stats); + void onPacketLost(uint32_t seq); + void onPacketRecovered(uint32_t seq); + bool onProbePacketReceived(const core::ContentObject &probe); + + // protocol state + void onNewRound(double round_len, bool in_sync); + + // main path + uint32_t getProducerPath() const { + if (mainPathIsValid()) return main_path_->getPathId(); + return 0; + } + + // delay metrics + bool isRttDiscovered() const { + return init_rtt_; + } + + uint64_t getRTT() const { + if (mainPathIsValid()) return main_path_->getMinRtt(); + return 0; + } + void resetRttStats() { + if (mainPathIsValid()) main_path_->clearRtt(); + } + + double getQueuing() const { + if (mainPathIsValid()) return main_path_->getQueuingDealy(); + return 0.0; + } + double getIAT() const { + if (mainPathIsValid()) return main_path_->getInterArrivalGap(); + return 0.0; + } + + double getJitter() const { + if (mainPathIsValid()) return main_path_->getJitter(); + return 0.0; + } + + // pending interests + uint64_t getInterestSentTime(uint32_t seq) { + auto it = pending_interests_.find(seq); + if (it != pending_interests_.end()) return it->second; + return 0; + } + bool isPending(uint32_t seq) { + if (pending_interests_.find(seq) != pending_interests_.end()) return true; + return false; + } + uint32_t getPendingInterestNumber() const { + return pending_interests_.size(); + } + PacketState isReceivedOrLost(uint32_t seq) { + auto it = received_or_lost_packets_.find(seq); + if (it != received_or_lost_packets_.end()) return it->second; + return PacketState::UNKNOWN; + } + + // loss rate + double getLossRate() const { return loss_rate_; } + double getResidualLossRate() const { return residual_loss_rate_; } + uint32_t getHighestSeqReceivedInOrder() const { + return highest_seq_received_in_order_; + } + uint32_t getLostData() const { return packets_lost_; }; + uint32_t getRecoveredLosses() const { return losses_recovered_; } + + // generic stats + uint32_t getReceivedBytesInRound() const { return received_bytes_; } + uint32_t getReceivedNacksInRound() const { + return received_nacks_last_round_; + } + uint32_t getSentInterestInRound() const { return sent_interests_last_round_; } + uint32_t getSentRtxInRound() const { return sent_rtx_last_round_; } + + // bandwidth/production metrics + double getAvailableBw() const { return 0.0; }; // TODO + double getProducerRate() const { return production_rate_; } + double getReceivedRate() const { return received_rate_; } + double getAveragePacketSize() const { return avg_packet_size_; } + + // nacks + uint32_t getRoundsWithoutNacks() const { return rounds_without_nacks_; } + uint32_t getLastSeqNacked() const { return last_seq_nacked_; } + + // producer state + bool isProducerActive() const { return producer_is_active_; } + + // packets from cache + double getPacketFromCacheRatio() const { return data_from_cache_rate_; } + + std::map::iterator getPendingInterestsMapBegin() { + return pending_interests_.begin(); + } + std::map::iterator getPendingInterestsMapEnd() { + return pending_interests_.end(); + } + + private: + void initParams(); + + // update stats + void updateState(); + void updateReceivedBytes(const core::ContentObject &content_object); + void updatePacketSize(const core::ContentObject &content_object); + void updatePathStats(const core::ContentObject &content_object, bool is_nack); + void updateLossRate(); + + void addRecvOrLost(uint32_t seq, PacketState state); + + void setInitRttTimer(uint32_t wait); + void checkInitRttTimer(); + + bool mainPathIsValid() const { + if (main_path_ != nullptr) + return true; + else + return false; + } + + // packets counters (total) + uint32_t sent_interests_; + uint32_t sent_rtx_; + uint32_t received_data_; + uint32_t received_nacks_; + uint32_t received_timeouts_; + uint32_t received_probes_; + + // loss counters + int32_t packets_lost_; + int32_t losses_recovered_; + uint32_t first_seq_in_round_; + uint32_t highest_seq_received_; + uint32_t highest_seq_received_in_order_; + uint32_t last_seq_nacked_; // segment for which we got an oldNack + double loss_rate_; + double residual_loss_rate_; + + // bw counters + uint32_t received_bytes_; + double avg_packet_size_; + double production_rate_; // rate communicated by the producer using nacks + double received_rate_; // rate recevied by the consumer + + // nack counter + // the bool takes tracks only about the valid nacks (no rtx) and it is used to + // switch between the states. Instead received_nacks_last_round_ logs all the + // nacks for statistics + bool nack_on_last_round_; + uint32_t received_nacks_last_round_; + + // packets counter + uint32_t received_packets_last_round_; + uint32_t received_data_last_round_; + uint32_t received_data_from_cache_; + double data_from_cache_rate_; + uint32_t sent_interests_last_round_; + uint32_t sent_rtx_last_round_; + + // round conunters + uint32_t rounds_; + uint32_t rounds_without_nacks_; + uint32_t rounds_without_packets_; + + // init rtt + uint64_t first_interest_sent_; + + // producer state + bool + producer_is_active_; // the prodcuer is active if we receive some packets + uint32_t last_production_seq_; // last production seq received by the producer + uint64_t last_prod_update_; // timestamp of the last packets used to update + // stats from the producer + + // paths stats + std::unordered_map> path_table_; + std::shared_ptr main_path_; + + // packet received + // cache where to store info about the last MAX_CACHED_PACKETS + std::map received_or_lost_packets_; + + // pending interests + std::map pending_interests_; + + // probes + std::shared_ptr rtt_probes_; + bool init_rtt_; + std::unique_ptr init_rtt_timer_; + + // callbacks + DiscoveredRttCallback discovered_rtt_callback_; +}; + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/trendline_estimator.cc b/libtransport/src/protocols/rtc/trendline_estimator.cc new file mode 100644 index 000000000..7a0803857 --- /dev/null +++ b/libtransport/src/protocols/rtc/trendline_estimator.cc @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// FROM +// https://source.chromium.org/chromium/chromium/src/+/master:third_party/webrtc/modules/congestion_controller/goog_cc/trendline_estimator.cc + +#include "trendline_estimator.h" + +#include + +#include +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +// Parameters for linear least squares fit of regression line to noisy data. +constexpr double kDefaultTrendlineSmoothingCoeff = 0.9; +constexpr double kDefaultTrendlineThresholdGain = 4.0; +// const char kBweWindowSizeInPacketsExperiment[] = +// "WebRTC-BweWindowSizeInPackets"; + +/*size_t ReadTrendlineFilterWindowSize( + const WebRtcKeyValueConfig* key_value_config) { + std::string experiment_string = + key_value_config->Lookup(kBweWindowSizeInPacketsExperiment); + size_t window_size; + int parsed_values = + sscanf(experiment_string.c_str(), "Enabled-%zu", &window_size); + if (parsed_values == 1) { + if (window_size > 1) + return window_size; + RTC_LOG(WARNING) << "Window size must be greater than 1."; + } + RTC_LOG(LS_WARNING) << "Failed to parse parameters for BweWindowSizeInPackets" + " experiment from field trial string. Using default."; + return TrendlineEstimatorSettings::kDefaultTrendlineWindowSize; +} +*/ + +OptionalDouble LinearFitSlope( + const std::deque& packets) { + // RTC_DCHECK(packets.size() >= 2); + // Compute the "center of mass". + double sum_x = 0; + double sum_y = 0; + for (const auto& packet : packets) { + sum_x += packet.arrival_time_ms; + sum_y += packet.smoothed_delay_ms; + } + double x_avg = sum_x / packets.size(); + double y_avg = sum_y / packets.size(); + // Compute the slope k = \sum (x_i-x_avg)(y_i-y_avg) / \sum (x_i-x_avg)^2 + double numerator = 0; + double denominator = 0; + for (const auto& packet : packets) { + double x = packet.arrival_time_ms; + double y = packet.smoothed_delay_ms; + numerator += (x - x_avg) * (y - y_avg); + denominator += (x - x_avg) * (x - x_avg); + } + if (denominator == 0) return OptionalDouble(); + return OptionalDouble(numerator / denominator); +} + +OptionalDouble ComputeSlopeCap( + const std::deque& packets, + const TrendlineEstimatorSettings& settings) { + /*RTC_DCHECK(1 <= settings.beginning_packets && + settings.beginning_packets < packets.size()); + RTC_DCHECK(1 <= settings.end_packets && + settings.end_packets < packets.size()); + RTC_DCHECK(settings.beginning_packets + settings.end_packets <= + packets.size());*/ + TrendlineEstimator::PacketTiming early = packets[0]; + for (size_t i = 1; i < settings.beginning_packets; ++i) { + if (packets[i].raw_delay_ms < early.raw_delay_ms) early = packets[i]; + } + size_t late_start = packets.size() - settings.end_packets; + TrendlineEstimator::PacketTiming late = packets[late_start]; + for (size_t i = late_start + 1; i < packets.size(); ++i) { + if (packets[i].raw_delay_ms < late.raw_delay_ms) late = packets[i]; + } + if (late.arrival_time_ms - early.arrival_time_ms < 1) { + return OptionalDouble(); + } + return OptionalDouble((late.raw_delay_ms - early.raw_delay_ms) / + (late.arrival_time_ms - early.arrival_time_ms) + + settings.cap_uncertainty); +} + +constexpr double kMaxAdaptOffsetMs = 15.0; +constexpr double kOverUsingTimeThreshold = 10; +constexpr int kMinNumDeltas = 60; +constexpr int kDeltaCounterMax = 1000; + +//} // namespace + +constexpr char TrendlineEstimatorSettings::kKey[]; + +TrendlineEstimatorSettings::TrendlineEstimatorSettings( + /*const WebRtcKeyValueConfig* key_value_config*/) { + /*if (absl::StartsWith( + key_value_config->Lookup(kBweWindowSizeInPacketsExperiment), + "Enabled")) { + window_size = ReadTrendlineFilterWindowSize(key_value_config); + } + Parser()->Parse(key_value_config->Lookup(TrendlineEstimatorSettings::kKey));*/ + window_size = kDefaultTrendlineWindowSize; + enable_cap = false; + beginning_packets = end_packets = 0; + cap_uncertainty = 0.0; + + /*if (window_size < 10 || 200 < window_size) { + RTC_LOG(LS_WARNING) << "Window size must be between 10 and 200 packets"; + window_size = kDefaultTrendlineWindowSize; + } + if (enable_cap) { + if (beginning_packets < 1 || end_packets < 1 || + beginning_packets > window_size || end_packets > window_size) { + RTC_LOG(LS_WARNING) << "Size of beginning and end must be between 1 and " + << window_size; + enable_cap = false; + beginning_packets = end_packets = 0; + cap_uncertainty = 0.0; + } + if (beginning_packets + end_packets > window_size) { + RTC_LOG(LS_WARNING) + << "Size of beginning plus end can't exceed the window size"; + enable_cap = false; + beginning_packets = end_packets = 0; + cap_uncertainty = 0.0; + } + if (cap_uncertainty < 0.0 || 0.025 < cap_uncertainty) { + RTC_LOG(LS_WARNING) << "Cap uncertainty must be between 0 and 0.025"; + cap_uncertainty = 0.0; + } + }*/ +} + +/*std::unique_ptr TrendlineEstimatorSettings::Parser() { + return StructParametersParser::Create("sort", &enable_sort, // + "cap", &enable_cap, // + "beginning_packets", + &beginning_packets, // + "end_packets", &end_packets, // + "cap_uncertainty", &cap_uncertainty, // + "window_size", &window_size); +}*/ + +TrendlineEstimator::TrendlineEstimator( + /*const WebRtcKeyValueConfig* key_value_config, + NetworkStatePredictor* network_state_predictor*/) + : settings_(), + smoothing_coef_(kDefaultTrendlineSmoothingCoeff), + threshold_gain_(kDefaultTrendlineThresholdGain), + num_of_deltas_(0), + first_arrival_time_ms_(-1), + accumulated_delay_(0), + smoothed_delay_(0), + delay_hist_(), + k_up_(0.0087), + k_down_(0.039), + overusing_time_threshold_(kOverUsingTimeThreshold), + threshold_(12.5), + prev_modified_trend_(NAN), + last_update_ms_(-1), + prev_trend_(0.0), + time_over_using_(-1), + overuse_counter_(0), + hypothesis_(BandwidthUsage::kBwNormal){ + // hypothesis_predicted_(BandwidthUsage::kBwNormal){//}, + // network_state_predictor_(network_state_predictor) { + /* RTC_LOG(LS_INFO) + << "Using Trendline filter for delay change estimation with settings " + << settings_.Parser()->Encode() << " and " + // << (network_state_predictor_ ? "injected" : "no") + << " network state predictor";*/ +} + +TrendlineEstimator::~TrendlineEstimator() {} + +void TrendlineEstimator::UpdateTrendline(double recv_delta_ms, + double send_delta_ms, + int64_t send_time_ms, + int64_t arrival_time_ms, + size_t packet_size) { + const double delta_ms = recv_delta_ms - send_delta_ms; + ++num_of_deltas_; + num_of_deltas_ = std::min(num_of_deltas_, kDeltaCounterMax); + if (first_arrival_time_ms_ == -1) first_arrival_time_ms_ = arrival_time_ms; + + // Exponential backoff filter. + accumulated_delay_ += delta_ms; + // BWE_TEST_LOGGING_PLOT(1, "accumulated_delay_ms", arrival_time_ms, + // accumulated_delay_); + smoothed_delay_ = smoothing_coef_ * smoothed_delay_ + + (1 - smoothing_coef_) * accumulated_delay_; + // BWE_TEST_LOGGING_PLOT(1, "smoothed_delay_ms", arrival_time_ms, + // smoothed_delay_); + + // Maintain packet window + delay_hist_.emplace_back( + static_cast(arrival_time_ms - first_arrival_time_ms_), + smoothed_delay_, accumulated_delay_); + if (settings_.enable_sort) { + for (size_t i = delay_hist_.size() - 1; + i > 0 && + delay_hist_[i].arrival_time_ms < delay_hist_[i - 1].arrival_time_ms; + --i) { + std::swap(delay_hist_[i], delay_hist_[i - 1]); + } + } + if (delay_hist_.size() > settings_.window_size) delay_hist_.pop_front(); + + // Simple linear regression. + double trend = prev_trend_; + if (delay_hist_.size() == settings_.window_size) { + // Update trend_ if it is possible to fit a line to the data. The delay + // trend can be seen as an estimate of (send_rate - capacity)/capacity. + // 0 < trend < 1 -> the delay increases, queues are filling up + // trend == 0 -> the delay does not change + // trend < 0 -> the delay decreases, queues are being emptied + OptionalDouble trendO = LinearFitSlope(delay_hist_); + if (trendO.has_value()) trend = trendO.value(); + if (settings_.enable_cap) { + OptionalDouble cap = ComputeSlopeCap(delay_hist_, settings_); + // We only use the cap to filter out overuse detections, not + // to detect additional underuses. + if (trend >= 0 && cap.has_value() && trend > cap.value()) { + trend = cap.value(); + } + } + } + // BWE_TEST_LOGGING_PLOT(1, "trendline_slope", arrival_time_ms, trend); + + Detect(trend, send_delta_ms, arrival_time_ms); +} + +void TrendlineEstimator::Update(double recv_delta_ms, double send_delta_ms, + int64_t send_time_ms, int64_t arrival_time_ms, + size_t packet_size, bool calculated_deltas) { + if (calculated_deltas) { + UpdateTrendline(recv_delta_ms, send_delta_ms, send_time_ms, arrival_time_ms, + packet_size); + } + /*if (network_state_predictor_) { + hypothesis_predicted_ = network_state_predictor_->Update( + send_time_ms, arrival_time_ms, hypothesis_); + }*/ +} + +BandwidthUsage TrendlineEstimator::State() const { + return /*network_state_predictor_ ? hypothesis_predicted_ :*/ hypothesis_; +} + +void TrendlineEstimator::Detect(double trend, double ts_delta, int64_t now_ms) { + /*if (num_of_deltas_ < 2) { + hypothesis_ = BandwidthUsage::kBwNormal; + return; + }*/ + + const double modified_trend = + std::min(num_of_deltas_, kMinNumDeltas) * trend * threshold_gain_; + prev_modified_trend_ = modified_trend; + // BWE_TEST_LOGGING_PLOT(1, "T", now_ms, modified_trend); + // BWE_TEST_LOGGING_PLOT(1, "threshold", now_ms, threshold_); + if (modified_trend > threshold_) { + if (time_over_using_ == -1) { + // Initialize the timer. Assume that we've been + // over-using half of the time since the previous + // sample. + time_over_using_ = ts_delta / 2; + } else { + // Increment timer + time_over_using_ += ts_delta; + } + overuse_counter_++; + if (time_over_using_ > overusing_time_threshold_ && overuse_counter_ > 1) { + if (trend >= prev_trend_) { + time_over_using_ = 0; + overuse_counter_ = 0; + hypothesis_ = BandwidthUsage::kBwOverusing; + } + } + } else if (modified_trend < -threshold_) { + time_over_using_ = -1; + overuse_counter_ = 0; + hypothesis_ = BandwidthUsage::kBwUnderusing; + } else { + time_over_using_ = -1; + overuse_counter_ = 0; + hypothesis_ = BandwidthUsage::kBwNormal; + } + prev_trend_ = trend; + UpdateThreshold(modified_trend, now_ms); +} + +void TrendlineEstimator::UpdateThreshold(double modified_trend, + int64_t now_ms) { + if (last_update_ms_ == -1) last_update_ms_ = now_ms; + + if (fabs(modified_trend) > threshold_ + kMaxAdaptOffsetMs) { + // Avoid adapting the threshold to big latency spikes, caused e.g., + // by a sudden capacity drop. + last_update_ms_ = now_ms; + return; + } + + const double k = fabs(modified_trend) < threshold_ ? k_down_ : k_up_; + const int64_t kMaxTimeDeltaMs = 100; + int64_t time_delta_ms = std::min(now_ms - last_update_ms_, kMaxTimeDeltaMs); + threshold_ += k * (fabs(modified_trend) - threshold_) * time_delta_ms; + if (threshold_ < 6.f) threshold_ = 6.f; + if (threshold_ > 600.f) threshold_ = 600.f; + // threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f); + last_update_ms_ = now_ms; +} + +} // namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/trendline_estimator.h b/libtransport/src/protocols/rtc/trendline_estimator.h new file mode 100644 index 000000000..372acbc67 --- /dev/null +++ b/libtransport/src/protocols/rtc/trendline_estimator.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// FROM +// https://source.chromium.org/chromium/chromium/src/+/master:third_party/webrtc/modules/congestion_controller/goog_cc/trendline_estimator.h + +#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_ +#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_ + +#include +#include + +#include +#include +#include +#include + +namespace transport { + +namespace protocol { + +namespace rtc { + +class OptionalDouble { + public: + OptionalDouble() : val(0), has_val(false){}; + OptionalDouble(double val) : val(val), has_val(true){}; + + double value() { return val; } + bool has_value() { return has_val; } + + private: + double val; + bool has_val; +}; + +enum class BandwidthUsage { + kBwNormal = 0, + kBwUnderusing = 1, + kBwOverusing = 2, + kLast +}; + +struct TrendlineEstimatorSettings { + static constexpr char kKey[] = "WebRTC-Bwe-TrendlineEstimatorSettings"; + static constexpr unsigned kDefaultTrendlineWindowSize = 20; + + // TrendlineEstimatorSettings() = delete; + TrendlineEstimatorSettings( + /*const WebRtcKeyValueConfig* key_value_config*/); + + // Sort the packets in the window. Should be redundant, + // but then almost no cost. + bool enable_sort = false; + + // Cap the trendline slope based on the minimum delay seen + // in the beginning_packets and end_packets respectively. + bool enable_cap = false; + unsigned beginning_packets = 7; + unsigned end_packets = 7; + double cap_uncertainty = 0.0; + + // Size (in packets) of the window. + unsigned window_size = kDefaultTrendlineWindowSize; + + // std::unique_ptr Parser(); +}; + +class TrendlineEstimator /*: public DelayIncreaseDetectorInterface */ { + public: + TrendlineEstimator(/*const WebRtcKeyValueConfig* key_value_config, + NetworkStatePredictor* network_state_predictor*/); + + ~TrendlineEstimator(); + + // Update the estimator with a new sample. The deltas should represent deltas + // between timestamp groups as defined by the InterArrival class. + void Update(double recv_delta_ms, double send_delta_ms, int64_t send_time_ms, + int64_t arrival_time_ms, size_t packet_size, + bool calculated_deltas); + + void UpdateTrendline(double recv_delta_ms, double send_delta_ms, + int64_t send_time_ms, int64_t arrival_time_ms, + size_t packet_size); + + BandwidthUsage State() const; + + struct PacketTiming { + PacketTiming(double arrival_time_ms, double smoothed_delay_ms, + double raw_delay_ms) + : arrival_time_ms(arrival_time_ms), + smoothed_delay_ms(smoothed_delay_ms), + raw_delay_ms(raw_delay_ms) {} + double arrival_time_ms; + double smoothed_delay_ms; + double raw_delay_ms; + }; + + private: + // friend class GoogCcStatePrinter; + void Detect(double trend, double ts_delta, int64_t now_ms); + + void UpdateThreshold(double modified_offset, int64_t now_ms); + + // Parameters. + TrendlineEstimatorSettings settings_; + const double smoothing_coef_; + const double threshold_gain_; + // Used by the existing threshold. + int num_of_deltas_; + // Keep the arrival times small by using the change from the first packet. + int64_t first_arrival_time_ms_; + // Exponential backoff filtering. + double accumulated_delay_; + double smoothed_delay_; + // Linear least squares regression. + std::deque delay_hist_; + + const double k_up_; + const double k_down_; + double overusing_time_threshold_; + double threshold_; + double prev_modified_trend_; + int64_t last_update_ms_; + double prev_trend_; + double time_over_using_; + int overuse_counter_; + BandwidthUsage hypothesis_; + // BandwidthUsage hypothesis_predicted_; + // NetworkStatePredictor* network_state_predictor_; + + // RTC_DISALLOW_COPY_AND_ASSIGN(TrendlineEstimator); +}; + +} // namespace rtc + +} // end namespace protocol + +} // end namespace transport +#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_ diff --git a/libtransport/src/protocols/transport_protocol.cc b/libtransport/src/protocols/transport_protocol.cc new file mode 100644 index 000000000..611c39212 --- /dev/null +++ b/libtransport/src/protocols/transport_protocol.cc @@ -0,0 +1,132 @@ +/* + * 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 +#include +#include + +namespace transport { + +namespace protocol { + +using namespace interface; + +TransportProtocol::TransportProtocol(implementation::ConsumerSocket *icn_socket, + Reassembly *reassembly_protocol) + : socket_(icn_socket), + reassembly_protocol_(reassembly_protocol), + index_manager_( + std::make_unique(socket_, this, reassembly_protocol)), + is_running_(false), + is_first_(false), + on_interest_retransmission_(VOID_HANDLER), + on_interest_output_(VOID_HANDLER), + on_interest_timeout_(VOID_HANDLER), + on_interest_satisfied_(VOID_HANDLER), + on_content_object_input_(VOID_HANDLER), + stats_summary_(VOID_HANDLER), + on_payload_(VOID_HANDLER) { + socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal_); + socket_->getSocketOption(OtherOptions::STATISTICS, &stats_); +} + +int TransportProtocol::start() { + // If the protocol is already running, return otherwise set as running + if (is_running_) return -1; + + // Get all callbacks references + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_RETRANSMISSION, + &on_interest_retransmission_); + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_OUTPUT, + &on_interest_output_); + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_EXPIRED, + &on_interest_timeout_); + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_SATISFIED, + &on_interest_satisfied_); + socket_->getSocketOption(ConsumerCallbacksOptions::CONTENT_OBJECT_INPUT, + &on_content_object_input_); + socket_->getSocketOption(ConsumerCallbacksOptions::STATS_SUMMARY, + &stats_summary_); + socket_->getSocketOption(ConsumerCallbacksOptions::READ_CALLBACK, + &on_payload_); + + socket_->getSocketOption(GeneralTransportOptions::ASYNC_MODE, is_async_); + + // Set it is the first time we schedule an interest + is_first_ = true; + + // Reset the protocol state machine + reset(); + // Schedule next interests + scheduleNextInterests(); + + is_first_ = false; + + // Set the protocol as running + is_running_ = true; + + if (!is_async_) { + // Start Event loop + portal_->runEventsLoop(); + + // Not running anymore + is_running_ = false; + } + + return 0; +} + +void TransportProtocol::stop() { + is_running_ = false; + + if (!is_async_) { + portal_->stopEventsLoop(); + } else { + portal_->clear(); + } +} + +void TransportProtocol::resume() { + if (is_running_) return; + + is_running_ = true; + + scheduleNextInterests(); + + portal_->runEventsLoop(); + + is_running_ = false; +} + +void TransportProtocol::onContentReassembled(std::error_code ec) { + stop(); + + if (!on_payload_) { + throw errors::RuntimeException( + "The read callback must be installed in the transport before " + "starting " + "the content retrieval."); + } + + if (!ec) { + on_payload_->readSuccess(stats_->getBytesRecv()); + } else { + on_payload_->readError(ec); + } +} + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/transport_protocol.h b/libtransport/src/protocols/transport_protocol.h new file mode 100644 index 000000000..124c57122 --- /dev/null +++ b/libtransport/src/protocols/transport_protocol.h @@ -0,0 +1,104 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace transport { + +namespace protocol { + +using namespace core; + +class IndexVerificationManager; + +using ReadCallback = interface::ConsumerSocket::ReadCallback; + +class TransportProtocolCallback { + virtual void onContentObject(const core::Interest &interest, + const core::ContentObject &content_object) = 0; + virtual void onTimeout(const core::Interest &interest) = 0; +}; + +class TransportProtocol : public core::Portal::ConsumerCallback, + public ContentObjectProcessingEventCallback { + static constexpr std::size_t interest_pool_size = 4096; + + friend class ManifestIndexManager; + + public: + TransportProtocol(implementation::ConsumerSocket *icn_socket, + Reassembly *reassembly_protocol); + + virtual ~TransportProtocol() = default; + + TRANSPORT_ALWAYS_INLINE bool isRunning() { return is_running_; } + + virtual int start(); + + virtual void stop(); + + virtual void resume(); + + virtual void scheduleNextInterests() = 0; + + // Events generated by the indexing + virtual void onContentReassembled(std::error_code ec); + virtual void onPacketDropped(Interest &interest, + ContentObject &content_object) override = 0; + virtual void onReassemblyFailed(std::uint32_t missing_segment) override = 0; + + protected: + // Consumer Callback + virtual void reset() = 0; + virtual void onContentObject(Interest &i, ContentObject &c) override = 0; + virtual void onTimeout(Interest::Ptr &&i) override = 0; + virtual void onError(std::error_code ec) override {} + + protected: + implementation::ConsumerSocket *socket_; + std::unique_ptr reassembly_protocol_; + std::unique_ptr index_manager_; + std::shared_ptr portal_; + std::atomic is_running_; + // True if it si the first time we schedule an interest + std::atomic is_first_; + interface::TransportStatistics *stats_; + + // Callbacks + interface::ConsumerInterestCallback *on_interest_retransmission_; + interface::ConsumerInterestCallback *on_interest_output_; + interface::ConsumerInterestCallback *on_interest_timeout_; + interface::ConsumerInterestCallback *on_interest_satisfied_; + interface::ConsumerContentObjectCallback *on_content_object_input_; + interface::ConsumerContentObjectCallback *on_content_object_; + interface::ConsumerTimerCallback *stats_summary_; + ReadCallback *on_payload_; + + bool is_async_; +}; + +} // end namespace protocol +} // end namespace transport diff --git a/libtransport/src/test/CMakeLists.txt b/libtransport/src/test/CMakeLists.txt index 19e59c7e1..dd3d1d923 100644 --- a/libtransport/src/test/CMakeLists.txt +++ b/libtransport/src/test/CMakeLists.txt @@ -14,14 +14,15 @@ include(BuildMacros) list(APPEND TESTS + test_auth + test_consumer_producer_rtc test_core_manifest - test_transport_producer + test_event_thread + test_fec_reedsolomon + test_interest + test_packet ) -if (${LIBTRANSPORT_SHARED} MATCHES ".*-memif.*") - set(LINK_FLAGS "-Wl,-unresolved-symbols=ignore-in-shared-libs") -endif() - foreach(test ${TESTS}) build_executable(${test} NO_INSTALL @@ -35,4 +36,4 @@ foreach(test ${TESTS}) ) add_test_internal(${test}) -endforeach() \ No newline at end of file +endforeach() diff --git a/libtransport/src/test/fec_reed_solomon.cc b/libtransport/src/test/fec_reed_solomon.cc new file mode 100644 index 000000000..36543c531 --- /dev/null +++ b/libtransport/src/test/fec_reed_solomon.cc @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace transport { +namespace interface { + +namespace { + +class ConsumerProducerTest : public ::testing::Test, + public ConsumerSocket::ReadCallback { + static const constexpr char prefix[] = "b001::1/128"; + static const constexpr char name[] = "b001::1"; + static const constexpr double prod_rate = 1.0e6; + static const constexpr size_t payload_size = 1200; + static constexpr std::size_t receive_buffer_size = 1500; + static const constexpr double prod_interval_microseconds = + double(payload_size) * 8 * 1e6 / prod_rate; + + public: + ConsumerProducerTest() + : io_service_(), + rtc_timer_(io_service_), + consumer_(TransportProtocolAlgorithms::RTC, io_service_), + producer_(ProductionProtocolAlgorithms::RTC_PROD, io_service_), + producer_prefix_(prefix), + consumer_name_(name), + packets_sent_(0), + packets_received_(0) { + global_config::IoModuleConfiguration config; + config.name = "loopback_module"; + config.set(); + } + + virtual ~ConsumerProducerTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() override { + // Code here will be called immediately after the constructor (right + // before each test). + + auto ret = consumer_.setSocketOption( + ConsumerCallbacksOptions::READ_CALLBACK, this); + ASSERT_EQ(ret, SOCKET_OPTION_SET); + + consumer_.connect(); + producer_.registerPrefix(producer_prefix_); + producer_.connect(); + } + + virtual void TearDown() override { + // Code here will be called immediately after each test (right + // before the destructor). + } + + void setTimer() { + using namespace std::chrono; + rtc_timer_.expires_from_now( + microseconds(unsigned(prod_interval_microseconds))); + rtc_timer_.async_wait(std::bind(&ConsumerProducerTest::produceRTCPacket, + this, std::placeholders::_1)); + } + + void produceRTCPacket(const std::error_code &ec) { + if (ec) { + FAIL() << "Failed to schedule packet production"; + io_service_.stop(); + } + + producer_.produceDatagram(consumer_name_, payload_, payload_size); + packets_sent_++; + setTimer(); + } + + // Consumer callback + bool isBufferMovable() noexcept override { return false; } + + void getReadBuffer(uint8_t **application_buffer, + size_t *max_length) override { + *application_buffer = receive_buffer_; + *max_length = receive_buffer_size; + } + + void readDataAvailable(std::size_t length) noexcept override {} + + size_t maxBufferSize() const override { return receive_buffer_size; } + + void readError(const std::error_code ec) noexcept override { + FAIL() << "Error while reading from RTC socket"; + io_service_.stop(); + } + + void readSuccess(std::size_t total_size) noexcept override { + packets_received_++; + } + + asio::io_service io_service_; + asio::steady_timer rtc_timer_; + ConsumerSocket consumer_; + ProducerSocket producer_; + core::Prefix producer_prefix_; + core::Name consumer_name_; + uint8_t payload_[payload_size]; + uint8_t receive_buffer_[payload_size]; + + uint64_t packets_sent_; + uint64_t packets_received_; +}; + +const char ConsumerProducerTest::prefix[]; +const char ConsumerProducerTest::name[]; + +} // namespace + +TEST_F(ConsumerProducerTest, EndToEnd) { + produceRTCPacket(std::error_code()); + consumer_.consume(consumer_name_); + + io_service_.run(); +} + +} // namespace interface + +} // namespace transport + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/libtransport/src/test/fec_rely.cc b/libtransport/src/test/fec_rely.cc new file mode 100644 index 000000000..e7745bae5 --- /dev/null +++ b/libtransport/src/test/fec_rely.cc @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace transport { +namespace interface { + +namespace { + +class ConsumerProducerTest : public ::testing::Test, + public ConsumerSocket::ReadCallback { + static const constexpr char prefix[] = "b001::1/128"; + static const constexpr char name[] = "b001::1"; + static const constexpr double prod_rate = 1.0e6; + static const constexpr size_t payload_size = 1200; + static constexpr std::size_t receive_buffer_size = 1500; + static const constexpr double prod_interval_microseconds = + double(payload_size) * 8 * 1e6 / prod_rate; + + public: + ConsumerProducerTest() + : io_service_(), + rtc_timer_(io_service_), + consumer_(TransportProtocolAlgorithms::RTC, io_service_), + producer_(ProductionProtocolAlgorithms::RTC_PROD, io_service_), + producer_prefix_(prefix), + consumer_name_(name), + packets_sent_(0), + packets_received_(0) { + global_config::IoModuleConfiguration config; + config.name = "loopback_module"; + config.set(); + } + + virtual ~ConsumerProducerTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() override { + // Code here will be called immediately after the constructor (right + // before each test). + + auto ret = consumer_.setSocketOption( + ConsumerCallbacksOptions::READ_CALLBACK, this); + ASSERT_EQ(ret, SOCKET_OPTION_SET); + + consumer_.connect(); + producer_.registerPrefix(producer_prefix_); + producer_.connect(); + } + + virtual void TearDown() override { + // Code here will be called immediately after each test (right + // before the destructor). + } + + void setTimer() { + using namespace std::chrono; + rtc_timer_.expires_from_now( + microseconds(unsigned(prod_interval_microseconds))); + rtc_timer_.async_wait(std::bind(&ConsumerProducerTest::produceRTCPacket, + this, std::placeholders::_1)); + } + + void produceRTCPacket(const std::error_code &ec) { + if (ec) { + FAIL() << "Failed to schedule packet production"; + io_service_.stop(); + } + + producer_.produceDatagram(consumer_name_, payload_, payload_size); + packets_sent_++; + setTimer(); + } + + // Consumer callback + bool isBufferMovable() noexcept override { return false; } + + void getReadBuffer(uint8_t **application_buffer, + size_t *max_length) override { + *application_buffer = receive_buffer_; + *max_length = receive_buffer_size; + } + + void readDataAvailable(std::size_t length) noexcept override {} + + size_t maxBufferSize() const override { return receive_buffer_size; } + + void readError(const std::error_code ec) noexcept override { + FAIL() << "Error while reading from RTC socket"; + io_service_.stop(); + } + + void readSuccess(std::size_t total_size) noexcept override { + packets_received_++; + } + + asio::io_service io_service_; + asio::steady_timer rtc_timer_; + ConsumerSocket consumer_; + ProducerSocket producer_; + core::Prefix producer_prefix_; + core::Name consumer_name_; + uint8_t payload_[payload_size]; + uint8_t receive_buffer_[payload_size]; + + uint64_t packets_sent_; + uint64_t packets_received_; +}; + +const char ConsumerProducerTest::prefix[]; +const char ConsumerProducerTest::name[]; + +} // namespace + +TEST_F(ConsumerProducerTest, EndToEnd) { + produceRTCPacket(std::error_code()); + consumer_.consume(consumer_name_); + + io_service_.run(); +} + +} // namespace interface + +} // namespace transport + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/libtransport/src/test/packet_samples.h b/libtransport/src/test/packet_samples.h new file mode 100644 index 000000000..e98d06a18 --- /dev/null +++ b/libtransport/src/test/packet_samples.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TCP_PROTO 0x06 +#define ICMP_PROTO 0x01 +#define ICMP6_PROTO 0x3a + +#define IPV6_HEADER(next_header, payload_length) \ + 0x60, 0x00, 0x00, 0x00, 0x00, payload_length, next_header, 0x40, 0xb0, 0x06, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xcd, 0xab, \ + 0xcd, 0xef, 0xb0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0xca + +#define IPV4_HEADER(next_header, payload_length) \ + 0x45, 0x02, 0x00, payload_length + 20, 0x47, 0xc4, 0x40, 0x00, 0x25, \ + next_header, 0x6e, 0x76, 0x03, 0x7b, 0xd9, 0xd0, 0xc0, 0xa8, 0x01, 0x5c + +#define TCP_HEADER(flags) \ + 0x12, 0x34, 0x43, 0x21, 0x00, 0x00, 0x00, 0x01, 0xb2, 0x8c, 0x03, 0x1f, \ + 0x80, flags, 0x00, 0x0a, 0xb9, 0xbb, 0x00, 0x00 + +#define PAYLOAD \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x20, 0x00, 0x00 + +#define PAYLOAD_SIZE 12 + +#define ICMP_ECHO_REQUEST \ + 0x08, 0x00, 0x87, 0xdb, 0x38, 0xa7, 0x00, 0x05, 0x60, 0x2b, 0xc2, 0xcb, \ + 0x00, 0x02, 0x29, 0x7c, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, \ + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, \ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, \ + 0x34, 0x35, 0x36, 0x37 + +#define ICMP6_ECHO_REQUEST \ + 0x80, 0x00, 0x86, 0x3c, 0x11, 0x0d, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, \ + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, \ + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, \ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33 + +#define AH_HEADER \ + 0x00, (128 >> 2), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 diff --git a/libtransport/src/test/test_auth.cc b/libtransport/src/test/test_auth.cc new file mode 100644 index 000000000..976981cce --- /dev/null +++ b/libtransport/src/test/test_auth.cc @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include +#include +#include +#include + +namespace transport { +namespace auth { + +namespace { +class AuthTest : public ::testing::Test { + protected: + const std::string PASSPHRASE = "hunter2"; + + AuthTest() = default; + ~AuthTest() {} + void SetUp() override {} + void TearDown() override {} +}; +} // namespace + +TEST_F(AuthTest, VoidVerifier) { + // Create a content object + core::ContentObject packet(HF_INET6_TCP_AH); + + // Fill it with bogus data + uint8_t buffer[256] = {0}; + packet.appendPayload(buffer, 256); + + // Verify that VoidVerifier validates the packet + std::shared_ptr verifier = std::make_shared(); + ASSERT_EQ(verifier->verifyPacket(&packet), true); + ASSERT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT); +} + +TEST_F(AuthTest, RSAVerifier) { + // Create the RSA signer from an Identity object + Identity identity("test_rsa.p12", PASSPHRASE, CryptoSuite::RSA_SHA256, 1024u, + 30, "RSAVerifier"); + std::shared_ptr signer = identity.getSigner(); + + // Create a content object + core::ContentObject packet(HF_INET6_TCP_AH, signer->getSignatureSize()); + + // Fill it with bogus data + uint8_t buffer[256] = {0}; + packet.appendPayload(buffer, 256); + + // Sign the packet + signer->signPacket(&packet); + + // Create the RSA verifier + PARCKey *key = parcSigner_CreatePublicKey(signer->getParcSigner()); + std::shared_ptr verifier = + std::make_shared(key); + + ASSERT_EQ(packet.getFormat(), HF_INET6_TCP_AH); + ASSERT_EQ(signer->getCryptoHashType(), CryptoHashType::SHA_256); + ASSERT_EQ(signer->getCryptoSuite(), CryptoSuite::RSA_SHA256); + ASSERT_EQ(signer->getSignatureSize(), 128u); + ASSERT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT); + + // Release PARC objects + parcKey_Release(&key); +} + +TEST_F(AuthTest, HMACVerifier) { + // Create the HMAC signer from a passphrase + std::shared_ptr signer = + std::make_shared(CryptoSuite::HMAC_SHA256, PASSPHRASE); + + // Create a content object + core::ContentObject packet(HF_INET6_TCP_AH, signer->getSignatureSize()); + + // Fill it with bogus data + uint8_t buffer[256] = {0}; + packet.appendPayload(buffer, 256); + + // Sign the packet + signer->signPacket(&packet); + + // Create the HMAC verifier + std::shared_ptr verifier = + std::make_shared(PASSPHRASE); + + ASSERT_EQ(packet.getFormat(), HF_INET6_TCP_AH); + ASSERT_EQ(signer->getCryptoHashType(), CryptoHashType::SHA_256); + ASSERT_EQ(signer->getCryptoSuite(), CryptoSuite::HMAC_SHA256); + ASSERT_EQ(signer->getSignatureSize(), 32u); + ASSERT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT); +} + +} // namespace auth +} // namespace transport diff --git a/libtransport/src/test/test_consumer_producer_rtc.cc b/libtransport/src/test/test_consumer_producer_rtc.cc new file mode 100644 index 000000000..87385971a --- /dev/null +++ b/libtransport/src/test/test_consumer_producer_rtc.cc @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include + +namespace transport { +namespace interface { + +namespace { + +class ConsumerProducerTest : public ::testing::Test, + public ConsumerSocket::ReadCallback { + static const constexpr char prefix[] = "b001::1/128"; + static const constexpr char name[] = "b001::1"; + static const constexpr double prod_rate = 1.0e6; + static const constexpr size_t payload_size = 1200; + static constexpr std::size_t receive_buffer_size = 1500; + static const constexpr double prod_interval_microseconds = + double(payload_size) * 8 * 1e6 / prod_rate; + + public: + ConsumerProducerTest() + : io_service_(), + rtc_timer_(io_service_), + stop_timer_(io_service_), + consumer_(TransportProtocolAlgorithms::RTC, io_service_), + producer_(ProductionProtocolAlgorithms::RTC_PROD, io_service_), + producer_prefix_(prefix), + consumer_name_(name), + packets_sent_(0), + packets_received_(0) { + global_config::IoModuleConfiguration config; + config.name = "loopback_module"; + config.set(); + } + + virtual ~ConsumerProducerTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() override { + // Code here will be called immediately after the constructor (right + // before each test). + + auto ret = consumer_.setSocketOption( + ConsumerCallbacksOptions::READ_CALLBACK, this); + ASSERT_EQ(ret, SOCKET_OPTION_SET); + + consumer_.connect(); + producer_.registerPrefix(producer_prefix_); + producer_.connect(); + } + + virtual void TearDown() override { + // Code here will be called immediately after each test (right + // before the destructor). + } + + void setTimer() { + using namespace std::chrono; + rtc_timer_.expires_from_now( + microseconds(unsigned(prod_interval_microseconds))); + rtc_timer_.async_wait(std::bind(&ConsumerProducerTest::produceRTCPacket, + this, std::placeholders::_1)); + } + + void setStopTimer() { + using namespace std::chrono; + stop_timer_.expires_from_now(seconds(unsigned(10))); + stop_timer_.async_wait( + std::bind(&ConsumerProducerTest::stop, this, std::placeholders::_1)); + } + + void produceRTCPacket(const std::error_code &ec) { + if (ec) { + io_service_.stop(); + } + + producer_.produceDatagram(consumer_name_, payload_, payload_size); + packets_sent_++; + setTimer(); + } + + void stop(const std::error_code &ec) { + rtc_timer_.cancel(); + producer_.stop(); + consumer_.stop(); + } + + // Consumer callback + bool isBufferMovable() noexcept override { return false; } + + void getReadBuffer(uint8_t **application_buffer, + size_t *max_length) override { + *application_buffer = receive_buffer_; + *max_length = receive_buffer_size; + } + + void readDataAvailable(std::size_t length) noexcept override {} + + size_t maxBufferSize() const override { return receive_buffer_size; } + + void readError(const std::error_code ec) noexcept override { + FAIL() << "Error while reading from RTC socket"; + io_service_.stop(); + } + + void readSuccess(std::size_t total_size) noexcept override { + packets_received_++; + std::cout << "Received something" << std::endl; + } + + asio::io_service io_service_; + asio::steady_timer rtc_timer_; + asio::steady_timer stop_timer_; + ConsumerSocket consumer_; + ProducerSocket producer_; + core::Prefix producer_prefix_; + core::Name consumer_name_; + uint8_t payload_[payload_size]; + uint8_t receive_buffer_[payload_size]; + + uint64_t packets_sent_; + uint64_t packets_received_; +}; + +const char ConsumerProducerTest::prefix[]; +const char ConsumerProducerTest::name[]; + +} // namespace + +TEST_F(ConsumerProducerTest, EndToEnd) { + produceRTCPacket(std::error_code()); + consumer_.consume(consumer_name_); + setStopTimer(); + + io_service_.run(); + + std::cout << "Packet received: " << packets_received_ << std::endl; + std::cout << "Packet sent: " << packets_sent_ << std::endl; +} + +} // namespace interface + +} // namespace transport + +int main(int argc, char **argv) { +#if 0 + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +#else + return 0; +#endif +} \ No newline at end of file diff --git a/libtransport/src/test/test_core_manifest.cc b/libtransport/src/test/test_core_manifest.cc index faf17dcf0..f98147d43 100644 --- a/libtransport/src/test/test_core_manifest.cc +++ b/libtransport/src/test/test_core_manifest.cc @@ -16,7 +16,8 @@ #include #include #include -#include +#include +#include #include #include @@ -72,6 +73,27 @@ class ManifestTest : public ::testing::Test { } // namespace +TEST_F(ManifestTest, MoveConstructor) { + // Create content object with manifest in payload + ContentObject co(HF_INET6_TCP_AH, 128); + co.appendPayload(&manifest_payload[0], manifest_payload.size()); + uint8_t buffer[256]; + co.appendPayload(buffer, 256); + + // Copy packet payload + uint8_t packet[1500]; + auto length = co.getPayload()->length(); + std::memcpy(packet, co.getPayload()->data(), length); + + // Create manifest + ContentObjectManifest m(std::move(co)); + + // Check manifest payload is exactly the same of content object + ASSERT_EQ(length, m.getPayload()->length()); + auto ret = std::memcmp(packet, m.getPayload()->data(), length); + ASSERT_EQ(ret, 0); +} + TEST_F(ManifestTest, SetLastManifest) { manifest1_.clear(); @@ -102,9 +124,9 @@ TEST_F(ManifestTest, SetManifestType) { TEST_F(ManifestTest, SetHashAlgorithm) { manifest1_.clear(); - utils::CryptoHashType hash1 = utils::CryptoHashType::SHA_512; - utils::CryptoHashType hash2 = utils::CryptoHashType::CRC32C; - utils::CryptoHashType hash3 = utils::CryptoHashType::SHA_256; + auth::CryptoHashType hash1 = auth::CryptoHashType::SHA_512; + auth::CryptoHashType hash2 = auth::CryptoHashType::CRC32C; + auth::CryptoHashType hash3 = auth::CryptoHashType::SHA_256; manifest1_.setHashAlgorithm(hash1); auto type_returned1 = manifest1_.getHashAlgorithm(); @@ -161,7 +183,7 @@ TEST_F(ManifestTest, SetSuffixList) { std::uniform_int_distribution idis( 0, std::numeric_limits::max()); - auto entries = new std::pair[3]; + auto entries = new std::pair[3]; uint32_t suffixes[3]; std::vector data[3]; @@ -170,8 +192,8 @@ TEST_F(ManifestTest, SetSuffixList) { 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)); + suffixes[i], auth::CryptoHash(data[i].data(), data[i].size(), + auth::CryptoHashType::SHA_256)); manifest1_.addSuffixHash(entries[i].first, entries[i].second); } @@ -186,9 +208,9 @@ TEST_F(ManifestTest, SetSuffixList) { // for (auto & item : manifest1_.getSuffixList()) { // auto hash = manifest1_.getHash(suffixes[i]); - // cond = utils::CryptoHash::compareBinaryDigest(hash, - // entries[i].second.getDigest().data(), - // entries[i].second.getType()); + // cond = auth::CryptoHash::compareBinaryDigest(hash, + // entries[i].second.getDigest().data(), + // entries[i].second.getType()); // ASSERT_TRUE(cond); // i++; // } @@ -205,4 +227,4 @@ TEST_F(ManifestTest, SetSuffixList) { int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/libtransport/src/test/test_event_thread.cc b/libtransport/src/test/test_event_thread.cc new file mode 100644 index 000000000..e66b49f10 --- /dev/null +++ b/libtransport/src/test/test_event_thread.cc @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +namespace utils { + +namespace { + +class EventThreadTest : public ::testing::Test { + protected: + EventThreadTest() : event_thread_() { + // You can do set-up work for each test here. + } + + virtual ~EventThreadTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + utils::EventThread event_thread_; +}; + +double average(const unsigned long samples[], int size) { + double sum = 0; + + for (int i = 0; i < size; i++) { + sum += samples[i]; + } + + return sum / size; +} + +double stdDeviation(const unsigned long samples[], int size) { + double avg = average(samples, size); + double var = 0; + + for (int i = 0; i < size; i++) { + var += (samples[i] - avg) * (samples[i] - avg); + } + + return sqrt(var / size); +} + +} // namespace + +TEST_F(EventThreadTest, SchedulingDelay) { + using namespace std::chrono; + const size_t size = 1000000; + std::vector samples(size); + + for (unsigned int i = 0; i < size; i++) { + auto t0 = steady_clock::now(); + event_thread_.add([t0, &samples, i]() { + auto t1 = steady_clock::now(); + samples[i] = duration_cast(t1 - t0).count(); + }); + } + + event_thread_.stop(); + + auto avg = average(&samples[0], size); + auto sd = stdDeviation(&samples[0], size); + (void)sd; + + // Expect average to be less that 1 ms + EXPECT_LT(avg, 1000000); +} + +} // namespace utils + +int main(int argc, char **argv) { +#if 0 + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +#else + return 0; +#endif +} \ No newline at end of file diff --git a/libtransport/src/test/test_fec_reedsolomon.cc b/libtransport/src/test/test_fec_reedsolomon.cc new file mode 100644 index 000000000..3b10b7307 --- /dev/null +++ b/libtransport/src/test/test_fec_reedsolomon.cc @@ -0,0 +1,291 @@ + +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include +#include + +#include +#include +#include + +namespace transport { +namespace core { + +double ReedSolomonTest(int k, int n, int size) { + fec::encoder encoder(k, n); + fec::decoder decoder(k, n); + + std::vector tx_block(k); + std::vector rx_block(k); + int count = 0; + int run = 0; + + int iterations = 100; + auto &packet_manager = PacketManager<>::getInstance(); + + encoder.setFECCallback([&tx_block](std::vector &repair_packets) { + for (auto &p : repair_packets) { + // Append repair symbols to tx_block + tx_block.emplace_back(std::move(p)); + } + }); + + decoder.setFECCallback([&](std::vector &source_packets) { + for (int i = 0; i < k; i++) { + // Compare decoded source packets with original transmitted packets. + if (*tx_block[i] != *source_packets[i]) { + count++; + } + } + }); + + do { + // Discard eventual packet appended in previous callback call + tx_block.erase(tx_block.begin() + k, tx_block.end()); + + // Initialization. Feed encoder with first k source packets + for (int i = 0; i < k; i++) { + // Get new buffer from pool + auto packet = packet_manager.getMemBuf(); + + // Let's append a bit less than size, so that the FEC class will take care + // of filling the rest with zeros + auto cur_size = size - (rand() % 100); + + // Set payload, saving 2 bytes at the beginning of the buffer for encoding + // the length + packet->append(cur_size); + packet->trimStart(2); + std::generate(packet->writableData(), packet->writableTail(), rand); + std::fill(packet->writableData(), packet->writableTail(), i + 1); + + // Set first byte of payload to i, to reorder at receiver side + packet->writableData()[0] = uint8_t(i); + + // Store packet in tx buffer and clear rx buffer + tx_block[i] = std::move(packet); + } + + // Create the repair packets + for (auto &tx : tx_block) { + encoder.consume(tx, tx->writableBuffer()[0]); + } + + // Simulate transmission on lossy channel + unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); + std::vector losses(n, false); + for (int i = 0; i < n - k; i++) losses[i] = true; + + int rxi = 0; + std::shuffle(losses.begin(), losses.end(), + std::default_random_engine(seed)); + for (int i = 0; i < n && rxi < k; i++) + if (losses[i] == false) { + rx_block[rxi++] = tx_block[i]; + if (i < k) { + // Source packet + decoder.consume(rx_block[rxi - 1], rx_block[rxi - 1]->data()[0]); + } else { + // Repair packet + decoder.consume(rx_block[rxi - 1]); + } + } + + decoder.clear(); + encoder.clear(); + } while (++run < iterations); + + return count; +} + +void ReedSolomonMultiBlockTest(int n_sourceblocks) { + int k = 16; + int n = 24; + int size = 1000; + + fec::encoder encoder(k, n); + fec::decoder decoder(k, n); + + auto &packet_manager = PacketManager<>::getInstance(); + + std::vector> tx_block; + std::vector> rx_block; + int count = 0; + int i = 0; + + // Receiver will receive packet for n_sourceblocks in a random order. + int total_packets = n * n_sourceblocks; + int tx_packets = k * n_sourceblocks; + unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); + + encoder.setFECCallback([&](std::vector &repair_packets) { + for (auto &p : repair_packets) { + // Append repair symbols to tx_block + tx_block.emplace_back(std::move(p), ++i); + } + + EXPECT_EQ(tx_block.size(), size_t(n)); + + // Select k packets to send, including at least one symbol. We start from + // the end for this reason. + for (int j = n - 1; j > n - k - 1; j--) { + rx_block.emplace_back(std::move(tx_block[j])); + } + + // Clear tx block for next source block + tx_block.clear(); + encoder.clear(); + }); + + // The decode callback must be called exactly n_sourceblocks times + decoder.setFECCallback( + [&](std::vector &source_packets) { count++; }); + + // Produce n * n_sourceblocks + // - ( k ) * n_sourceblocks source packets + // - (n - k) * n_sourceblocks symbols) + for (i = 0; i < total_packets; i++) { + // Get new buffer from pool + auto packet = packet_manager.getMemBuf(); + + // Let's append a bit less than size, so that the FEC class will take care + // of filling the rest with zeros + auto cur_size = size - (rand() % 100); + + // Set payload, saving 2 bytes at the beginning of the buffer for encoding + // the length + packet->append(cur_size); + packet->trimStart(2); + std::fill(packet->writableData(), packet->writableTail(), i + 1); + + // Set first byte of payload to i, to reorder at receiver side + packet->writableData()[0] = uint8_t(i); + + // Store packet in tx buffer + tx_block.emplace_back(packet, i); + + // Feed encoder with packet + encoder.consume(packet, i); + } + + // Here rx_block must contains k * n_sourceblocks packets + EXPECT_EQ(size_t(tx_packets), size_t(rx_block.size())); + + // Lets shuffle the rx_block before starting feeding the decoder. + std::shuffle(rx_block.begin(), rx_block.end(), + std::default_random_engine(seed)); + + for (auto &p : rx_block) { + int index = p.second % n; + if (index < k) { + // Source packet + decoder.consume(p.first, p.second); + } else { + // Repair packet + decoder.consume(p.first); + } + } + + // Simple test to check we get all the source packets + EXPECT_EQ(count, n_sourceblocks); +} + +TEST(ReedSolomonTest, RSk1n3) { + int k = 1; + int n = 3; + int size = 1000; + EXPECT_LE(ReedSolomonTest(k, n, size), 0); +} + +TEST(ReedSolomonTest, RSk6n10) { + int k = 6; + int n = 10; + int size = 1000; + EXPECT_LE(ReedSolomonTest(k, n, size), 0); +} + +TEST(ReedSolomonTest, RSk8n32) { + int k = 8; + int n = 32; + int size = 1000; + EXPECT_LE(ReedSolomonTest(k, n, size), 0); +} + +TEST(ReedSolomonTest, RSk16n24) { + int k = 16; + int n = 24; + int size = 1000; + EXPECT_LE(ReedSolomonTest(k, n, size), 0); +} + +TEST(ReedSolomonTest, RSk10n30) { + int k = 10; + int n = 30; + int size = 1000; + EXPECT_LE(ReedSolomonTest(k, n, size), 0); +} + +TEST(ReedSolomonTest, RSk10n40) { + int k = 10; + int n = 40; + int size = 1000; + EXPECT_LE(ReedSolomonTest(k, n, size), 0); +} + +TEST(ReedSolomonTest, RSk10n60) { + int k = 10; + int n = 60; + int size = 1000; + EXPECT_LE(ReedSolomonTest(k, n, size), 0); +} + +TEST(ReedSolomonTest, RSk10n90) { + int k = 10; + int n = 90; + int size = 1000; + EXPECT_LE(ReedSolomonTest(k, n, size), 0); +} + +TEST(ReedSolomonMultiBlockTest, RSMB1) { + int blocks = 1; + ReedSolomonMultiBlockTest(blocks); +} + +TEST(ReedSolomonMultiBlockTest, RSMB10) { + int blocks = 10; + ReedSolomonMultiBlockTest(blocks); +} + +TEST(ReedSolomonMultiBlockTest, RSMB100) { + int blocks = 100; + ReedSolomonMultiBlockTest(blocks); +} + +TEST(ReedSolomonMultiBlockTest, RSMB1000) { + int blocks = 1000; + ReedSolomonMultiBlockTest(blocks); +} + +int main(int argc, char **argv) { + srand(time(0)); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + +} // namespace core +} // namespace transport diff --git a/libtransport/src/test/test_interest.cc b/libtransport/src/test/test_interest.cc new file mode 100644 index 000000000..0a835db24 --- /dev/null +++ b/libtransport/src/test/test_interest.cc @@ -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. + */ + +#include +#include +#include +#include + +#include +#include +#include + +namespace transport { + +namespace core { + +namespace { +// The fixture for testing class Foo. +class InterestTest : public ::testing::Test { + protected: + InterestTest() : name_("b001::123|321"), interest_() { + // You can do set-up work for each test here. + } + + virtual ~InterestTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + Name name_; + + Interest interest_; + + std::vector buffer_ = {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV6_HEADER(TCP_PROTO, 20 + PAYLOAD_SIZE), + // ICMP6 echo request + TCP_HEADER(0x00), + // Payload + PAYLOAD}; +}; + +void testFormatConstructor(Packet::Format format = HF_UNSPEC) { + try { + Interest interest(format, 0); + } catch (...) { + FAIL() << "ERROR: Unexpected exception thrown for " << format; + } +} + +void testFormatConstructorException(Packet::Format format = HF_UNSPEC) { + try { + Interest interest(format, 0); + FAIL() << "We expected an exception here"; + } catch (errors::MalformedPacketException &exc) { + // Ok right exception + } catch (...) { + FAIL() << "Wrong exception thrown"; + } +} + +} // namespace + +TEST_F(InterestTest, ConstructorWithFormat) { + /** + * Without arguments it should be format = HF_UNSPEC. + * We expect a crash. + */ + + testFormatConstructor(Packet::Format::HF_INET_TCP); + testFormatConstructor(Packet::Format::HF_INET6_TCP); + testFormatConstructorException(Packet::Format::HF_INET_ICMP); + testFormatConstructorException(Packet::Format::HF_INET6_ICMP); + testFormatConstructor(Packet::Format::HF_INET_TCP_AH); + testFormatConstructor(Packet::Format::HF_INET6_TCP_AH); + testFormatConstructorException(Packet::Format::HF_INET_ICMP_AH); + testFormatConstructorException(Packet::Format::HF_INET6_ICMP_AH); +} + +TEST_F(InterestTest, ConstructorWithName) { + /** + * Without arguments it should be format = HF_UNSPEC. + * We expect a crash. + */ + Name n("b001::1|123"); + + try { + Interest interest(n); + } catch (...) { + FAIL() << "ERROR: Unexpected exception thrown"; + } +} + +TEST_F(InterestTest, ConstructorWithBuffer) { + // Ensure buffer is interest + auto ret = Interest::isInterest(&buffer_[0]); + EXPECT_TRUE(ret); + + // Create interest from buffer + try { + Interest interest(Interest::COPY_BUFFER, &buffer_[0], buffer_.size()); + } catch (...) { + FAIL() << "ERROR: Unexpected exception thrown"; + } + + std::vector buffer2{// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV6_HEADER(ICMP6_PROTO, 60 + 44), + // ICMP6 echo request + TCP_HEADER(0x00), + // Payload + PAYLOAD}; + + // Ensure this throws an exception + try { + Interest interest(Interest::COPY_BUFFER, &buffer2[0], buffer2.size()); + FAIL() << "We expected an exception here"; + } catch (errors::MalformedPacketException &exc) { + // Ok right exception + } catch (...) { + FAIL() << "Wrong exception thrown"; + } +} + +TEST_F(InterestTest, SetGetName) { + // Create interest from buffer + Interest interest(Interest::COPY_BUFFER, &buffer_[0], buffer_.size()); + + // Get name + auto n = interest.getName(); + + // ensure name is b002::ca|1 + Name n2("b002::ca|1"); + auto ret = (n == n2); + + EXPECT_TRUE(ret); + + Name n3("b003::1234|1234"); + + // Change name to b003::1234|1234 + interest.setName(n3); + + // Check name was set + n = interest.getName(); + ret = (n == n3); + EXPECT_TRUE(ret); +} + +TEST_F(InterestTest, SetGetLocator) { + // Create interest from buffer + Interest interest(Interest::COPY_BUFFER, &buffer_[0], buffer_.size()); + + // Get locator + auto l = interest.getLocator(); + + ip_address_t address; + ip_address_pton("b006::ab:cdab:cdef", &address); + auto ret = !std::memcmp(&l, &address, sizeof(address)); + + EXPECT_TRUE(ret); + + // Set different locator + ip_address_pton("2001::1234::4321::abcd::", &address); + + // Set it on interest + interest.setLocator(address); + + // Check it was set + l = interest.getLocator(); + ret = !std::memcmp(&l, &address, sizeof(address)); + + EXPECT_TRUE(ret); +} + +TEST_F(InterestTest, SetGetLifetime) { + // Create interest from buffer + Interest interest; + const constexpr uint32_t lifetime = 10000; + + // Set lifetime + interest.setLifetime(lifetime); + + // Get lifetime + auto l = interest.getLifetime(); + + // Ensure they are the same + EXPECT_EQ(l, lifetime); +} + +TEST_F(InterestTest, HasManifest) { + // Create interest from buffer + Interest interest; + + // Let's expect anexception here + try { + interest.setPayloadType(PayloadType::UNSPECIFIED); + FAIL() << "We expect an esception here"; + } catch (errors::RuntimeException &exc) { + // Ok right exception + } catch (...) { + FAIL() << "Wrong exception thrown"; + } + + interest.setPayloadType(PayloadType::DATA); + EXPECT_FALSE(interest.hasManifest()); + + interest.setPayloadType(PayloadType::MANIFEST); + EXPECT_TRUE(interest.hasManifest()); +} + +TEST_F(InterestTest, AppendSuffixesEncodeAndIterate) { + // Create interest from buffer + Interest interest; + + // Appenad some suffixes, with some duplicates + interest.appendSuffix(1); + interest.appendSuffix(2); + interest.appendSuffix(5); + interest.appendSuffix(3); + interest.appendSuffix(4); + interest.appendSuffix(5); + interest.appendSuffix(5); + interest.appendSuffix(5); + interest.appendSuffix(5); + interest.appendSuffix(5); + + // Encode them in wire format + interest.encodeSuffixes(); + + // Iterate over them. They should be in order and without repetitions + auto suffix = interest.firstSuffix(); + auto n_suffixes = interest.numberOfSuffixes(); + + for (uint32_t i = 0; i < n_suffixes; i++) { + EXPECT_EQ(*(suffix + i), (i + 1)); + } +} + +} // namespace core +} // namespace transport + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/libtransport/src/test/test_packet.cc b/libtransport/src/test/test_packet.cc new file mode 100644 index 000000000..0ee140e2c --- /dev/null +++ b/libtransport/src/test/test_packet.cc @@ -0,0 +1,1047 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include + +namespace transport { + +namespace core { + +/** + * Since packet is an abstract class, we derive a concrete class to be used for + * the test. + */ +class PacketForTest : public Packet { + public: + template + PacketForTest(Args &&... args) : Packet(std::forward(args)...) {} + + virtual ~PacketForTest() {} + + const Name &getName() const override { + throw errors::NotImplementedException(); + } + + Name &getWritableName() override { throw errors::NotImplementedException(); } + + void setName(const Name &name) override { + throw errors::NotImplementedException(); + } + + void setName(Name &&name) override { + throw errors::NotImplementedException(); + } + + void setLifetime(uint32_t lifetime) override { + throw errors::NotImplementedException(); + } + + uint32_t getLifetime() const override { + throw errors::NotImplementedException(); + } + + void setLocator(const ip_address_t &locator) override { + throw errors::NotImplementedException(); + } + + void resetForHash() override { throw errors::NotImplementedException(); } + + ip_address_t getLocator() const override { + throw errors::NotImplementedException(); + } +}; + +namespace { +// The fixture for testing class Foo. +class PacketTest : public ::testing::Test { + protected: + PacketTest() + : name_("b001::123|321"), + packet(Packet::COPY_BUFFER, &raw_packets_[HF_INET6_TCP][0], + raw_packets_[HF_INET6_TCP].size()) { + // You can do set-up work for each test here. + } + + virtual ~PacketTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + Name name_; + + PacketForTest packet; + + static std::map> raw_packets_; + + std::vector payload = { + 0x11, 0x11, 0x01, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad // , 0x00, 0x00, + // 0x00, 0x45, 0xa3, + // 0xd1, 0xf2, 0x2b, + // 0x94, 0x41, 0x22, + // 0xc9, 0x00, 0x00, + // 0x00, 0x44, 0xa3, + // 0xd1, 0xf2, 0x2b, + // 0x94, 0x41, 0x22, + // 0xc8 + }; +}; + +std::map> PacketTest::raw_packets_ = { + {Packet::Format::HF_INET6_TCP, + + {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV6_HEADER(TCP_PROTO, 20 + PAYLOAD_SIZE), + // TCP src=0x1234 dst=0x4321, seq=0x0001 + TCP_HEADER(0x00), + // Payload + PAYLOAD}}, + + {Packet::Format::HF_INET_TCP, + {// IPv4 src=3.13.127.8, dst=192.168.1.92 + IPV4_HEADER(TCP_PROTO, 20 + PAYLOAD_SIZE), + // TCP src=0x1234 dst=0x4321, seq=0x0001 + TCP_HEADER(0x00), + // Other + PAYLOAD}}, + + {Packet::Format::HF_INET_ICMP, + {// IPv4 src=3.13.127.8, dst=192.168.1.92 + IPV4_HEADER(ICMP_PROTO, 64), + // ICMP echo request + ICMP_ECHO_REQUEST}}, + + {Packet::Format::HF_INET6_ICMP, + {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV6_HEADER(ICMP6_PROTO, 60), + // ICMP6 echo request + ICMP6_ECHO_REQUEST}}, + + {Packet::Format::HF_INET6_TCP_AH, + {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV6_HEADER(TCP_PROTO, 20 + 44 + 128), + // ICMP6 echo request + TCP_HEADER(0x18), + // hICN AH header + AH_HEADER}}, + + {Packet::Format::HF_INET_TCP_AH, + {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV4_HEADER(TCP_PROTO, 20 + 44 + 128), + // ICMP6 echo request + TCP_HEADER(0x18), + // hICN AH header + AH_HEADER}}, + + // XXX No flag defined in ICMP header to signal AH header. + {Packet::Format::HF_INET_ICMP_AH, + {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV4_HEADER(ICMP_PROTO, 64 + 44), + // ICMP6 echo request + ICMP_ECHO_REQUEST, + // hICN AH header + AH_HEADER}}, + + {Packet::Format::HF_INET6_ICMP_AH, + {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV6_HEADER(ICMP6_PROTO, 60 + 44), + // ICMP6 echo request + ICMP6_ECHO_REQUEST, + // hICN AH header + AH_HEADER}}, + +}; + +void testFormatConstructor(Packet::Format format = HF_UNSPEC) { + try { + PacketForTest packet(format); + } catch (...) { + FAIL() << "ERROR: Unexpected exception thrown for " << format; + } +} + +void testFormatAndAdditionalHeaderConstructor(Packet::Format format, + std::size_t additional_header) { + PacketForTest packet(format, additional_header); + // Packet length should be the one of the normal header + the + // additional_header + + EXPECT_EQ(packet.headerSize(), + Packet::getHeaderSizeFromFormat(format) + additional_header); +} + +void testRawBufferConstructor(std::vector packet, + Packet::Format format) { + try { + // Try to construct packet from correct buffer + PacketForTest p(Packet::WRAP_BUFFER, &packet[0], packet.size(), + packet.size()); + + // Check format is expected one. + EXPECT_EQ(p.getFormat(), format); + + // // Try the same using a MemBuf + // auto buf = utils::MemBuf::wrapBuffer(&packet[0], packet.size()); + // buf->append(packet.size()); + // PacketForTest p2(std::move(buf)); + + // EXPECT_EQ(p2.getFormat(), format); + } catch (...) { + FAIL() << "ERROR: Unexpected exception thrown"; + } + + try { + // Try to construct packet from wrong buffer + + // Modify next header to 0 + /* ipv6 */ + packet[6] = 0x00; + /* ipv4 */ + packet[9] = 0x00; + PacketForTest p(Packet::WRAP_BUFFER, &packet[0], packet.size(), + packet.size()); + + // Format should fallback to HF_UNSPEC + EXPECT_EQ(p.getFormat(), HF_UNSPEC); + } catch (...) { + FAIL() << "ERROR: Unexpected exception thrown."; + } +} + +void getHeaderSizeFromBuffer(Packet::Format format, + std::vector &packet, + std::size_t expected) { + auto header_size = PacketForTest::getHeaderSizeFromBuffer(format, &packet[0]); + EXPECT_EQ(header_size, expected); +} + +void getHeaderSizeFromFormat(Packet::Format format, std::size_t expected) { + auto header_size = PacketForTest::getHeaderSizeFromFormat(format); + EXPECT_EQ(header_size, expected); +} + +void getPayloadSizeFromBuffer(Packet::Format format, + std::vector &packet, + std::size_t expected) { + auto payload_size = + PacketForTest::getPayloadSizeFromBuffer(format, &packet[0]); + EXPECT_EQ(payload_size, expected); +} + +void getFormatFromBuffer(Packet::Format expected, + std::vector &packet) { + auto format = PacketForTest::getFormatFromBuffer(&packet[0], packet.size()); + EXPECT_EQ(format, expected); +} + +void getHeaderSize(std::size_t expected, const PacketForTest &packet) { + auto size = packet.headerSize(); + EXPECT_EQ(size, expected); +} + +void testGetFormat(Packet::Format expected, const Packet &packet) { + auto format = packet.getFormat(); + EXPECT_EQ(format, expected); +} + +} // namespace + +TEST_F(PacketTest, ConstructorWithFormat) { + testFormatConstructor(Packet::Format::HF_INET_TCP); + testFormatConstructor(Packet::Format::HF_INET6_TCP); + testFormatConstructor(Packet::Format::HF_INET_ICMP); + testFormatConstructor(Packet::Format::HF_INET6_ICMP); + testFormatConstructor(Packet::Format::HF_INET_TCP_AH); + testFormatConstructor(Packet::Format::HF_INET6_TCP_AH); + testFormatConstructor(Packet::Format::HF_INET_ICMP_AH); + testFormatConstructor(Packet::Format::HF_INET6_ICMP_AH); +} + +TEST_F(PacketTest, ConstructorWithFormatAndAdditionalHeader) { + testFormatAndAdditionalHeaderConstructor(Packet::Format::HF_INET_TCP, 123); + testFormatAndAdditionalHeaderConstructor(Packet::Format::HF_INET6_TCP, 360); + testFormatAndAdditionalHeaderConstructor(Packet::Format::HF_INET_ICMP, 21); + testFormatAndAdditionalHeaderConstructor(Packet::Format::HF_INET6_ICMP, 444); + testFormatAndAdditionalHeaderConstructor(Packet::Format::HF_INET_TCP_AH, 555); + testFormatAndAdditionalHeaderConstructor(Packet::Format::HF_INET6_TCP_AH, + 321); + testFormatAndAdditionalHeaderConstructor(Packet::Format::HF_INET_ICMP_AH, + 123); + testFormatAndAdditionalHeaderConstructor(Packet::Format::HF_INET6_ICMP_AH, + 44); +} + +TEST_F(PacketTest, ConstructorWithNew) { + auto &_packet = raw_packets_[HF_INET6_TCP]; + auto packet_ptr = new PacketForTest(Packet::WRAP_BUFFER, &_packet[0], + _packet.size(), _packet.size()); + (void)packet_ptr; +} + +TEST_F(PacketTest, ConstructorWithRawBufferInet6Tcp) { + auto format = Packet::Format::HF_INET6_TCP; + testRawBufferConstructor(raw_packets_[format], format); +} + +TEST_F(PacketTest, ConstructorWithRawBufferInetTcp) { + auto format = Packet::Format::HF_INET_TCP; + testRawBufferConstructor(raw_packets_[format], format); +} + +TEST_F(PacketTest, ConstructorWithRawBufferInetIcmp) { + auto format = Packet::Format::HF_INET_ICMP; + testRawBufferConstructor(raw_packets_[format], format); +} + +TEST_F(PacketTest, ConstructorWithRawBufferInet6Icmp) { + auto format = Packet::Format::HF_INET6_ICMP; + testRawBufferConstructor(raw_packets_[format], format); +} + +TEST_F(PacketTest, ConstructorWithRawBufferInet6TcpAh) { + auto format = Packet::Format::HF_INET6_TCP_AH; + testRawBufferConstructor(raw_packets_[format], format); +} + +TEST_F(PacketTest, ConstructorWithRawBufferInetTcpAh) { + auto format = Packet::Format::HF_INET_TCP_AH; + testRawBufferConstructor(raw_packets_[format], format); +} + +TEST_F(PacketTest, MoveConstructor) { + PacketForTest p0(Packet::Format::HF_INET6_TCP); + PacketForTest p1(std::move(p0)); + EXPECT_EQ(p0.getFormat(), Packet::Format::HF_UNSPEC); + EXPECT_EQ(p1.getFormat(), Packet::Format::HF_INET6_TCP); +} + +TEST_F(PacketTest, TestGetHeaderSizeFromBuffer) { + getHeaderSizeFromBuffer(HF_INET6_TCP, raw_packets_[HF_INET6_TCP], + HICN_V6_TCP_HDRLEN); + getHeaderSizeFromBuffer(HF_INET_TCP, raw_packets_[HF_INET_TCP], + HICN_V4_TCP_HDRLEN); + getHeaderSizeFromBuffer(HF_INET6_ICMP, raw_packets_[HF_INET6_ICMP], + IPV6_HDRLEN + 4); + getHeaderSizeFromBuffer(HF_INET_ICMP, raw_packets_[HF_INET_ICMP], + IPV4_HDRLEN + 4); + getHeaderSizeFromBuffer(HF_INET6_TCP_AH, raw_packets_[HF_INET6_TCP_AH], + HICN_V6_TCP_AH_HDRLEN + 128); + getHeaderSizeFromBuffer(HF_INET_TCP_AH, raw_packets_[HF_INET_TCP_AH], + HICN_V4_TCP_AH_HDRLEN + 128); +} + +TEST_F(PacketTest, TestGetHeaderSizeFromFormat) { + getHeaderSizeFromFormat(HF_INET6_TCP, HICN_V6_TCP_HDRLEN); + getHeaderSizeFromFormat(HF_INET_TCP, HICN_V4_TCP_HDRLEN); + getHeaderSizeFromFormat(HF_INET6_ICMP, IPV6_HDRLEN + 4); + getHeaderSizeFromFormat(HF_INET_ICMP, IPV4_HDRLEN + 4); + getHeaderSizeFromFormat(HF_INET6_TCP_AH, HICN_V6_TCP_AH_HDRLEN); + getHeaderSizeFromFormat(HF_INET_TCP_AH, HICN_V4_TCP_AH_HDRLEN); +} + +TEST_F(PacketTest, TestGetPayloadSizeFromBuffer) { + getPayloadSizeFromBuffer(HF_INET6_TCP, raw_packets_[HF_INET6_TCP], 12); + getPayloadSizeFromBuffer(HF_INET_TCP, raw_packets_[HF_INET_TCP], 12); + getPayloadSizeFromBuffer(HF_INET6_ICMP, raw_packets_[HF_INET6_ICMP], 56); + getPayloadSizeFromBuffer(HF_INET_ICMP, raw_packets_[HF_INET_ICMP], 60); + getPayloadSizeFromBuffer(HF_INET6_TCP_AH, raw_packets_[HF_INET6_TCP_AH], 0); + getPayloadSizeFromBuffer(HF_INET_TCP_AH, raw_packets_[HF_INET_TCP_AH], 0); +} + +TEST_F(PacketTest, TestIsInterest) { + auto ret = PacketForTest::isInterest(&raw_packets_[HF_INET6_TCP][0]); + + EXPECT_TRUE(ret); +} + +TEST_F(PacketTest, TestGetFormatFromBuffer) { + getFormatFromBuffer(HF_INET6_TCP, raw_packets_[HF_INET6_TCP]); + getFormatFromBuffer(HF_INET_TCP, raw_packets_[HF_INET_TCP]); + getFormatFromBuffer(HF_INET6_ICMP, raw_packets_[HF_INET6_ICMP]); + getFormatFromBuffer(HF_INET_ICMP, raw_packets_[HF_INET_ICMP]); + getFormatFromBuffer(HF_INET6_TCP_AH, raw_packets_[HF_INET6_TCP_AH]); + getFormatFromBuffer(HF_INET_TCP_AH, raw_packets_[HF_INET_TCP_AH]); +} + +// TEST_F(PacketTest, TestReplace) { +// PacketForTest packet(Packet::WRAP_BUFFER, &raw_packets_[HF_INET6_TCP][0], +// raw_packets_[HF_INET6_TCP].size()); + +// // Replace current packet with another one +// packet.replace(&raw_packets_[HF_INET_TCP][0], +// raw_packets_[HF_INET_TCP].size()); + +// // Check new format +// ASSERT_EQ(packet.getFormat(), HF_INET_TCP); +// } + +TEST_F(PacketTest, TestPayloadSize) { + // Check payload size of existing packet + auto &_packet = raw_packets_[HF_INET6_TCP]; + PacketForTest packet(Packet::WRAP_BUFFER, &_packet[0], _packet.size(), + _packet.size()); + + EXPECT_EQ(packet.payloadSize(), std::size_t(PAYLOAD_SIZE)); + + // Check for dynamic generated packet + std::string payload0(1024, 'X'); + + // Create the packet + PacketForTest packet2(HF_INET6_TCP); + + // Payload size should now be zero + EXPECT_EQ(packet2.payloadSize(), std::size_t(0)); + + // Append payload 1 time + packet2.appendPayload((const uint8_t *)payload0.c_str(), payload0.size()); + + // size should now be 1024 + EXPECT_EQ(packet2.payloadSize(), std::size_t(1024)); + + // Append second payload + std::string payload1(1024, 'X'); + packet2.appendPayload((const uint8_t *)payload1.c_str(), payload1.size()); + + // Check size is 2048 + EXPECT_EQ(packet2.payloadSize(), std::size_t(2048)); + + // Append Membuf + packet2.appendPayload(utils::MemBuf::copyBuffer( + (const uint8_t *)payload1.c_str(), payload1.size())); + + // Check size is 3072 + EXPECT_EQ(packet2.payloadSize(), std::size_t(3072)); +} + +TEST_F(PacketTest, TestHeaderSize) { + getHeaderSize(HICN_V6_TCP_HDRLEN, + PacketForTest(Packet::Format::HF_INET6_TCP)); + getHeaderSize(HICN_V4_TCP_HDRLEN, PacketForTest(Packet::Format::HF_INET_TCP)); + getHeaderSize(HICN_V6_ICMP_HDRLEN, + PacketForTest(Packet::Format::HF_INET6_ICMP)); + getHeaderSize(HICN_V4_ICMP_HDRLEN, + PacketForTest(Packet::Format::HF_INET_ICMP)); + getHeaderSize(HICN_V6_TCP_AH_HDRLEN, + PacketForTest(Packet::Format::HF_INET6_TCP_AH)); + getHeaderSize(HICN_V4_TCP_AH_HDRLEN, + PacketForTest(Packet::Format::HF_INET_TCP_AH)); +} + +TEST_F(PacketTest, TestMemBufReference) { + // Create packet + auto &_packet = raw_packets_[HF_INET6_TCP]; + + // Packet was not created as a shared_ptr. If we try to get a membuf shared + // ptr we should get an exception. + // TODO test with c++ 17 + // try { + // PacketForTest packet(&_packet[0], _packet.size()); + // auto membuf_ref = packet.acquireMemBufReference(); + // FAIL() << "The acquireMemBufReference() call should have throwed an " + // "exception!"; + // } catch (const std::bad_weak_ptr &e) { + // // Ok + // } catch (...) { + // FAIL() << "Not expected exception."; + // } + + auto packet_ptr = std::make_shared( + Packet::WRAP_BUFFER, &_packet[0], _packet.size(), _packet.size()); + PacketForTest &packet = *packet_ptr; + + // Acquire a reference to the membuf + auto membuf_ref = packet.acquireMemBufReference(); + + // Check refcount. It should be 2 + EXPECT_EQ(membuf_ref.use_count(), 2); + + // Now increment membuf references + Packet::MemBufPtr membuf = packet.acquireMemBufReference(); + + // Now reference count should be 2 + EXPECT_EQ(membuf_ref.use_count(), 3); + + // Copy again + Packet::MemBufPtr membuf2 = membuf; + + // Now reference count should be 3 + EXPECT_EQ(membuf_ref.use_count(), 4); +} + +TEST_F(PacketTest, TestReset) { + // Check everything is ok + EXPECT_EQ(packet.getFormat(), HF_INET6_TCP); + EXPECT_EQ(packet.length(), raw_packets_[HF_INET6_TCP].size()); + EXPECT_EQ(packet.headerSize(), HICN_V6_TCP_HDRLEN); + EXPECT_EQ(packet.payloadSize(), packet.length() - packet.headerSize()); + + // Reset the packet + packet.reset(); + + // Rerun test + EXPECT_EQ(packet.getFormat(), HF_UNSPEC); + EXPECT_EQ(packet.length(), std::size_t(0)); + EXPECT_EQ(packet.headerSize(), std::size_t(0)); + EXPECT_EQ(packet.payloadSize(), std::size_t(0)); +} + +TEST_F(PacketTest, TestAppendPayload) { + // Append payload with raw buffer + uint8_t raw_buffer[2048]; + auto original_payload_length = packet.payloadSize(); + packet.appendPayload(raw_buffer, 1024); + + EXPECT_EQ(original_payload_length + 1024, packet.payloadSize()); + + for (int i = 0; i < 10; i++) { + // Append other payload 10 times + packet.appendPayload(raw_buffer, 1024); + EXPECT_EQ(original_payload_length + 1024 + (1024) * (i + 1), + packet.payloadSize()); + } + + // Append payload using membuf + packet.appendPayload(utils::MemBuf::copyBuffer(raw_buffer, 2048)); + EXPECT_EQ(original_payload_length + 1024 + 1024 * 10 + 2048, + packet.payloadSize()); + + // Check the underlying MemBuf length is the expected one + utils::MemBuf *current = &packet; + size_t total = 0; + do { + total += current->length(); + current = current->next(); + } while (current != &packet); + + EXPECT_EQ(total, packet.headerSize() + packet.payloadSize()); + + // LEt's try now to reset this packet + packet.reset(); + + // There should be no more bufferls left in the chain + EXPECT_EQ(&packet, packet.next()); + EXPECT_EQ(packet.getFormat(), HF_UNSPEC); + EXPECT_EQ(packet.length(), std::size_t(0)); + EXPECT_EQ(packet.headerSize(), std::size_t(0)); + EXPECT_EQ(packet.payloadSize(), std::size_t(0)); +} + +TEST_F(PacketTest, GetPayload) { + // Append payload with raw buffer + uint8_t raw_buffer[2048]; + auto original_payload_length = packet.payloadSize(); + packet.appendPayload(raw_buffer, 2048); + + // Get payload + auto payload = packet.getPayload(); + // Check payload length is correct + utils::MemBuf *current = payload.get(); + size_t total = 0; + do { + total += current->length(); + current = current->next(); + } while (current != payload.get()); + + ASSERT_EQ(total, packet.payloadSize()); + + // Linearize the payload + payload->gather(total); + + // Check memory correspond + payload->trimStart(original_payload_length); + auto ret = memcmp(raw_buffer, payload->data(), 2048); + EXPECT_EQ(ret, 0); +} + +TEST_F(PacketTest, UpdateLength) { + auto original_payload_size = packet.payloadSize(); + + // Add some fake payload without using the API + packet.append(200); + + // payloadSize does not know about the new payload, yet + EXPECT_EQ(packet.payloadSize(), original_payload_size); + + // Let's now update the packet length + packet.updateLength(); + + // Now payloadSize knows + EXPECT_EQ(packet.payloadSize(), std::size_t(original_payload_size + 200)); + + // We may also update the length without adding real content. This is only + // written in the packet header. + packet.updateLength(128); + EXPECT_EQ(packet.payloadSize(), + std::size_t(original_payload_size + 200 + 128)); +} + +TEST_F(PacketTest, SetGetPayloadType) { + auto payload_type = packet.getPayloadType(); + + // It should be normal content object by default + EXPECT_EQ(payload_type, PayloadType::DATA); + + // Set it to be manifest + packet.setPayloadType(PayloadType::MANIFEST); + + // Check it is manifest + payload_type = packet.getPayloadType(); + + EXPECT_EQ(payload_type, PayloadType::MANIFEST); +} + +TEST_F(PacketTest, GetFormat) { + { + PacketForTest p0(Packet::WRAP_BUFFER, + &raw_packets_[Packet::Format::HF_INET_TCP][0], + raw_packets_[Packet::Format::HF_INET_TCP].size(), + raw_packets_[Packet::Format::HF_INET_TCP].size()); + testGetFormat(Packet::Format::HF_INET_TCP, p0); + + PacketForTest p1(Packet::WRAP_BUFFER, + &raw_packets_[Packet::Format::HF_INET6_TCP][0], + raw_packets_[Packet::Format::HF_INET6_TCP].size(), + raw_packets_[Packet::Format::HF_INET6_TCP].size()); + testGetFormat(Packet::Format::HF_INET6_TCP, p1); + + PacketForTest p2(Packet::WRAP_BUFFER, + &raw_packets_[Packet::Format::HF_INET_ICMP][0], + raw_packets_[Packet::Format::HF_INET_ICMP].size(), + raw_packets_[Packet::Format::HF_INET_ICMP].size()); + testGetFormat(Packet::Format::HF_INET_ICMP, p2); + + PacketForTest p3(Packet::WRAP_BUFFER, + &raw_packets_[Packet::Format::HF_INET6_ICMP][0], + raw_packets_[Packet::Format::HF_INET6_ICMP].size(), + raw_packets_[Packet::Format::HF_INET6_ICMP].size()); + testGetFormat(Packet::Format::HF_INET6_ICMP, p3); + + PacketForTest p4(Packet::WRAP_BUFFER, + &raw_packets_[Packet::Format::HF_INET_TCP_AH][0], + raw_packets_[Packet::Format::HF_INET_TCP_AH].size(), + raw_packets_[Packet::Format::HF_INET_TCP_AH].size()); + testGetFormat(Packet::Format::HF_INET_TCP_AH, p4); + + PacketForTest p5(Packet::WRAP_BUFFER, + &raw_packets_[Packet::Format::HF_INET6_TCP_AH][0], + raw_packets_[Packet::Format::HF_INET6_TCP_AH].size(), + raw_packets_[Packet::Format::HF_INET6_TCP_AH].size()); + testGetFormat(Packet::Format::HF_INET6_TCP_AH, p5); + } + + // Let's try now creating empty packets + { + PacketForTest p0(Packet::Format::HF_INET_TCP); + testGetFormat(Packet::Format::HF_INET_TCP, p0); + + PacketForTest p1(Packet::Format::HF_INET6_TCP); + testGetFormat(Packet::Format::HF_INET6_TCP, p1); + + PacketForTest p2(Packet::Format::HF_INET_ICMP); + testGetFormat(Packet::Format::HF_INET_ICMP, p2); + + PacketForTest p3(Packet::Format::HF_INET6_ICMP); + testGetFormat(Packet::Format::HF_INET6_ICMP, p3); + + PacketForTest p4(Packet::Format::HF_INET_TCP_AH); + testGetFormat(Packet::Format::HF_INET_TCP_AH, p4); + + PacketForTest p5(Packet::Format::HF_INET6_TCP_AH); + testGetFormat(Packet::Format::HF_INET6_TCP_AH, p5); + } +} + +TEST_F(PacketTest, SetGetTestSignatureTimestamp) { + // Let's try to set the signature timestamp in a packet without AH header. We + // expect an exception. + using namespace std::chrono; + uint64_t now = + duration_cast(system_clock::now().time_since_epoch()) + .count(); + + try { + packet.setSignatureTimestamp(now); + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + // Same fot get method + try { + auto t = packet.getSignatureTimestamp(); + // Let's make compiler happy + (void)t; + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + // Now let's construct a AH packet, with no additional space for signature + PacketForTest p(HF_INET6_TCP_AH); + p.setSignatureTimestamp(now); + uint64_t now_get = p.getSignatureTimestamp(); + + // Check we got the right value + EXPECT_EQ(now_get, now); +} + +TEST_F(PacketTest, TestSetGetValidationAlgorithm) { + // Let's try to set the validation algorithm in a packet without AH header. We + // expect an exception. + + try { + packet.setValidationAlgorithm(auth::CryptoSuite::RSA_SHA256); + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + // Same fot get method + try { + auto v = packet.getSignatureTimestamp(); + // Let's make compiler happy + (void)v; + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + // Now let's construct a AH packet, with no additional space for signature + PacketForTest p(HF_INET6_TCP_AH); + p.setValidationAlgorithm(auth::CryptoSuite::RSA_SHA256); + auto v_get = p.getValidationAlgorithm(); + + // Check we got the right value + EXPECT_EQ(v_get, auth::CryptoSuite::RSA_SHA256); +} + +TEST_F(PacketTest, TestSetGetKeyId) { + uint8_t key[32]; + auth::KeyId key_id = std::make_pair(key, sizeof(key)); + + try { + packet.setKeyId(key_id); + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + // Same fot get method + try { + auto k = packet.getKeyId(); + // Let's make compiler happy + (void)k; + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + // Now let's construct a AH packet, with no additional space for signature + PacketForTest p(HF_INET6_TCP_AH); + p.setKeyId(key_id); + auto p_get = p.getKeyId(); + + // Check we got the right value + EXPECT_EQ(p_get.second, key_id.second); + + auto ret = memcmp(p_get.first, key_id.first, p_get.second); + EXPECT_EQ(ret, 0); +} + +TEST_F(PacketTest, DISABLED_TestChecksum) { + // Checksum should be wrong + bool integrity = packet.checkIntegrity(); + EXPECT_FALSE(integrity); + + // Let's fix it + packet.setChecksum(); + + // Check again + integrity = packet.checkIntegrity(); + EXPECT_TRUE(integrity); + + // Check with AH header and 300 bytes signature + PacketForTest p(HF_INET6_TCP_AH, 300); + std::string payload(5000, 'X'); + p.appendPayload((const uint8_t *)payload.c_str(), payload.size() / 2); + p.appendPayload((const uint8_t *)(payload.c_str() + payload.size() / 2), + payload.size() / 2); + + p.setChecksum(); + integrity = p.checkIntegrity(); + EXPECT_TRUE(integrity); +} + +TEST_F(PacketTest, TestSetSyn) { + // Test syn of non-tcp format and check exception is thrown + try { + auto p = PacketForTest(Packet::WRAP_BUFFER, &raw_packets_[HF_INET6_ICMP][0], + raw_packets_[HF_INET6_ICMP].size(), + raw_packets_[HF_INET6_ICMP].size()); + // Let's make compiler happy + p.setSyn(); + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + packet.setSyn(); + EXPECT_TRUE(packet.testSyn()); + + packet.resetSyn(); + EXPECT_FALSE(packet.testSyn()); +} + +TEST_F(PacketTest, TestSetFin) { + // Test syn of non-tcp format and check exception is thrown + try { + auto p = PacketForTest(Packet::WRAP_BUFFER, &raw_packets_[HF_INET6_ICMP][0], + raw_packets_[HF_INET6_ICMP].size(), + raw_packets_[HF_INET6_ICMP].size()); + // Let's make compiler happy + p.setFin(); + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + packet.setFin(); + EXPECT_TRUE(packet.testFin()); + + packet.resetFin(); + EXPECT_FALSE(packet.testFin()); +} + +TEST_F(PacketTest, TestSetAck) { + // Test syn of non-tcp format and check exception is thrown + try { + auto p = PacketForTest(Packet::WRAP_BUFFER, &raw_packets_[HF_INET6_ICMP][0], + raw_packets_[HF_INET6_ICMP].size(), + raw_packets_[HF_INET6_ICMP].size()); + // Let's make compiler happy + p.setAck(); + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + packet.setAck(); + EXPECT_TRUE(packet.testAck()); + + packet.resetAck(); + EXPECT_FALSE(packet.testAck()); +} + +TEST_F(PacketTest, TestSetRst) { + // Test syn of non-tcp format and check exception is thrown + try { + auto p = PacketForTest(Packet::WRAP_BUFFER, &raw_packets_[HF_INET6_ICMP][0], + raw_packets_[HF_INET6_ICMP].size(), + raw_packets_[HF_INET6_ICMP].size()); + // Let's make compiler happy + p.setRst(); + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + packet.setRst(); + EXPECT_TRUE(packet.testRst()); + + packet.resetRst(); + EXPECT_FALSE(packet.testRst()); +} + +TEST_F(PacketTest, TestResetFlags) { + packet.setRst(); + packet.setSyn(); + packet.setAck(); + packet.setFin(); + EXPECT_TRUE(packet.testRst()); + EXPECT_TRUE(packet.testAck()); + EXPECT_TRUE(packet.testFin()); + EXPECT_TRUE(packet.testSyn()); + + packet.resetFlags(); + EXPECT_FALSE(packet.testRst()); + EXPECT_FALSE(packet.testAck()); + EXPECT_FALSE(packet.testFin()); + EXPECT_FALSE(packet.testSyn()); +} + +TEST_F(PacketTest, TestSetGetSrcPort) { + try { + auto p = PacketForTest(Packet::WRAP_BUFFER, &raw_packets_[HF_INET6_ICMP][0], + raw_packets_[HF_INET6_ICMP].size(), + raw_packets_[HF_INET6_ICMP].size()); + // Let's make compiler happy + p.setSrcPort(12345); + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + packet.setSrcPort(12345); + EXPECT_EQ(packet.getSrcPort(), 12345); +} + +TEST_F(PacketTest, TestSetGetDstPort) { + try { + auto p = PacketForTest(Packet::WRAP_BUFFER, &raw_packets_[HF_INET6_ICMP][0], + raw_packets_[HF_INET6_ICMP].size(), + raw_packets_[HF_INET6_ICMP].size()); + // Let's make compiler happy + p.setDstPort(12345); + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + packet.setDstPort(12345); + EXPECT_EQ(packet.getDstPort(), 12345); +} + +TEST_F(PacketTest, TestEnsureCapacity) { + PacketForTest &p = packet; + + // This shoul be false + auto ret = p.ensureCapacity(raw_packets_[HF_INET6_TCP].size() + 10); + EXPECT_FALSE(ret); + + // This should be true + ret = p.ensureCapacity(raw_packets_[HF_INET6_TCP].size()); + EXPECT_TRUE(ret); + + // This should be true + ret = p.ensureCapacity(raw_packets_[HF_INET6_TCP].size() - 10); + EXPECT_TRUE(ret); + + // Try to trim the packet start + p.trimStart(10); + // Now this should be false + ret = p.ensureCapacity(raw_packets_[HF_INET6_TCP].size()); + EXPECT_FALSE(ret); + + // Create a new packet + auto p2 = PacketForTest(Packet::WRAP_BUFFER, &raw_packets_[HF_INET6_ICMP][0], + raw_packets_[HF_INET6_ICMP].size(), + raw_packets_[HF_INET6_ICMP].size()); + + p2.appendPayload(utils::MemBuf::createCombined(2000)); + + // This should be false, since the buffer is chained + ret = p2.ensureCapacity(raw_packets_[HF_INET6_TCP].size() - 10); + EXPECT_FALSE(ret); +} + +TEST_F(PacketTest, TestEnsureCapacityAndFillUnused) { + // Create packet by excluding the payload (So only L3 + L4 headers). The + // payload will be trated as unused tailroom + PacketForTest p = + PacketForTest(Packet::WRAP_BUFFER, &raw_packets_[HF_INET6_TCP][0], + raw_packets_[HF_INET6_TCP].size() - PAYLOAD_SIZE, + raw_packets_[HF_INET6_TCP].size()); + + // Copy original packet payload, which is here trated as a unused tailroom + uint8_t original_payload[PAYLOAD_SIZE]; + uint8_t *payload = &raw_packets_[HF_INET6_TCP][0] + + raw_packets_[HF_INET6_TCP].size() - PAYLOAD_SIZE; + std::memcpy(original_payload, payload, PAYLOAD_SIZE); + + // This should be true and the unused tailroom should be unmodified + auto ret = p.ensureCapacityAndFillUnused( + raw_packets_[HF_INET6_TCP].size() - (PAYLOAD_SIZE + 10), 0); + EXPECT_TRUE(ret); + ret = std::memcmp(original_payload, payload, PAYLOAD_SIZE); + EXPECT_EQ(ret, 0); + + // This should fill the payload with zeros + ret = p.ensureCapacityAndFillUnused(raw_packets_[HF_INET6_TCP].size(), 0); + EXPECT_TRUE(ret); + uint8_t zeros[PAYLOAD_SIZE]; + std::memset(zeros, 0, PAYLOAD_SIZE); + ret = std::memcmp(payload, zeros, PAYLOAD_SIZE); + EXPECT_EQ(ret, 0); + + // This should fill the payload with ones + ret = p.ensureCapacityAndFillUnused(raw_packets_[HF_INET6_TCP].size(), 1); + EXPECT_TRUE(ret); + uint8_t ones[PAYLOAD_SIZE]; + std::memset(ones, 1, PAYLOAD_SIZE); + ret = std::memcmp(payload, ones, PAYLOAD_SIZE); + EXPECT_EQ(ret, 0); + + // This should return false and the payload should be unmodified + ret = p.ensureCapacityAndFillUnused(raw_packets_[HF_INET6_TCP].size() + 1, 1); + EXPECT_FALSE(ret); + ret = std::memcmp(payload, ones, PAYLOAD_SIZE); + EXPECT_EQ(ret, 0); +} + +TEST_F(PacketTest, TestSetGetTTL) { + packet.setTTL(128); + EXPECT_EQ(packet.getTTL(), 128); +} + +} // namespace core +} // namespace transport + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/libtransport/src/transport.config b/libtransport/src/transport.config new file mode 100644 index 000000000..a21175b8d --- /dev/null +++ b/libtransport/src/transport.config @@ -0,0 +1,27 @@ +// Configuration for io_module + +io_module = { + path = []; + name = "forwarder_module"; +}; + +forwarder = { + n_threads = 1; + + listeners = { + l0 = { + local_address = "127.0.0.1"; + local_port = 33436; + } + }; + + connectors = { + c0 = { + /* local_address and local_port are optional */ + local_address = "127.0.0.1"; + local_port = 33436; + remote_address = "10.20.30.40"; + remote_port = 33436; + } + }; +}; \ No newline at end of file diff --git a/libtransport/src/utils/content_store.cc b/libtransport/src/utils/content_store.cc index cb3db6d94..c5cb91149 100644 --- a/libtransport/src/utils/content_store.cc +++ b/libtransport/src/utils/content_store.cc @@ -17,7 +17,6 @@ #include #include #include - #include namespace utils { @@ -60,8 +59,7 @@ void ContentStore::insert( ObjectTimeEntry(content_object, std::chrono::steady_clock::now()), pos); } -const std::shared_ptr ContentStore::find( - const Interest &interest) { +std::shared_ptr ContentStore::find(const Interest &interest) { utils::SpinLock::Acquire locked(cs_mutex_); std::shared_ptr ret = empty_reference_; diff --git a/libtransport/src/utils/content_store.h b/libtransport/src/utils/content_store.h index 03ce76f42..56cd2abb6 100644 --- a/libtransport/src/utils/content_store.h +++ b/libtransport/src/utils/content_store.h @@ -52,7 +52,7 @@ class ContentStore { void insert(const std::shared_ptr &content_object); - const std::shared_ptr find(const Interest &interest); + std::shared_ptr find(const Interest &interest); void erase(const Name &exact_name); diff --git a/libtransport/src/utils/daemonizator.cc b/libtransport/src/utils/daemonizator.cc index c51a68d14..bc7bae700 100644 --- a/libtransport/src/utils/daemonizator.cc +++ b/libtransport/src/utils/daemonizator.cc @@ -17,7 +17,6 @@ #include #include #include - #include #include diff --git a/libtransport/src/utils/epoll_event_reactor.cc b/libtransport/src/utils/epoll_event_reactor.cc index 63c08df95..eb8c65352 100644 --- a/libtransport/src/utils/epoll_event_reactor.cc +++ b/libtransport/src/utils/epoll_event_reactor.cc @@ -14,12 +14,11 @@ */ #include - +#include +#include #include #include -#include -#include #include namespace utils { @@ -111,7 +110,7 @@ void EpollEventReactor::runEventLoop(int timeout) { if (errno == EINTR) { continue; } else { - return; + return; } } diff --git a/libtransport/src/utils/epoll_event_reactor.h b/libtransport/src/utils/epoll_event_reactor.h index 4cb87ebd4..9ebfca937 100644 --- a/libtransport/src/utils/epoll_event_reactor.h +++ b/libtransport/src/utils/epoll_event_reactor.h @@ -16,9 +16,9 @@ #pragma once #include +#include #include -#include #include #include #include diff --git a/libtransport/src/utils/fd_deadline_timer.h b/libtransport/src/utils/fd_deadline_timer.h index 8bc3bbca3..38396e027 100644 --- a/libtransport/src/utils/fd_deadline_timer.h +++ b/libtransport/src/utils/fd_deadline_timer.h @@ -17,16 +17,14 @@ #include #include - +#include +#include #include #include #include #include -#include -#include - namespace utils { class FdDeadlineTimer : public DeadlineTimer { diff --git a/libtransport/src/utils/membuf.cc b/libtransport/src/utils/membuf.cc index 94e5b13a1..73c45cf6d 100644 --- a/libtransport/src/utils/membuf.cc +++ b/libtransport/src/utils/membuf.cc @@ -145,6 +145,18 @@ void MemBuf::operator delete(void* /* ptr */, void* /* placement */) { // constructor. } +bool MemBuf::operator==(const MemBuf& other) { + if (length() != other.length()) { + return false; + } + + return (memcmp(data(), other.data(), length()) == 0); +} + +bool MemBuf::operator!=(const MemBuf& other) { + return !this->operator==(other); +} + void MemBuf::releaseStorage(HeapStorage* storage, uint16_t freeFlags) { // Use relaxed memory order here. If we are unlucky and happen to get // out-of-date data the compare_exchange_weak() call below will catch @@ -299,21 +311,23 @@ unique_ptr MemBuf::takeOwnership(void* buf, std::size_t capacity, } } -MemBuf::MemBuf(WrapBufferOp, const void* buf, std::size_t capacity) noexcept +MemBuf::MemBuf(WrapBufferOp, const void* buf, std::size_t length, + std::size_t capacity) noexcept : MemBuf(InternalConstructor(), 0, // We cast away the const-ness of the buffer here. // This is okay since MemBuf users must use unshare() to create a // copy of this buffer before writing to the buffer. static_cast(const_cast(buf)), capacity, - static_cast(const_cast(buf)), capacity) {} + static_cast(const_cast(buf)), length) {} -unique_ptr MemBuf::wrapBuffer(const void* buf, std::size_t capacity) { - return std::make_unique(WRAP_BUFFER, buf, capacity); +unique_ptr MemBuf::wrapBuffer(const void* buf, std::size_t length, + std::size_t capacity) { + return std::make_unique(WRAP_BUFFER, buf, length, capacity); } -MemBuf MemBuf::wrapBufferAsValue(const void* buf, +MemBuf MemBuf::wrapBufferAsValue(const void* buf, std::size_t length, std::size_t capacity) noexcept { - return MemBuf(WrapBufferOp::WRAP_BUFFER, buf, capacity); + return MemBuf(WrapBufferOp::WRAP_BUFFER, buf, length, capacity); } MemBuf::MemBuf() noexcept {} @@ -862,4 +876,22 @@ void MemBuf::initExtBuffer(uint8_t* buf, size_t mallocSize, *infoReturn = sharedInfo; } +bool MemBuf::ensureCapacity(std::size_t capacity) { + return !isChained() && std::size_t((bufferEnd() - data())) >= capacity; +} + +bool MemBuf::ensureCapacityAndFillUnused(std::size_t capacity, + uint8_t placeholder) { + auto ret = ensureCapacity(capacity); + if (!ret) { + return ret; + } + + if (length() < capacity) { + std::memset(writableTail(), placeholder, capacity - length()); + } + + return ret; +} + } // namespace utils \ No newline at end of file diff --git a/libtransport/src/utils/memory_pool_allocator.h b/libtransport/src/utils/memory_pool_allocator.h index adc1443ad..a960b91bb 100644 --- a/libtransport/src/utils/memory_pool_allocator.h +++ b/libtransport/src/utils/memory_pool_allocator.h @@ -149,4 +149,4 @@ class Allocator : private MemoryPool { void destroy(pointer p) { p->~T(); } }; -} \ No newline at end of file +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/utils/min_filter.h b/libtransport/src/utils/min_filter.h index dcfd5652d..f1aaea7a8 100644 --- a/libtransport/src/utils/min_filter.h +++ b/libtransport/src/utils/min_filter.h @@ -43,6 +43,11 @@ class MinFilter { by_arrival_.push_front(by_order_.insert(std::forward(value))); } + TRANSPORT_ALWAYS_INLINE void clear() { + by_arrival_.clear(); + by_order_.clear(); + } + TRANSPORT_ALWAYS_INLINE const T& begin() { return *by_order_.cbegin(); } TRANSPORT_ALWAYS_INLINE const T& rBegin() { return *by_order_.crbegin(); } diff --git a/scripts/build-packages.sh b/scripts/build-packages.sh index f3e3ff678..781dbc9bd 100644 --- a/scripts/build-packages.sh +++ b/scripts/build-packages.sh @@ -31,20 +31,12 @@ function build_package() { mkdir -p ${SCRIPT_PATH}/../build && pushd ${SCRIPT_PATH}/../build rm -rf * - # First round - Without libmemif - cmake -G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_APPS=ON .. - ninja -j8 package - - # Second round - With Libmemif - rm -rf libtransport ctrl/libhicnctrl - cmake -G Ninja -DCMAKE_INSTALL_PREFIX=/usr \ - -DBUILD_HICNPLUGIN=ON \ - -DBUILD_LIBTRANSPORT=ON \ - -DBUILD_APPS=ON \ - -DBUILD_HICNLIGHT=OFF \ - -DBUILD_SYSREPOPLUGIN=OFF \ - -DBUILD_TELEMETRY=ON \ - -DBUILD_WSPLUGIN=ON \ + cmake -G Ninja -DCMAKE_INSTALL_PREFIX=/usr \ + -DBUILD_HICNPLUGIN=ON \ + -DBUILD_LIBTRANSPORT=ON \ + -DBUILD_APPS=ON \ + -DBUILD_HICNLIGHT=ON \ + -DBUILD_TELEMETRY=ON \ ${SCRIPT_PATH}/.. ninja -j8 package diff --git a/scripts/functions.sh b/scripts/functions.sh index 26c993f6d..9cb62b5cc 100644 --- a/scripts/functions.sh +++ b/scripts/functions.sh @@ -49,6 +49,7 @@ DEPS_UBUNTU=("build-essential" "libmemif" "libasio-dev" "libconfig-dev" + "libconfig++-dev" "libcurl4-openssl-dev" "libevent-dev" "libssl-dev" @@ -58,11 +59,7 @@ DEPS_UBUNTU=("build-essential" "libvppinfra=${VPP_VERSION_DEB}" "libvppinfra-dev=${VPP_VERSION_DEB}" "vpp-plugin-core=${VPP_VERSION_DEB}" - "python3-ply" - "wireshark" - "wireshark-dev" - "libgcrypt-dev" - "libgnutls28-dev") + "python3-ply") # BUILD_TOOLS_GROUP_CENTOS="'Development Tools'" DEPS_CENTOS=("vpp-devel-${VPP_VERSION_RPM}" @@ -77,9 +74,7 @@ DEPS_CENTOS=("vpp-devel-${VPP_VERSION_RPM}" "libconfig-devel" "dnf-plugins-core" "bzip2" - "rpm-build" - "wireshark-devel" - "libgcrypt-devel") + "rpm-build") COLLECTD_SOURCE="https://github.com/collectd/collectd/releases/download/collectd-5.12.0/collectd-5.12.0.tar.bz2" @@ -119,19 +114,12 @@ function setup_fdio_repo() { fi } -function setup_wireshark_repo() { - if [ ${DISTRIB_ID} == "ubuntu" ]; then - sudo add-apt-repository ppa:wireshark-dev/stable - sudo add-apt-repository universe - fi -} - # Install dependencies function install_deps() { DISTRIB_ID=${ID} if [ ${DISTRIB_ID} == "ubuntu" ]; then - echo ${DEPS_UBUNTU[@]} | xargs sudo DEBIAN_FRONTEND=noninteractive ${apt_get} install -y --allow-unauthenticated --no-install-recommends + echo ${DEPS_UBUNTU[@]} | xargs sudo ${apt_get} install -y --allow-unauthenticated --no-install-recommends elif [ ${DISTRIB_ID} == "centos" ]; then yum config-manager --set-enabled powertools # Temporary workaround until centos fixes the asio-devel package (https://forums.centos.org/viewtopic.php?t=73034) @@ -158,8 +146,7 @@ function setup() { # export variables depending on the platform we are running call_once setup_fdio_repo - call_once setup_wireshark_repo call_once install_deps call_once install_cmake call_once install_collectd_headers -} \ No newline at end of file +} diff --git a/utils/.clang-format b/utils/.clang-format new file mode 100644 index 000000000..cd21e2017 --- /dev/null +++ b/utils/.clang-format @@ -0,0 +1,14 @@ +# Copyright (c) 2017-2021 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +BasedOnStyle: Google diff --git a/utils/src/hiperf.cc b/utils/src/hiperf.cc index f4764e096..203c2acb9 100644 --- a/utils/src/hiperf.cc +++ b/utils/src/hiperf.cc @@ -16,19 +16,16 @@ #include #include #include -#include +#include +#include +#include #include #include -#include -#include +#include +#include #include #include -#ifdef SECURE_HICNTRANSPORT -#include -#include -#endif - #ifndef _WIN32 #include #endif @@ -37,6 +34,7 @@ #include #include #include +#include #include #ifdef __linux__ @@ -50,7 +48,6 @@ #endif namespace transport { - namespace interface { #ifndef ERROR_SUCCESS @@ -59,13 +56,50 @@ namespace interface { #define ERROR_SETUP -5 #define MIN_PROBE_SEQ 0xefffffff +struct packet_t { + uint64_t timestamp; + uint32_t size; +}; + +inline uint64_t _ntohll(const uint64_t *input) { + uint64_t return_val; + uint8_t *tmp = (uint8_t *)&return_val; + + tmp[0] = *input >> 56; + tmp[1] = *input >> 48; + tmp[2] = *input >> 40; + tmp[3] = *input >> 32; + tmp[4] = *input >> 24; + tmp[5] = *input >> 16; + tmp[6] = *input >> 8; + tmp[7] = *input >> 0; + + return return_val; +} + +inline uint64_t _htonll(const uint64_t *input) { return (_ntohll(input)); } + +struct nack_packet_t { + uint64_t timestamp; + uint32_t prod_rate; + uint32_t prod_seg; + + inline uint64_t getTimestamp() const { return _ntohll(×tamp); } + inline void setTimestamp(uint64_t time) { timestamp = _htonll(&time); } + + inline uint32_t getProductionRate() const { return ntohl(prod_rate); } + inline void setProductionRate(uint32_t rate) { prod_rate = htonl(rate); } + + inline uint32_t getProductionSegement() const { return ntohl(prod_seg); } + inline void setProductionSegement(uint32_t seg) { prod_seg = htonl(seg); } +}; + /** * Container for command line configuration for hiperf client. */ struct ClientConfiguration { ClientConfiguration() : name("b001::abcd", 0), - verify(false), beta(-1.f), drop_factor(-1.f), window(-1), @@ -78,15 +112,11 @@ struct ClientConfiguration { transport_protocol_(CBR), rtc_(false), test_mode_(false), -#ifdef SECURE_HICNTRANSPORT secure_(false), -#endif producer_prefix_(), - interest_lifetime_(500) { - } + interest_lifetime_(500) {} Name name; - bool verify; double beta; double drop_factor; double window; @@ -99,9 +129,7 @@ struct ClientConfiguration { TransportProtocolAlgorithms transport_protocol_; bool rtc_; bool test_mode_; -#ifdef SECURE_HICNTRANSPORT bool secure_; -#endif Prefix producer_prefix_; uint32_t interest_lifetime_; }; @@ -156,21 +184,19 @@ struct ServerConfiguration { sign(false), content_lifetime(600000000_U32), download_size(20 * 1024 * 1024), - hash_algorithm(utils::CryptoHashType::SHA_256), + hash_algorithm(auth::CryptoHashType::SHA_256), keystore_name(""), passphrase(""), keystore_password("cisco"), multiphase_produce_(false), rtc_(false), interactive_(false), + trace_based_(false), + trace_index_(0), + trace_file_(nullptr), production_rate_(std::string("2048kbps")), - payload_size_(1400) -#ifdef SECURE_HICNTRANSPORT - , - secure_(false) -#endif - { - } + payload_size_(1400), + secure_(false) {} Prefix name; bool virtual_producer; @@ -179,18 +205,20 @@ struct ServerConfiguration { bool sign; std::uint32_t content_lifetime; std::uint32_t download_size; - utils::CryptoHashType hash_algorithm; + auth::CryptoHashType hash_algorithm; std::string keystore_name; std::string passphrase; std::string keystore_password; bool multiphase_produce_; bool rtc_; bool interactive_; + bool trace_based_; + std::uint32_t trace_index_; + char *trace_file_; Rate production_rate_; std::size_t payload_size_; -#ifdef SECURE_HICNTRANSPORT bool secure_; -#endif + std::vector trace_; }; /** @@ -217,18 +245,26 @@ class HIperfClient { : configuration_(conf), total_duration_milliseconds_(0), old_bytes_value_(0), + old_interest_tx_value_(0), + old_fec_interest_tx_value_(0), + old_fec_data_rx_value_(0), + old_lost_data_value_(0), + old_bytes_recovered_value_(0), + old_retx_value_(0), + old_sent_int_value_(0), + old_received_nacks_value_(0), + avg_data_delay_(0), + delay_sample_(0), + received_bytes_(0), + received_data_pkt_(0), signals_(io_service_), expected_seg_(0), lost_packets_(std::unordered_set()), - rtc_callback_(configuration_.rtc_ ? new RTCCallback(*this) : nullptr), - callback_(configuration_.rtc_ ? nullptr : new Callback(*this)), - key_callback_(configuration_.rtc_ ? nullptr : new KeyCallback(*this)) {} - - ~HIperfClient() { - delete callback_; - delete key_callback_; - delete rtc_callback_; - } + rtc_callback_(*this), + callback_(*this), + key_callback_(*this) {} + + ~HIperfClient() {} void checkReceivedRtcContent(ConsumerSocket &c, const ContentObject &contentObject) { @@ -237,11 +273,15 @@ class HIperfClient { uint32_t receivedSeg = contentObject.getName().getSuffix(); auto payload = contentObject.getPayload(); - if ((uint32_t)payload->length() == 8) { // 8 is the size of the NACK - // payload - uint32_t *payloadPtr = (uint32_t *)payload->data(); - uint32_t productionSeg = *(payloadPtr); - uint32_t productionRate = *(++payloadPtr); + if ((uint32_t)payload->length() == 16) { // 16 is the size of the NACK + struct nack_packet_t *nack_pkt = + (struct nack_packet_t *)contentObject.getPayload()->data(); + uint32_t productionSeg = nack_pkt->getProductionSegement(); + uint32_t productionRate = nack_pkt->getProductionRate(); + + // uint32_t *payloadPtr = (uint32_t *)payload->data(); + // uint32_t productionSeg = *(payloadPtr); + // uint32_t productionRate = *(++payloadPtr); if (productionRate == 0) { std::cout << "[STOP] producer is not producing content" << std::endl; @@ -254,7 +294,7 @@ class HIperfClient { << std::endl; expected_seg_ = productionSeg; } else if (receivedSeg > productionSeg && receivedSeg < MIN_PROBE_SEQ) { - std::cout << "[WINDOW TO LARGE] received NACK for " << receivedSeg + std::cout << "[WINDOW TOO LARGE] received NACK for " << receivedSeg << ". Next expected packet " << productionSeg << std::endl; } else if (receivedSeg >= MIN_PROBE_SEQ) { std::cout << "[PROBE] probe number = " << receivedSeg << std::endl; @@ -262,7 +302,24 @@ class HIperfClient { return; } - if (receivedSeg > expected_seg_) { + received_bytes_ += (payload->length() - 12); + received_data_pkt_++; + + // collecting delay stats. Just for performance testing + // XXX we should probably get the transport header (12) somewhere + uint64_t *senderTimeStamp = (uint64_t *)(payload->data() + 12); + uint64_t now = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + double new_delay = (double)(now - *senderTimeStamp); + if (*senderTimeStamp > now) + new_delay = -1 * (double)(*senderTimeStamp - now); + + delay_sample_++; + avg_data_delay_ = + avg_data_delay_ + (new_delay - avg_data_delay_) / delay_sample_; + + if (receivedSeg > expected_seg_ && expected_seg_ != 0) { for (uint32_t i = expected_seg_; i < receivedSeg; i++) { std::cout << "[LOSS] lost packet " << i << std::endl; lost_packets_.insert(i); @@ -283,24 +340,12 @@ class HIperfClient { expected_seg_ = receivedSeg + 1; } - bool verifyData(ConsumerSocket &c, const ContentObject &contentObject) { - if (contentObject.getPayloadType() == PayloadType::CONTENT_OBJECT) { - std::cout << "VERIFY CONTENT" << std::endl; - } else if (contentObject.getPayloadType() == PayloadType::MANIFEST) { - std::cout << "VERIFY MANIFEST" << std::endl; - } - - return true; - } - void processLeavingInterest(ConsumerSocket &c, const Interest &interest) {} void handleTimerExpiration(ConsumerSocket &c, const TransportStatistics &stats) { - if (configuration_.rtc_) return; - const char separator = ' '; - const int width = 20; + const int width = 15; utils::TimePoint t2 = utils::SteadyClock::now(); auto exact_duration = @@ -323,28 +368,125 @@ class HIperfClient { std::stringstream window; window << stats.getAverageWindowSize() << std::setfill(separator) - << "[Interest]"; + << "[Int]"; std::stringstream avg_rtt; - avg_rtt << stats.getAverageRtt() << std::setfill(separator) << "[us]"; - - std::cout << std::left << std::setw(width) << "Interval"; - std::cout << std::left << std::setw(width) << "Transfer"; - std::cout << std::left << std::setw(width) << "Bandwidth"; - std::cout << std::left << std::setw(width) << "Retr"; - std::cout << std::left << std::setw(width) << "Cwnd"; - std::cout << std::left << std::setw(width) << "AvgRtt" << std::endl; - - std::cout << std::left << std::setw(width) << interval.str(); - std::cout << std::left << std::setw(width) << bytes_transferred.str(); - std::cout << std::left << std::setw(width) << bandwidth.str(); - std::cout << std::left << std::setw(width) << stats.getRetxCount(); - std::cout << std::left << std::setw(width) << window.str(); - std::cout << std::left << std::setw(width) << avg_rtt.str() << std::endl; - std::cout << std::endl; + avg_rtt << stats.getAverageRtt() << std::setfill(separator) << "[ms]"; + if (configuration_.rtc_) { + // we get rtc stats more often, thus we need ms in the interval + std::stringstream interval_ms; + interval_ms << total_duration_milliseconds_ << "-" + << total_duration_milliseconds_ + exact_duration.count(); + + std::stringstream lost_data; + lost_data << stats.getLostData() - old_lost_data_value_ + << std::setfill(separator) << "[pkt]"; + + std::stringstream bytes_recovered_data; + bytes_recovered_data << stats.getBytesRecoveredData() - + old_bytes_recovered_value_ + << std::setfill(separator) << "[pkt]"; + + std::stringstream data_delay; + data_delay << avg_data_delay_ << std::setfill(separator) << "[ms]"; + + std::stringstream received_data_pkt; + received_data_pkt << received_data_pkt_ << std::setfill(separator) + << "[pkt]"; + + std::stringstream goodput; + goodput << (received_bytes_ * 8.0) / (exact_duration.count()) / 1000.0 + << std::setfill(separator) << "[Mbps]"; + + std::stringstream loss_rate; + loss_rate << std::fixed << std::setprecision(2) + << stats.getLossRatio() * 100.0 << std::setfill(separator) + << "[%]"; + + std::stringstream retx_sent; + retx_sent << stats.getRetxCount() - old_retx_value_ + << std::setfill(separator) << "[pkt]"; + + std::stringstream interest_sent; + interest_sent << stats.getInterestTx() - old_sent_int_value_ + << std::setfill(separator) << "[pkt]"; + + std::stringstream nacks; + nacks << stats.getReceivedNacks() - old_received_nacks_value_ + << std::setfill(separator) << "[pkt]"; + + // statistics not yet available in the transport + // std::stringstream interest_fec_tx; + // interest_fec_tx << stats.getInterestFecTxCount() - + // old_fec_interest_tx_value_ << std::setfill(separator) << "[pkt]"; + // std::stringstream bytes_fec_recv; + // bytes_fec_recv << stats.getBytesFecRecv() - old_fec_data_rx_value_ + // << std::setfill(separator) << "[bytes]"; + std::cout << std::left << std::setw(width) << "Interval"; + std::cout << std::left << std::setw(width) << "RecvData"; + std::cout << std::left << std::setw(width) << "Bandwidth"; + std::cout << std::left << std::setw(width) << "Goodput"; + std::cout << std::left << std::setw(width) << "LossRate"; + std::cout << std::left << std::setw(width) << "Retr"; + std::cout << std::left << std::setw(width) << "InterestSent"; + std::cout << std::left << std::setw(width) << "ReceivedNacks"; + std::cout << std::left << std::setw(width) << "SyncWnd"; + std::cout << std::left << std::setw(width) << "MinRtt"; + std::cout << std::left << std::setw(width) << "LostData"; + std::cout << std::left << std::setw(width) << "RecoveredData"; + std::cout << std::left << std::setw(width) << "State"; + std::cout << std::left << std::setw(width) << "DataDelay" << std::endl; + + std::cout << std::left << std::setw(width) << interval_ms.str(); + std::cout << std::left << std::setw(width) << received_data_pkt.str(); + std::cout << std::left << std::setw(width) << bandwidth.str(); + std::cout << std::left << std::setw(width) << goodput.str(); + std::cout << std::left << std::setw(width) << loss_rate.str(); + std::cout << std::left << std::setw(width) << retx_sent.str(); + std::cout << std::left << std::setw(width) << interest_sent.str(); + std::cout << std::left << std::setw(width) << nacks.str(); + std::cout << std::left << std::setw(width) << window.str(); + std::cout << std::left << std::setw(width) << avg_rtt.str(); + std::cout << std::left << std::setw(width) << lost_data.str(); + std::cout << std::left << std::setw(width) << bytes_recovered_data.str(); + std::cout << std::left << std::setw(width) << stats.getCCStatus(); + std::cout << std::left << std::setw(width) << data_delay.str(); + std::cout << std::endl; + + // statistics not yet available in the transport + // std::cout << std::left << std::setw(width) << interest_fec_tx.str(); + // std::cout << std::left << std::setw(width) << bytes_fec_recv.str(); + } else { + std::cout << std::left << std::setw(width) << "Interval"; + std::cout << std::left << std::setw(width) << "Transfer"; + std::cout << std::left << std::setw(width) << "Bandwidth"; + std::cout << std::left << std::setw(width) << "Retr"; + std::cout << std::left << std::setw(width) << "Cwnd"; + std::cout << std::left << std::setw(width) << "AvgRtt" << std::endl; + + std::cout << std::left << std::setw(width) << interval.str(); + std::cout << std::left << std::setw(width) << bytes_transferred.str(); + std::cout << std::left << std::setw(width) << bandwidth.str(); + std::cout << std::left << std::setw(width) << stats.getRetxCount(); + std::cout << std::left << std::setw(width) << window.str(); + std::cout << std::left << std::setw(width) << avg_rtt.str() << std::endl; + std::cout << std::endl; + } total_duration_milliseconds_ += (uint32_t)exact_duration.count(); old_bytes_value_ = stats.getBytesRecv(); + old_lost_data_value_ = stats.getLostData(); + old_bytes_recovered_value_ = stats.getBytesRecoveredData(); + old_fec_interest_tx_value_ = stats.getInterestFecTxCount(); + old_fec_data_rx_value_ = stats.getBytesFecRecv(); + old_retx_value_ = stats.getRetxCount(); + old_sent_int_value_ = stats.getInterestTx(); + old_received_nacks_value_ = stats.getReceivedNacks(); + delay_sample_ = 0; + avg_data_delay_ = 0; + received_bytes_ = 0; + received_data_pkt_ = 0; + t_stats_ = utils::SteadyClock::now(); } @@ -359,7 +501,6 @@ class HIperfClient { configuration_.transport_protocol_ = CBR; } -#ifdef SECURE_HICNTRANSPORT if (configuration_.secure_) { consumer_socket_ = std::make_shared( RAAQM, configuration_.transport_protocol_); @@ -373,12 +514,9 @@ class HIperfClient { secure_consumer_socket.registerPrefix(configuration_.producer_prefix_); } } else { -#endif consumer_socket_ = std::make_shared(configuration_.transport_protocol_); -#ifdef SECURE_HICNTRANSPORT } -#endif consumer_socket_->setSocketOption( GeneralTransportOptions::INTEREST_LIFETIME, @@ -421,34 +559,21 @@ class HIperfClient { } } - if (configuration_.verify) { - std::shared_ptr verifier = - std::make_shared(); - PARCKeyId *key_id_; - - if (!configuration_.producer_certificate.empty()) { - key_id_ = verifier->addKeyFromCertificate( - configuration_.producer_certificate); - if (key_id_ == nullptr) return ERROR_SETUP; - } - - if (!configuration_.passphrase.empty()) { - key_id_ = verifier->addKeyFromPassphrase( - configuration_.passphrase, utils::CryptoSuite::HMAC_SHA256); - if (key_id_ == nullptr) return ERROR_SETUP; - } - + if (!configuration_.producer_certificate.empty()) { + std::shared_ptr verifier = + std::make_shared( + configuration_.producer_certificate); if (consumer_socket_->setSocketOption(GeneralTransportOptions::VERIFIER, - verifier) == - SOCKET_OPTION_NOT_SET) { + verifier) == SOCKET_OPTION_NOT_SET) return ERROR_SETUP; - } } - if (consumer_socket_->setSocketOption( - GeneralTransportOptions::VERIFY_SIGNATURE, configuration_.verify) == - SOCKET_OPTION_NOT_SET) { - return ERROR_SETUP; + if (!configuration_.passphrase.empty()) { + std::shared_ptr verifier = + std::make_shared(configuration_.passphrase); + if (consumer_socket_->setSocketOption(GeneralTransportOptions::VERIFIER, + verifier) == SOCKET_OPTION_NOT_SET) + return ERROR_SETUP; } ret = consumer_socket_->setSocketOption( @@ -462,16 +587,11 @@ class HIperfClient { } if (!configuration_.rtc_) { - /* key_callback_->setConsumer(consumer_socket_); */ - /* consumer_socket_->setSocketOption(ConsumerCallbacksOptions::READ_CALLBACK, - * key_callback_); */ - /* consumer_socket_->setSocketOption(GeneralTransportOptions::KEY_CONTENT, - * true); */ ret = consumer_socket_->setSocketOption( - ConsumerCallbacksOptions::READ_CALLBACK, callback_); + ConsumerCallbacksOptions::READ_CALLBACK, &callback_); } else { ret = consumer_socket_->setSocketOption( - ConsumerCallbacksOptions::READ_CALLBACK, rtc_callback_); + ConsumerCallbacksOptions::READ_CALLBACK, &rtc_callback_); } if (ret == SOCKET_OPTION_NOT_SET) { @@ -489,6 +609,13 @@ class HIperfClient { } } + if (configuration_.rtc_) { + std::shared_ptr transport_stats; + consumer_socket_->getSocketOption( + OtherOptions::STATISTICS, (TransportStatistics **)&transport_stats); + transport_stats->setAlpha(0.0); + } + ret = consumer_socket_->setSocketOption( ConsumerCallbacksOptions::STATS_SUMMARY, (ConsumerTimerCallback)std::bind(&HIperfClient::handleTimerExpiration, @@ -515,15 +642,15 @@ class HIperfClient { std::cout << "Starting download of " << configuration_.name << std::endl; signals_.add(SIGINT); - signals_.async_wait([this](const std::error_code &, const int &) { - consumer_socket_->stop(); - io_service_.stop(); - }); + signals_.async_wait( + [this](const std::error_code &, const int &) { io_service_.stop(); }); t_download_ = t_stats_ = std::chrono::steady_clock::now(); consumer_socket_->asyncConsume(configuration_.name); io_service_.run(); + consumer_socket_->stop(); + return ERROR_SUCCESS; } @@ -648,36 +775,6 @@ class HIperfClient { std::cout << "Key size: " << total_size << " bytes" << std::endl; } - void readKey() { - std::shared_ptr verifier = - std::make_shared(); - verifier->addKeyFromPassphrase(*key_, utils::CryptoSuite::HMAC_SHA256); - - if (consumer_socket_) { - consumer_socket_->setSocketOption(GeneralTransportOptions::KEY_CONTENT, - false); - consumer_socket_->setSocketOption(GeneralTransportOptions::VERIFIER, - verifier); - } else { - std::cout << "Consumer socket not set" << std::endl; - return; - } - - if (validateKey()) { - std::cout << "Key has been authenticated" << std::endl; - } else { - std::cout << "Key could not be authenticated" << std::endl; - return; - } - - if (consumer_socket_->verifyKeyPackets()) { - std::cout << "Signatures of key packets are valid" << std::endl; - } else { - std::cout << "Signatures of key packets are not valid" << std::endl; - return; - } - } - void setConsumer(std::shared_ptr consumer_socket) { consumer_socket_ = consumer_socket; } @@ -693,14 +790,31 @@ class HIperfClient { Time t_download_; uint32_t total_duration_milliseconds_; uint64_t old_bytes_value_; + uint64_t old_interest_tx_value_; + uint64_t old_fec_interest_tx_value_; + uint64_t old_fec_data_rx_value_; + uint64_t old_lost_data_value_; + uint64_t old_bytes_recovered_value_; + uint32_t old_retx_value_; + uint32_t old_sent_int_value_; + uint32_t old_received_nacks_value_; + + // IMPORTANT: to be used only for performance testing, when consumer and + // producer are synchronized. Used for rtc only at the moment + double avg_data_delay_; + uint32_t delay_sample_; + + uint32_t received_bytes_; + uint32_t received_data_pkt_; + asio::io_service io_service_; asio::signal_set signals_; - std::shared_ptr consumer_socket_; uint32_t expected_seg_; std::unordered_set lost_packets_; - RTCCallback *rtc_callback_; - Callback *callback_; - KeyCallback *key_callback_; + RTCCallback rtc_callback_; + Callback callback_; + KeyCallback key_callback_; + std::shared_ptr consumer_socket_; }; // namespace interface /** @@ -723,11 +837,11 @@ class HIperfServer { #ifndef _WIN32 ptr_last_segment_(&last_segment_), input_(io_service_), - rtc_running_(false) + rtc_running_(false), #else - ptr_last_segment_(&last_segment_) + ptr_last_segment_(&last_segment_), #endif - { + flow_name_(configuration_.name.getName()) { std::string buffer(configuration_.payload_size_, 'X'); std::cout << "Producing contents under name " << conf.name.getName() << std::endl; @@ -738,9 +852,9 @@ class HIperfServer { #endif for (int i = 0; i < (1 << log2_content_object_buffer_size); i++) { - content_objects_[i] = std::make_shared( - conf.name.getName(), HF_INET6_TCP, (const uint8_t *)buffer.data(), - buffer.size()); + content_objects_[i] = ContentObject::Ptr( + new ContentObject(conf.name.getName(), HF_INET6_TCP, 0, + (const uint8_t *)buffer.data(), buffer.size())); content_objects_[i]->setLifetime( default_values::content_object_expiry_time); } @@ -799,15 +913,16 @@ class HIperfServer { produceContentAsync(p, interest.getName(), suffix); } - void produceContent(ProducerSocket &p, Name content_name, uint32_t suffix) { + void produceContent(ProducerSocket &p, const Name &content_name, + uint32_t suffix) { auto b = utils::MemBuf::create(configuration_.download_size); std::memset(b->writableData(), '?', configuration_.download_size); b->append(configuration_.download_size); uint32_t total; utils::TimePoint t0 = utils::SteadyClock::now(); - total = p.produce(content_name, std::move(b), - !configuration_.multiphase_produce_, suffix); + total = p.produceStream(content_name, std::move(b), + !configuration_.multiphase_produce_, suffix); utils::TimePoint t1 = utils::SteadyClock::now(); std::cout @@ -822,11 +937,6 @@ class HIperfServer { auto b = utils::MemBuf::create(configuration_.download_size); std::memset(b->writableData(), '?', configuration_.download_size); b->append(configuration_.download_size); - /* std::string passphrase = "hunter2"; */ - /* auto b = utils::MemBuf::create(passphrase.length() + 1); */ - /* std::memcpy(b->writableData(), passphrase.c_str(), passphrase.length() + - * 1); */ - /* b->append(passphrase.length() + 1); */ p.asyncProduce(content_name, std::move(b), !configuration_.multiphase_produce_, suffix, @@ -845,23 +955,22 @@ class HIperfServer { std::placeholders::_1, std::placeholders::_2)); } - std::shared_ptr getProducerIdentity( - std::string &keystore_name, std::string &keystore_password, - utils::CryptoHashType &hash_algorithm) { - if (access(keystore_name.c_str(), F_OK) != -1) { - return std::make_shared(keystore_name, keystore_password, - hash_algorithm); - } else { - return std::make_shared(keystore_name, keystore_password, - utils::CryptoSuite::RSA_SHA256, - 1024, 365, "producer-test"); + std::shared_ptr getProducerIdentity( + std::string &keystore_path, std::string &keystore_pwd, + auth::CryptoHashType &hash_type) { + if (access(keystore_path.c_str(), F_OK) != -1) { + return std::make_shared(keystore_path, keystore_pwd, + hash_type); } + return std::make_shared(keystore_path, keystore_pwd, + auth::CryptoSuite::RSA_SHA256, 1024, + 365, "producer-test"); } int setup() { int ret; + int production_protocol; -#ifdef SECURE_HICNTRANSPORT if (configuration_.secure_) { auto identity = getProducerIdentity(configuration_.keystore_name, configuration_.keystore_password, @@ -869,22 +978,21 @@ class HIperfServer { producer_socket_ = std::make_unique( configuration_.rtc_, identity); } else { -#endif - if (configuration_.rtc_) { - producer_socket_ = std::make_unique(); + if (!configuration_.rtc_) { + production_protocol = ProductionProtocolAlgorithms::BYTE_STREAM; } else { - producer_socket_ = std::make_unique(); + production_protocol = ProductionProtocolAlgorithms::RTC_PROD; } -#ifdef SECURE_HICNTRANSPORT + + producer_socket_ = std::make_unique(production_protocol); } -#endif if (configuration_.sign) { - std::shared_ptr signer; + std::shared_ptr signer; if (!configuration_.passphrase.empty()) { - signer = std::make_shared( - configuration_.passphrase, utils::CryptoSuite::HMAC_SHA256); + signer = std::make_shared( + auth::CryptoSuite::HMAC_SHA256, configuration_.passphrase); } else if (!configuration_.keystore_name.empty()) { auto identity = getProducerIdentity(configuration_.keystore_name, configuration_.keystore_password, @@ -901,8 +1009,7 @@ class HIperfServer { } uint32_t rtc_header_size = 0; - if(configuration_.rtc_) - rtc_header_size = 8; + if (configuration_.rtc_) rtc_header_size = 12; producer_socket_->setSocketOption( GeneralTransportOptions::DATA_PACKET_SIZE, (uint32_t)( @@ -987,8 +1094,61 @@ class HIperfServer { this, std::placeholders::_1)); auto payload = content_objects_[content_objects_index_++ & mask_]->getPayload(); - producer_socket_->produce( - payload->data(), payload->length() < 1400 ? payload->length() : 1400); + + // this is used to compute the data packet delay + // Used only for performance evaluation + // It requires clock synchronization between producer and consumer + uint64_t now = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + std::memcpy(payload->writableData(), &now, sizeof(uint64_t)); + + producer_socket_->produceDatagram( + flow_name_, payload->data(), + payload->length() < 1400 ? payload->length() : 1400); + } + + void sendRTCContentObjectCallbackWithTrace(std::error_code ec) { + if (ec) return; + + auto payload = + content_objects_[content_objects_index_++ & mask_]->getPayload(); + + uint32_t packet_len = + configuration_.trace_[configuration_.trace_index_].size; + + // this is used to compute the data packet delay + // used only for performance evaluation + // it requires clock synchronization between producer and consumer + uint64_t now = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + std::memcpy(payload->writableData(), &now, sizeof(uint64_t)); + + if (packet_len > payload->length()) packet_len = payload->length(); + if (packet_len > 1400) packet_len = 1400; + + producer_socket_->produceDatagram(flow_name_, payload->data(), packet_len); + + uint32_t next_index = configuration_.trace_index_ + 1; + uint64_t schedule_next; + if (next_index < configuration_.trace_.size()) { + schedule_next = + configuration_.trace_[next_index].timestamp - + configuration_.trace_[configuration_.trace_index_].timestamp; + } else { + // here we need to loop, schedule in a random time + schedule_next = 1000; + } + + configuration_.trace_index_ = + (configuration_.trace_index_ + 1) % configuration_.trace_.size(); + rtc_timer_.expires_from_now(std::chrono::microseconds(schedule_next)); + rtc_timer_.async_wait( + std::bind(&HIperfServer::sendRTCContentObjectCallbackWithTrace, this, + std::placeholders::_1)); } #ifndef _WIN32 @@ -1021,6 +1181,21 @@ class HIperfServer { } #endif + int parseTraceFile() { + std::ifstream trace(configuration_.trace_file_); + if (trace.fail()) { + return -1; + } + std::string line; + while (std::getline(trace, line)) { + std::istringstream iss(line); + struct packet_t packet; + iss >> packet.timestamp >> packet.size; + configuration_.trace_.push_back(packet); + } + return 0; + } + int run() { std::cerr << "Starting to serve consumers" << std::endl; @@ -1038,6 +1213,21 @@ class HIperfServer { input_, input_buffer_, '\n', std::bind(&HIperfServer::handleInput, this, std::placeholders::_1, std::placeholders::_2)); + } else if (configuration_.trace_based_) { + std::cout << "trace-based mode enabled" << std::endl; + if (configuration_.trace_file_ == nullptr) { + std::cout << "cannot find the trace file" << std::endl; + return ERROR_SETUP; + } + if (parseTraceFile() < 0) { + std::cout << "cannot parse the trace file" << std::endl; + return ERROR_SETUP; + } + rtc_running_ = true; + rtc_timer_.expires_from_now(std::chrono::milliseconds(1)); + rtc_timer_.async_wait( + std::bind(&HIperfServer::sendRTCContentObjectCallbackWithTrace, + this, std::placeholders::_1)); } else { rtc_running_ = true; rtc_timer_.expires_from_now( @@ -1078,6 +1268,7 @@ class HIperfServer { asio::posix::stream_descriptor input_; asio::streambuf input_buffer_; bool rtc_running_; + core::Name flow_name_; #endif }; // namespace interface @@ -1095,6 +1286,8 @@ void usage() { << "Run RTC protocol (client or server)" << std::endl; std::cerr << "-f\t\t\t\t" << "Log file" << std::endl; + std::cerr << "-z\t\t\t\t" + << "IO module to use. Default: hicnlight_module" << std::endl; #endif std::cerr << std::endl; std::cerr << "SERVER SPECIFIC:" << std::endl; @@ -1140,13 +1333,18 @@ void usage() { "Interactive mode, start/stop real time content production " "by pressing return. To be used with the -R option" << std::endl; -#ifdef SECURE_HICNTRANSPORT + std::cerr + << "-T\t\t\t\t" + "Trace based mode, hiperf takes as input a file with a trace. " + "Each line of the file indicates the timestamp and the size of " + "the packet to generate. To be used with the -R option. -B and -I " + "will be ignored." + << std::endl; std::cerr << "-E\t\t\t\t\t" << "Enable encrypted communication. Requires the path to a p12 " "file containing the " "crypto material used for the TLS handshake" << std::endl; -#endif #endif std::cerr << std::endl; std::cerr << "CLIENT SPECIFIC:" << std::endl; @@ -1169,26 +1367,21 @@ void usage() { std::cerr << "-i\t\t\t" << "Show the statistics every milliseconds." << std::endl; - std::cerr << "-v\t\t\t\t\t" - << "Enable verification of received data" << std::endl; std::cerr << "-c\t\t\t" << "Path of the producer certificate to be used for verifying the " - "origin of the packets received. Must be used with -v." + "origin of the packets received." << std::endl; std::cerr << "-k\t\t\t\t" << "String from which is derived the symmetric key used by the " - "producer to sign packets and by the consumer to verify them. " - "Must be used with -v." + "producer to sign packets and by the consumer to verify them." << std::endl; std::cerr << "-t\t\t\t\t\t" "Test mode, check if the client is receiving the " "correct data. This is an RTC specific option, to be " "used with the -R (default false)" << std::endl; -#ifdef SECURE_HICNTRANSPORT std::cerr << "-P\t\t\t\t\t" << "Prefix of the producer where to do the handshake" << std::endl; -#endif } int main(int argc, char *argv[]) { @@ -1205,6 +1398,9 @@ int main(int argc, char *argv[]) { int options = 0; char *log_file = nullptr; + interface::global_config::IoModuleConfiguration config; + std::string conf_file; + config.name = "hicnlight_module"; // Consumer ClientConfiguration client_configuration; @@ -1214,9 +1410,9 @@ int main(int argc, char *argv[]) { int opt; #ifndef _WIN32 - while ((opt = getopt(argc, argv, - "DSCf:b:d:W:RM:c:vA:s:rmlK:k:y:p:hi:xE:P:B:ItL:")) != - -1) { + while ((opt = getopt( + argc, argv, + "DSCf:b:d:W:RM:c:vA:s:rmlK:k:y:p:hi:xE:P:B:ItL:z:T:F:")) != -1) { switch (opt) { // Common case 'D': { @@ -1225,11 +1421,19 @@ int main(int argc, char *argv[]) { } case 'I': { server_configuration.interactive_ = true; + server_configuration.trace_based_ = false; + break; + } + case 'T': { + server_configuration.interactive_ = false; + server_configuration.trace_based_ = true; + server_configuration.trace_file_ = optarg; break; } #else while ((opt = getopt(argc, argv, - "SCf:b:d:W:RM:c:vA:s:rmlK:k:y:p:hi:xB:E:P:tL:")) != -1) { + "SCf:b:d:W:RM:c:vA:s:rmlK:k:y:p:hi:xB:E:P:tL:z:F:")) != + -1) { switch (opt) { #endif case 'f': { @@ -1241,6 +1445,14 @@ int main(int argc, char *argv[]) { server_configuration.rtc_ = true; break; } + case 'z': { + config.name = optarg; + break; + } + case 'F': { + conf_file = optarg; + break; + } // Server or Client case 'S': { @@ -1255,7 +1467,6 @@ int main(int argc, char *argv[]) { server_configuration.passphrase = std::string(optarg); client_configuration.passphrase = std::string(optarg); server_configuration.sign = true; - options = -1; break; } @@ -1280,23 +1491,16 @@ int main(int argc, char *argv[]) { options = 1; break; } -#ifdef SECURE_HICNTRANSPORT case 'P': { client_configuration.producer_prefix_ = Prefix(optarg); client_configuration.secure_ = true; break; } -#endif case 'c': { client_configuration.producer_certificate = std::string(optarg); options = 1; break; } - case 'v': { - client_configuration.verify = true; - options = 1; - break; - } case 'i': { client_configuration.report_interval_milliseconds_ = std::stoul(optarg); options = 1; @@ -1346,11 +1550,11 @@ int main(int argc, char *argv[]) { } case 'y': { if (strncasecmp(optarg, "sha256", 6) == 0) { - server_configuration.hash_algorithm = utils::CryptoHashType::SHA_256; + server_configuration.hash_algorithm = auth::CryptoHashType::SHA_256; } else if (strncasecmp(optarg, "sha512", 6) == 0) { - server_configuration.hash_algorithm = utils::CryptoHashType::SHA_512; + server_configuration.hash_algorithm = auth::CryptoHashType::SHA_512; } else if (strncasecmp(optarg, "crc32", 5) == 0) { - server_configuration.hash_algorithm = utils::CryptoHashType::CRC32C; + server_configuration.hash_algorithm = auth::CryptoHashType::CRC32C; } else { std::cerr << "Ignored unknown hash algorithm. Using SHA 256." << std::endl; @@ -1375,13 +1579,11 @@ int main(int argc, char *argv[]) { options = -1; break; } -#ifdef SECURE_HICNTRANSPORT case 'E': { server_configuration.keystore_name = std::string(optarg); server_configuration.secure_ = true; break; } -#endif case 'h': default: usage(); @@ -1395,7 +1597,6 @@ int main(int argc, char *argv[]) { << std::endl; usage(); return EXIT_FAILURE; - } else if (options < 0 && role > 0) { std::cerr << "Server options cannot be used when using the " "software in client mode" @@ -1443,6 +1644,14 @@ int main(int argc, char *argv[]) { } #endif + /** + * IO module configuration + */ + config.set(); + + // Parse config file + transport::interface::global_config::parseConfigurationFile(conf_file); + if (role > 0) { HIperfClient c(client_configuration); if (c.setup() != ERROR_SETUP) { @@ -1461,11 +1670,11 @@ int main(int argc, char *argv[]) { #ifdef _WIN32 WSACleanup(); #endif + return 0; } } // end namespace interface - } // end namespace transport int main(int argc, char *argv[]) { diff --git a/utils/src/ping_client.cc b/utils/src/ping_client.cc index 67440cdc1..e7a9228f2 100644 --- a/utils/src/ping_client.cc +++ b/utils/src/ping_client.cc @@ -13,9 +13,10 @@ * limitations under the License. */ +#include #include #include -#include +#include #include // Let's make the linker happy @@ -40,7 +41,7 @@ namespace core { namespace ping { typedef std::map SendTimeMap; -typedef utils::Verifier Verifier; +typedef auth::AsymmetricVerifier Verifier; class Configuration { public: @@ -104,7 +105,7 @@ class Client : interface::Portal::ConsumerCallback { received_ = 0; timedout_ = 0; if (!c->certificate_.empty()) { - key_id_ = verifier_.addKeyFromCertificate(c->certificate_); + verifier_.setCertificate(c->certificate_); } } @@ -116,13 +117,12 @@ class Client : interface::Portal::ConsumerCallback { portal_.runEventsLoop(); } - void onContentObject(Interest::Ptr &&interest, - ContentObject::Ptr &&object) override { + void onContentObject(Interest &interest, ContentObject &object) override { uint64_t rtt = 0; if (!config_->certificate_.empty()) { auto t0 = std::chrono::steady_clock::now(); - if (verifier_.verify(*object)) { + if (verifier_.verifyPacket(&object)) { auto t1 = std::chrono::steady_clock::now(); auto dt = std::chrono::duration_cast(t1 - t0); @@ -133,7 +133,7 @@ class Client : interface::Portal::ConsumerCallback { } } - auto it = send_timestamps_.find(interest->getName().getSuffix()); + auto it = send_timestamps_.find(interest.getName().getSuffix()); if (it != send_timestamps_.end()) { rtt = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()) @@ -144,40 +144,40 @@ class Client : interface::Portal::ConsumerCallback { if (config_->verbose_) { std::cout << "<<< recevied object. " << std::endl; - std::cout << "<<< interest name: " << interest->getName() - << " src port: " << interest->getSrcPort() - << " dst port: " << interest->getDstPort() - << " flags: " << interest->printFlags() << std::endl; - std::cout << "<<< object name: " << object->getName() - << " src port: " << object->getSrcPort() - << " dst port: " << object->getDstPort() - << " flags: " << object->printFlags() << " path label " - << object->getPathLabel() << " (" - << (object->getPathLabel() >> 24) << ")" - << " TTL: " << (int)object->getTTL() << std::endl; + std::cout << "<<< interest name: " << interest.getName() + << " src port: " << interest.getSrcPort() + << " dst port: " << interest.getDstPort() + << " flags: " << interest.printFlags() << std::endl; + std::cout << "<<< object name: " << object.getName() + << " src port: " << object.getSrcPort() + << " dst port: " << object.getDstPort() + << " flags: " << object.printFlags() << " path label " + << object.getPathLabel() << " (" + << (object.getPathLabel() >> 24) << ")" + << " TTL: " << (int)object.getTTL() << std::endl; } else if (!config_->quiet_) { std::cout << "<<< received object. " << std::endl; std::cout << "<<< round trip: " << rtt << " [us]" << std::endl; - std::cout << "<<< interest name: " << interest->getName() << std::endl; - std::cout << "<<< object name: " << object->getName() << std::endl; + std::cout << "<<< interest name: " << interest.getName() << std::endl; + std::cout << "<<< object name: " << object.getName() << std::endl; std::cout << "<<< content object size: " - << object->payloadSize() + object->headerSize() << " [bytes]" + << object.payloadSize() + object.headerSize() << " [bytes]" << std::endl; } if (config_->dump_) { std::cout << "----- interest dump -----" << std::endl; - interest->dump(); + interest.dump(); std::cout << "-------------------------" << std::endl; std::cout << "----- object dump -------" << std::endl; - object->dump(); + object.dump(); std::cout << "-------------------------" << std::endl; } if (!config_->quiet_) std::cout << std::endl; if (!config_->always_syn_) { - if (object->testSyn() && object->testAck() && state_ == SYN_STATE) { + if (object.testSyn() && object.testAck() && state_ == SYN_STATE) { state_ = ACK_STATE; } } @@ -217,7 +217,7 @@ class Client : interface::Portal::ConsumerCallback { void onError(std::error_code ec) override {} void doPing() { - Name interest_name(config_->name_, (uint32_t)sequence_number_); + const Name interest_name(config_->name_, (uint32_t)sequence_number_); hicn_format_t format; if (interest_name.getAddressFamily() == AF_INET) { format = HF_INET_TCP; @@ -225,11 +225,9 @@ class Client : interface::Portal::ConsumerCallback { format = HF_INET6_TCP; } - Interest::Ptr interest(new Interest(std::move(interest_name), format), - nullptr); + auto interest = std::make_shared(interest_name, format); interest->setLifetime(uint32_t(config_->interestLifetime_)); - interest->resetFlags(); if (config_->open_ || config_->always_syn_) { @@ -313,7 +311,6 @@ class Client : interface::Portal::ConsumerCallback { std::unique_ptr timer_; Configuration *config_; Verifier verifier_; - PARCKeyId *key_id_; }; void help() { diff --git a/utils/src/ping_server.cc b/utils/src/ping_server.cc index b1a6e1509..79c662231 100644 --- a/utils/src/ping_server.cc +++ b/utils/src/ping_server.cc @@ -23,11 +23,10 @@ #include #include -#include -#include +#include +#include #include - #include namespace transport { @@ -35,16 +34,16 @@ namespace transport { namespace interface { using HashAlgorithm = core::HashAlgorithm; -using CryptoSuite = utils::CryptoSuite; +using CryptoSuite = auth::CryptoSuite; -utils::Identity setProducerIdentity(std::string keystore_name, - std::string keystore_password, - utils::CryptoHashType hash_algorithm) { +auth::Identity setProducerIdentity(std::string keystore_name, + std::string keystore_password, + auth::CryptoHashType hash_algorithm) { if (access(keystore_name.c_str(), F_OK) != -1) { - return utils::Identity(keystore_name, keystore_password, hash_algorithm); + return auth::Identity(keystore_name, keystore_password, hash_algorithm); } else { - return utils::Identity(keystore_name, keystore_password, - CryptoSuite::RSA_SHA256, 1024, 365, "producer-test"); + return auth::Identity(keystore_name, keystore_password, + CryptoSuite::RSA_SHA256, 1024, 365, "producer-test"); } } @@ -54,7 +53,7 @@ class CallbackContainer { public: CallbackContainer(const Name &prefix, uint32_t object_size, bool verbose, bool dump, bool quite, bool flags, bool reset, uint8_t ttl, - utils::Identity *identity, bool sign, uint32_t lifetime) + auth::Identity *identity, bool sign, uint32_t lifetime) : buffer_(object_size, 'X'), content_objects_((std::uint32_t)(1 << log2_content_object_buffer_size)), mask_((std::uint16_t)(1 << log2_content_object_buffer_size) - 1), @@ -83,7 +82,7 @@ class CallbackContainer { for (int i = 0; i < (1 << log2_content_object_buffer_size); i++) { content_objects_[i] = std::make_shared( - prefix, format, (const uint8_t *)buffer_.data(), buffer_.size()); + prefix, format, 0, (const uint8_t *)buffer_.data(), buffer_.size()); content_objects_[i]->setLifetime(lifetime); } } @@ -153,7 +152,7 @@ class CallbackContainer { if (!quite_) std::cout << std::endl; if (sign_) { - identity_->getSigner()->sign(*content_object); + identity_->getSigner()->signPacket(content_object.get()); } p.produce(*content_object); @@ -171,7 +170,7 @@ class CallbackContainer { bool flags_; bool reset_; uint8_t ttl_; - utils::Identity *identity_; + auth::Identity *identity_; bool sign_; }; @@ -291,14 +290,14 @@ int main(int argc, char **argv) { if (object_size > 1350) object_size = 1350; CallbackContainer *stubs; - utils::Identity identity = setProducerIdentity( - keystore_path, keystore_password, utils::CryptoHashType::SHA_256); + auth::Identity identity = setProducerIdentity( + keystore_path, keystore_password, auth::CryptoHashType::SHA_256); if (sign) { stubs = new CallbackContainer(n, object_size, verbose, dump, quite, flags, reset, ttl, &identity, sign, data_lifetime); } else { - utils::Identity *identity = nullptr; + auth::Identity *identity = nullptr; stubs = new CallbackContainer(n, object_size, verbose, dump, quite, flags, reset, ttl, identity, sign, data_lifetime); } -- cgit 1.2.3-korg