diff options
Diffstat (limited to 'libtransport/src/test')
-rw-r--r-- | libtransport/src/test/CMakeLists.txt | 13 | ||||
-rw-r--r-- | libtransport/src/test/fec_reed_solomon.cc | 154 | ||||
-rw-r--r-- | libtransport/src/test/fec_rely.cc | 156 | ||||
-rw-r--r-- | libtransport/src/test/packet_samples.h | 58 | ||||
-rw-r--r-- | libtransport/src/test/test_auth.cc | 110 | ||||
-rw-r--r-- | libtransport/src/test/test_consumer_producer_rtc.cc | 176 | ||||
-rw-r--r-- | libtransport/src/test/test_core_manifest.cc | 44 | ||||
-rw-r--r-- | libtransport/src/test/test_event_thread.cc | 106 | ||||
-rw-r--r-- | libtransport/src/test/test_fec_reedsolomon.cc | 291 | ||||
-rw-r--r-- | libtransport/src/test/test_interest.cc | 267 | ||||
-rw-r--r-- | libtransport/src/test/test_packet.cc | 1047 |
11 files changed, 2405 insertions, 17 deletions
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 <gtest/gtest.h> +#include <hicn/transport/interfaces/socket_consumer.h> +#include <hicn/transport/interfaces/socket_options_keys.h> +#include <hicn/transport/interfaces/socket_producer.h> +#include <hicn/transport/interfaces/global_conf_interface.h> + +#include <asio/io_service.hpp> +#include <asio/steady_timer.hpp> +#include <fec/rs.h> + +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 <gtest/gtest.h> +#include <hicn/transport/interfaces/socket_consumer.h> +#include <hicn/transport/interfaces/socket_options_keys.h> +#include <hicn/transport/interfaces/socket_producer.h> +#include <hicn/transport/interfaces/global_conf_interface.h> + +#include <asio/io_service.hpp> +#include <asio/steady_timer.hpp> + +#include <rely/encoder.hpp> +#include <rely/decoder.hpp> + +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 <gtest/gtest.h> +#include <hicn/transport/auth/crypto_hash_type.h> +#include <hicn/transport/auth/identity.h> +#include <hicn/transport/auth/signer.h> +#include <hicn/transport/auth/verifier.h> +#include <hicn/transport/core/content_object.h> + +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> verifier = std::make_shared<VoidVerifier>(); + 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> 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> verifier = + std::make_shared<AsymmetricVerifier>(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> signer = + std::make_shared<SymmetricSigner>(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> verifier = + std::make_shared<SymmetricVerifier>(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 <gtest/gtest.h> +#include <hicn/transport/interfaces/global_conf_interface.h> +#include <hicn/transport/interfaces/socket_consumer.h> +#include <hicn/transport/interfaces/socket_options_keys.h> +#include <hicn/transport/interfaces/socket_producer.h> + +#include <asio/io_service.hpp> +#include <asio/steady_timer.hpp> + +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 <core/manifest_format_fixed.h> #include <core/manifest_inline.h> #include <gtest/gtest.h> -#include <hicn/transport/security/crypto_hash_type.h> +#include <hicn/transport/auth/crypto_hash_type.h> +#include <test/packet_samples.h> #include <climits> #include <random> @@ -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<uint64_t> idis( 0, std::numeric_limits<uint32_t>::max()); - auto entries = new std::pair<uint32_t, utils::CryptoHash>[3]; + auto entries = new std::pair<uint32_t, auth::CryptoHash>[3]; uint32_t suffixes[3]; std::vector<unsigned char> 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<uint8_t>().data(), - // entries[i].second.getType()); + // cond = auth::CryptoHash::compareBinaryDigest(hash, + // entries[i].second.getDigest<uint8_t>().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 <gtest/gtest.h> +#include <hicn/transport/utils/event_thread.h> + +#include <cmath> + +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<unsigned long> samples(size); + + for (unsigned int i = 0; i < size; i++) { + auto t0 = steady_clock::now(); + event_thread_.add([t0, &samples, i]() { + auto t1 = steady_clock::now(); + samples[i] = duration_cast<nanoseconds>(t1 - t0).count(); + }); + } + + 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 <core/rs.h> +#include <gtest/gtest.h> +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/core/global_object_pool.h> + +#include <algorithm> +#include <iostream> +#include <random> + +namespace transport { +namespace core { + +double ReedSolomonTest(int k, int n, int size) { + fec::encoder encoder(k, n); + fec::decoder decoder(k, n); + + std::vector<fec::buffer> tx_block(k); + std::vector<fec::buffer> rx_block(k); + int count = 0; + int run = 0; + + int iterations = 100; + auto &packet_manager = PacketManager<>::getInstance(); + + encoder.setFECCallback([&tx_block](std::vector<fec::buffer> &repair_packets) { + for (auto &p : repair_packets) { + // Append repair symbols to tx_block + tx_block.emplace_back(std::move(p)); + } + }); + + decoder.setFECCallback([&](std::vector<fec::buffer> &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<bool> losses(n, false); + for (int i = 0; i < n - k; i++) losses[i] = true; + + int rxi = 0; + std::shuffle(losses.begin(), losses.end(), + std::default_random_engine(seed)); + 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<std::pair<fec::buffer, uint32_t>> tx_block; + std::vector<std::pair<fec::buffer, uint32_t>> 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<fec::buffer> &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<fec::buffer> &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 <gtest/gtest.h> +#include <hicn/transport/core/interest.h> +#include <hicn/transport/errors/not_implemented_exception.h> +#include <test/packet_samples.h> + +#include <climits> +#include <random> +#include <vector> + +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<uint8_t> 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<uint8_t> 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 <gtest/gtest.h> +#include <hicn/transport/core/packet.h> +#include <hicn/transport/errors/not_implemented_exception.h> +#include <test/packet_samples.h> + +#include <climits> +#include <random> +#include <vector> + +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 <typename... Args> + PacketForTest(Args &&... args) : Packet(std::forward<Args>(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<Packet::Format, std::vector<uint8_t>> raw_packets_; + + std::vector<uint8_t> 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<Packet::Format, std::vector<uint8_t>> 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<uint8_t> 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<uint8_t> &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<uint8_t> &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<uint8_t> &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<PacketForTest>( + 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<milliseconds>(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(); +} |