From bac3da61644515f05663789b122554dc77549286 Mon Sep 17 00:00:00 2001 From: Luca Muscariello Date: Thu, 17 Jan 2019 13:47:57 +0100 Subject: This is the first commit of the hicn project Change-Id: I6f2544ad9b9f8891c88cc4bcce3cf19bd3cc863f Signed-off-by: Luca Muscariello --- utils/CMakeLists.txt | 54 ++++ utils/src/hiperf.cc | 724 +++++++++++++++++++++++++++++++++++++++++++++++ utils/src/ping_client.cc | 428 ++++++++++++++++++++++++++++ utils/src/ping_server.cc | 300 ++++++++++++++++++++ 4 files changed, 1506 insertions(+) create mode 100755 utils/CMakeLists.txt create mode 100755 utils/src/hiperf.cc create mode 100755 utils/src/ping_client.cc create mode 100755 utils/src/ping_server.cc (limited to 'utils') diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt new file mode 100755 index 000000000..95fdd508d --- /dev/null +++ b/utils/CMakeLists.txt @@ -0,0 +1,54 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) +set(CMAKE_CXX_STANDARD 14) + +project(Utils) + +set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Modules" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules" +) + +include(BuildMacros) +include(Packager) + +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + find_package(Libtransport REQUIRED) +else() + # TODO Set name of targets in CMakeroot file + set(LIBTRANSPORT_LIBRARIES ${LIBTRANSPORT}) +endif() + +set (COMPILER_DEFINITIONS "-DASIO_STANDALONE") + +list(APPEND UTILS_SRC + src/hiperf.cc + src/ping_client.cc + src/ping_server.cc +) + +foreach(util ${UTILS_SRC}) + get_filename_component(_util_name ${util} NAME) + string(REGEX REPLACE ".cc" "" util_name ${_util_name}) + + build_executable(${util_name} + SOURCES ${util} + LINK_LIBRARIES ${LIBTRANSPORT_LIBRARIES} + DEPENDS transport + COMPONENT utils + DEFINITIONS ${COMPILER_DEFINITIONS} + ) +endforeach() \ No newline at end of file diff --git a/utils/src/hiperf.cc b/utils/src/hiperf.cc new file mode 100755 index 000000000..d8953b36a --- /dev/null +++ b/utils/src/hiperf.cc @@ -0,0 +1,724 @@ +/* + * 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 + +#ifdef __linux__ +#include +#endif + +namespace transport { + +namespace interface { + +#define ERROR_SUCCESS 0 +#define ERROR_SETUP -5 + +using CryptoSuite = utils::CryptoSuite; +using Identity = utils::Identity; + +struct ClientConfiguration { + ClientConfiguration() + : name("b001::abcd", 0), + verify(false), + beta(-1.f), + drop_factor(-1.f), + window(-1), + virtual_download(true), + producer_certificate("/tmp/rsa_certificate.pem"), + receive_buffer(std::make_shared>()), + download_size(0), + report_interval_milliseconds_(1000), + rtc_(false) {} + + Name name; + bool verify; + double beta; + double drop_factor; + double window; + bool virtual_download; + std::string producer_certificate; + std::shared_ptr> receive_buffer; + std::size_t download_size; + std::uint32_t report_interval_milliseconds_; + TransportProtocolAlgorithms transport_protocol_; + bool rtc_; +}; + +struct ServerConfiguration { + ServerConfiguration() + : name("b001::abcd/64"), + virtual_producer(true), + manifest(false), + live_production(false), + sign(false), + content_lifetime(600000000_U32), + content_object_size(1440), + download_size(20 * 1024 * 1024), + hash_algorithm(HashAlgorithm::SHA_256), + keystore_name("/tmp/rsa_crypto_material.p12"), + keystore_password("cisco"), + multiphase_produce_(false) {} + + Prefix name; + bool virtual_producer; + bool manifest; + bool live_production; + bool sign; + std::uint32_t content_lifetime; + std::uint16_t content_object_size; + std::uint32_t download_size; + HashAlgorithm hash_algorithm; + std::string keystore_name; + std::string keystore_password; + bool multiphase_produce_; +}; + +class HIperfClient { + typedef std::chrono::time_point Time; + typedef std::chrono::microseconds TimeDuration; + + public: + HIperfClient(const ClientConfiguration &conf) + : configuration_(conf), + total_duration_milliseconds_(0), + old_bytes_value_(0) {} + + void processPayload(ConsumerSocket &c, std::size_t bytes_transferred, + const std::error_code &ec) { + Time t2 = std::chrono::steady_clock::now(); + TimeDuration dt = std::chrono::duration_cast(t2 - t1_); + long usec = dt.count(); + + std::cout << "Content retrieved. Size: " << bytes_transferred << " [Bytes]" + << std::endl; + + std::cerr << "Elapsed Time: " << usec / 1000000.0 << " seconds -- " + << (bytes_transferred * 8) * 1.0 / usec * 1.0 << " [Mbps]" + << std::endl; + } + + 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) { + // std::cout << "LEAVES " << interest.getName().toUri() << std::endl; + } + + void handleTimerExpiration(ConsumerSocket &c, std::size_t byte_count, + std::chrono::milliseconds &exact_duration, + float c_window, uint32_t retransmissions, + uint32_t average_rtt) { + const char separator = ' '; + const int width = 20; + + std::stringstream interval; + interval << total_duration_milliseconds_ / 1000 << "-" + << total_duration_milliseconds_ / 1000 + + exact_duration.count() / 1000; + + std::stringstream bytes_transferred; + bytes_transferred << std::fixed << std::setprecision(3) + << (byte_count - old_bytes_value_) / 1000000.0 + << std::setfill(separator) << "[MBytes]"; + + std::stringstream bandwidth; + bandwidth << ((byte_count - old_bytes_value_) * 8) / + (exact_duration.count()) / 1000.0 + << std::setfill(separator) << "[Mbps]"; + + std::stringstream window; + window << c_window << std::setfill(separator) << "[Interest]"; + + std::stringstream avg_rtt; + avg_rtt << average_rtt << 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) << retransmissions; + 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_ += exact_duration.count(); + old_bytes_value_ = byte_count; + } + + int setup() { + int ret; + + // Set the transport algorithm + TransportProtocolAlgorithms transport_protocol; + + if (configuration_.rtc_) { + transport_protocol = RTC; + } else if (configuration_.window < 0) { + transport_protocol = RAAQM; + } else { + transport_protocol = CBR; + } + + consumer_socket_ = std::make_unique(transport_protocol); + +#if defined(DEBUG) && defined(__linux__) + std::shared_ptr portal; + consumer_socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal); + signals_ = + std::make_unique(portal->getIoService(), SIGUSR1); + signals_->async_wait([this](const std::error_code &, const int &) { + std::cout << "Signal SIGUSR1!" << std::endl; + mtrace(); + }); +#endif + + if (consumer_socket_->setSocketOption(CURRENT_WINDOW_SIZE, + configuration_.window) == + SOCKET_OPTION_NOT_SET) { + std::cerr << "ERROR -- Impossible to set the size of the window." + << std::endl; + return ERROR_SETUP; + } + + if (transport_protocol == RAAQM && configuration_.beta != -1.f) { + if (consumer_socket_->setSocketOption(RaaqmTransportOptions::BETA_VALUE, + configuration_.beta) == + SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + } + + if (transport_protocol == RAAQM && configuration_.drop_factor != -1.f) { + if (consumer_socket_->setSocketOption(RaaqmTransportOptions::DROP_FACTOR, + configuration_.drop_factor) == + SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + } + + if (consumer_socket_->setSocketOption(OtherOptions::VIRTUAL_DOWNLOAD, + false) == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + if (configuration_.verify) { + if (consumer_socket_->setSocketOption( + GeneralTransportOptions::CERTIFICATE, + configuration_.producer_certificate) == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + } + + if (consumer_socket_->setSocketOption( + GeneralTransportOptions::VERIFY_SIGNATURE, configuration_.verify) == + SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + ret = consumer_socket_->setSocketOption( + ConsumerCallbacksOptions::INTEREST_OUTPUT, + (ConsumerInterestCallback)std::bind( + &HIperfClient::processLeavingInterest, this, std::placeholders::_1, + std::placeholders::_2)); + + if (ret == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + ret = consumer_socket_->setSocketOption( + ConsumerCallbacksOptions::CONTENT_RETRIEVED, + (ConsumerContentCallback)std::bind( + &HIperfClient::processPayload, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3)); + + if (ret == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + ret = consumer_socket_->setSocketOption( + ConsumerCallbacksOptions::TIMER_EXPIRES, + (ConsumerTimerCallback)std::bind( + &HIperfClient::handleTimerExpiration, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, + std::placeholders::_5, std::placeholders::_6)); + + if (ret == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + if (consumer_socket_->setSocketOption( + GeneralTransportOptions::TIMER_INTERVAL, + configuration_.report_interval_milliseconds_) == + SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + consumer_socket_->connect(); + + return ERROR_SUCCESS; + } + + int run() { + std::cout << "Starting download of " << configuration_.name << std::endl; + + do { + t1_ = std::chrono::steady_clock::now(); + consumer_socket_->consume(configuration_.name, + *configuration_.receive_buffer); + } while (configuration_.virtual_download); + + return ERROR_SUCCESS; + } + + private: + ClientConfiguration configuration_; + std::unique_ptr consumer_socket_; + Time t1_; + uint32_t total_duration_milliseconds_; + uint64_t old_bytes_value_; + // std::unique_ptr signals_; +}; + +class HIperfServer { + const std::size_t log2_content_object_buffer_size = 8; + + public: + HIperfServer(ServerConfiguration &conf) + : configuration_(conf), + // signals_(io_service_, SIGINT, SIGQUIT), + content_objects_((1 << log2_content_object_buffer_size)), + content_objects_index_(0), + mask_((1 << log2_content_object_buffer_size) - 1) { + // signals_.async_wait([this] (const std::error_code&, const int&) + // {std::cout << "STOPPING!!" << std::endl; io_service_.stop();}); + + std::string buffer(1440, 'X'); + + std::cout << "Producing contents under name " << conf.name.getName() + << std::endl; + + 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]->setLifetime( + default_values::content_object_expiry_time); + } + } + + void processInterest(ProducerSocket &p, const Interest &interest) { + content_objects_[content_objects_index_ & mask_]->setName( + interest.getName()); + + // if (final_chunk_number_ > 0 && interest.getName().getSuffix() == 0) { + // auto name = interest.getName(); + // manifest_ = std::make_shared(name); + // // manifest_->setFinalChunkNumber(final_chunk_number_); + // manifest_->encode(); + // p.produce(*manifest_); + // return; + // } + + producer_socket_->produce( + *content_objects_[content_objects_index_++ & mask_]); + } + + void processInterest2(ProducerSocket &p, const Interest &interest) { + producer_socket_->setSocketOption(ProducerCallbacksOptions::CACHE_MISS, + (ProducerInterestCallback)VOID_HANDLER); + producer_socket_->setSocketOption( + GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME, 5000_U32); + produceContent(interest.getName().getSuffix()); + producer_socket_->setSocketOption( + ProducerCallbacksOptions::CACHE_MISS, + (ProducerInterestCallback)bind(&HIperfServer::processInterest2, this, + std::placeholders::_1, + std::placeholders::_2)); + } + + void produceContent(uint32_t suffix) { + core::Name name = configuration_.name.getName(); + + std::string content(configuration_.download_size, '?'); + uint32_t total; + + total = producer_socket_->produce( + name, reinterpret_cast(content.data()), content.size(), + !configuration_.multiphase_produce_); + + std::cout << "Written " << total << "pieces of data in output buffer" + << std::endl; + } + + utils::Identity setProducerIdentity(std::string &keystore_name, + std::string &keystore_password, + HashAlgorithm &hash_algorithm) { + if (access(keystore_name.c_str(), F_OK) != -1) { + return utils::Identity(keystore_name, keystore_password, hash_algorithm); + } else { + return utils::Identity(keystore_name, keystore_password, + CryptoSuite::RSA_SHA256, 1024, 365, + "producer-test"); + } + } + + int setup() { + int ret; + + producer_socket_ = std::make_unique(); + + if (configuration_.sign) { + Identity identity = setProducerIdentity(configuration_.keystore_name, + configuration_.keystore_password, + configuration_.hash_algorithm); + + if (producer_socket_->setSocketOption(GeneralTransportOptions::IDENTITY, + identity) == + SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + } + + producer_socket_->registerPrefix(configuration_.name); + + if (!configuration_.virtual_producer) { + if (producer_socket_->setSocketOption( + GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME, + configuration_.content_lifetime) == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + if (producer_socket_->setSocketOption( + GeneralTransportOptions::MAKE_MANIFEST, + configuration_.manifest) == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + if (producer_socket_->setSocketOption( + GeneralTransportOptions::OUTPUT_BUFFER_SIZE, 200000U) == + SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + if (!configuration_.live_production) { + produceContent(0); + } else { + ret = producer_socket_->setSocketOption( + ProducerCallbacksOptions::CACHE_MISS, + (ProducerInterestCallback)bind(&HIperfServer::processInterest2, + this, std::placeholders::_1, + std::placeholders::_2)); + + if (ret == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + } + } else { + ret = producer_socket_->setSocketOption( + GeneralTransportOptions::OUTPUT_BUFFER_SIZE, 0U); + + if (ret == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + ret = producer_socket_->setSocketOption( + ProducerCallbacksOptions::CACHE_MISS, + (ProducerInterestCallback)bind(&HIperfServer::processInterest, this, + std::placeholders::_1, + std::placeholders::_2)); + + if (ret == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + } + + producer_socket_->connect(); + + return ERROR_SUCCESS; + } + + int run() { + std::cerr << "Starting to serve consumers" << std::endl; + producer_socket_->serveForever(); + + return ERROR_SUCCESS; + } + + private: + ServerConfiguration configuration_; + std::unique_ptr producer_socket_; + // asio::signal_set signals_; + std::vector> content_objects_; + std::uint16_t content_objects_index_; + std::uint16_t mask_; +}; + +void usage() { + std::cerr << std::endl; + std::cerr << "HIPERF - A tool for performing network throughput " + "measurements with hICN" + << std::endl; + std::cerr << "usage: hiperf [-S|-C] [options] [prefix|name]" << std::endl; + std::cerr << "Server or Client:" << std::endl; + std::cerr << "-D\t\t\t\t\t" + << "Run as a daemon" << std::endl; + std::cerr << std::endl; + std::cerr << "Server specific:" << std::endl; + std::cerr << "-s\t\t\t\tSize of the content to publish" + << std::endl; + std::cerr << "-r\t\t\t\t\t" + << "Produce real content of content_size bytes" << std::endl; + std::cerr << "-m\t\t\t\t\t" + << "Produce transport manifest" << std::endl; + std::cerr << "-l\t\t\t\t\t" + << "Start producing content upon the reception of the " + "first interest" + << std::endl; + std::cerr << "-k\t\t\t\t" + << "Path of p12 file containing the " + "crypto material used for signing the packets" + << std::endl; + std::cerr << "-y\t\t\t" + << "Use the selected hash algorithm for " + "calculating manifest digests" + << std::endl; + std::cerr << "-p\t\t\t\t" + << "Password for p12 keystore" << std::endl; + std::cerr << std::endl; + std::cerr << "Client specific:" << std::endl; + std::cerr << "-b\t\t\t" + << "RAAQM beta parameter" << std::endl; + std::cerr << "-d\t\t\t" + << "RAAQM drop factor " + "parameter" + << std::endl; + std::cerr << "-W\t\t\t\t" + << "Use a fixed congestion window " + "for retrieving the 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" + << std::endl; + std::cout << "-v\t\t\t\t\t" + << "Enable verification of received data" << std::endl; +} + +int main(int argc, char *argv[]) { + // Common + bool daemon = false; + + // -1 server, 0 undefined, 1 client + int role = 0; + int options = 0; + + char *log_file = nullptr; + + // Consumer + ClientConfiguration client_configuration; + + // Producer + ServerConfiguration server_configuration; + + int opt; + while ((opt = getopt(argc, argv, "DSCf:b:d:W:c:vs:rmlk:y:p:hi:x")) != -1) { + switch (opt) { + // Common + case 'D': + daemon = true; + break; + case 'f': + log_file = optarg; + break; + + // Server or Client + case 'S': + role -= 1; + break; + case 'C': + role += 1; + break; + + // Client specifc + case 'b': + client_configuration.beta = std::stod(optarg); + options = 1; + break; + case 'd': + client_configuration.drop_factor = std::stod(optarg); + options = 1; + break; + case 'W': + client_configuration.window = std::stod(optarg); + options = 1; + break; + 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; + break; + case 'R': + client_configuration.rtc_ = true; + break; + + // Server specific + case 's': + server_configuration.download_size = std::stoul(optarg); + options = -1; + break; + case 'r': + server_configuration.virtual_producer = false; + options = -1; + break; + case 'm': + server_configuration.manifest = true; + options = -1; + break; + case 'l': + server_configuration.live_production = true; + options = -1; + break; + case 'k': + server_configuration.keystore_name = std::string(optarg); + server_configuration.sign = true; + options = -1; + break; + case 'y': + if (strncasecmp(optarg, "sha256", 6) == 0) { + server_configuration.hash_algorithm = HashAlgorithm::SHA_256; + } else if (strncasecmp(optarg, "sha512", 6) == 0) { + server_configuration.hash_algorithm = HashAlgorithm::SHA_512; + } else if (strncasecmp(optarg, "crc32", 5) == 0) { + server_configuration.hash_algorithm = HashAlgorithm::CRC32C; + } else { + std::cerr << "Ignored unknown hash algorithm. Using SHA 256." + << std::endl; + } + options = -1; + break; + case 'p': + server_configuration.keystore_password = std::string(optarg); + options = -1; + break; + case 'x': + server_configuration.multiphase_produce_ = true; + options = -1; + break; + case 'h': + default: + usage(); + return EXIT_FAILURE; + } + } + + if (options > 0 && role < 0) { + std::cerr << "Client options cannot be used when using the " + "software in server mode" + << 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" + << std::endl; + usage(); + return EXIT_FAILURE; + } else if (!role) { + std::cerr << "Please specify if running hiperf as client " + "or server." + << std::endl; + usage(); + return EXIT_FAILURE; + } + + if (argv[optind] == 0) { + std::cerr << "Please specify the name/prefix to use." << std::endl; + usage(); + return EXIT_FAILURE; + } else { + if (role > 0) { + client_configuration.name = Name(argv[optind]); + } else { + server_configuration.name = Prefix(argv[optind]); + } + } + + if (log_file) { + int fd = open(log_file, O_WRONLY | O_APPEND | O_CREAT, S_IWUSR | S_IRUSR); + dup2(fd, STDOUT_FILENO); + dup2(STDOUT_FILENO, STDERR_FILENO); + close(fd); + } + + if (daemon) { + utils::Daemonizator::daemonize(false); + } + + if (role > 0) { + HIperfClient c(client_configuration); + if (c.setup() != ERROR_SETUP) { + c.run(); + } + } else if (role < 0) { + HIperfServer s(server_configuration); + if (s.setup() != ERROR_SETUP) { + s.run(); + } + } else { + usage(); + return EXIT_FAILURE; + } + + std::cout << "Bye bye" << std::endl; + + return 0; +} + +} // end namespace interface + +} // end namespace transport + +int main(int argc, char *argv[]) { + return transport::interface::main(argc, argv); +} diff --git a/utils/src/ping_client.cc b/utils/src/ping_client.cc new file mode 100755 index 000000000..178bd8bac --- /dev/null +++ b/utils/src/ping_client.cc @@ -0,0 +1,428 @@ +/* + * 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 SYN_STATE 1 +#define ACK_STATE 2 + +namespace transport { + +namespace core { + +namespace ping { + +typedef std::map SendTimeMap; +typedef utils::Verifier Verifier; + +class Configuration { + public: + uint64_t interestLifetime_; + uint64_t pingInterval_; + uint64_t maxPing_; + uint64_t first_suffix_; + std::string name_; + std::string certificate_; + uint16_t srcPort_; + uint16_t dstPort_; + bool verbose_; + bool dump_; + bool jump_; + bool open_; + bool always_syn_; + bool always_ack_; + bool quiet_; + uint32_t jump_freq_; + uint32_t jump_size_; + uint8_t ttl_; + + Configuration() { + interestLifetime_ = 500; // ms + pingInterval_ = 1000000; // us + maxPing_ = 10; // number of interests + first_suffix_ = 0; + name_ = "b001::1"; // string + srcPort_ = 9695; + dstPort_ = 8080; + verbose_ = false; + dump_ = false; + jump_ = false; + open_ = false; + always_syn_ = false; + always_ack_ = false; + quiet_ = false; + jump_freq_ = 0; + jump_size_ = 0; + ttl_ = 64; + } +}; + +class Client : interface::BasePortal::ConsumerCallback { + public: + Client(Configuration *c) : portal_() { + // Let the main thread to catch SIGINT and SIGQUIT + // asio::signal_set signals(io_service, SIGINT, SIGQUIT); + // signals.async_wait(std::bind(&Client::afterSignal, this)); + + portal_.connect(); + portal_.setConsumerCallback(this); + timer_.reset(new asio::steady_timer(portal_.getIoService())); + config_ = c; + sequence_number_ = config_->first_suffix_; + last_jump_ = 0; + processed_ = 0; + state_ = SYN_STATE; + sent_ = 0; + received_ = 0; + timedout_ = 0; + if (!c->certificate_.empty()) { + key_id_ = verifier_.addKeyFromCertificate(c->certificate_); + } + } + + void ping() { + std::cout << "start ping" << std::endl; + doPing(); + portal_.runEventsLoop(); + } + + void onContentObject(Interest::Ptr &&interest, + ContentObject::Ptr &&object) override { + uint64_t rtt = 0; + + if (!config_->certificate_.empty()) { + auto t0 = std::chrono::steady_clock::now(); + if (verifier_.verify(*object)) { + auto t1 = std::chrono::steady_clock::now(); + auto dt = + std::chrono::duration_cast(t1 - t0); + std::cout << "Verification time: " << dt.count() << std::endl; + std::cout << "<<<<<< Signature OK!!!" << std::endl; + } else { + std::cout << "<<<<<< Signature verification failed!" << std::endl; + } + } + + 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()) + .count() - + it->second; + send_timestamps_.erase(it); + } + + 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; + } 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 << "<<< content object size: " + << object->payloadSize() + object->headerSize() << " [bytes]" + << std::endl; + } + + if (config_->dump_) { + std::cout << "----- interest dump -----" << std::endl; + interest->dump(); + std::cout << "-------------------------" << std::endl; + std::cout << "----- object dump -------" << std::endl; + 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) { + state_ = ACK_STATE; + } + } + + received_++; + processed_++; + if (processed_ >= config_->maxPing_) { + afterSignal(); + } + } + + void onTimeout(Interest::Ptr &&interest) override { + if (config_->verbose_) { + std::cout << "### timeout for " << interest->getName() + << " src port: " << interest->getSrcPort() + << " dst port: " << interest->getDstPort() + << " flags: " << interest->printFlags() << std::endl; + } else if (!config_->quiet_) { + std::cout << "### timeout for " << interest->getName() << std::endl; + } + + if (config_->dump_) { + std::cout << "----- interest dump -----" << std::endl; + interest->dump(); + std::cout << "-------------------------" << std::endl; + } + + if (!config_->quiet_) std::cout << std::endl; + + timedout_++; + processed_++; + if (processed_ >= config_->maxPing_) { + afterSignal(); + } + } + + void doPing() { + Name interest_name(config_->name_, sequence_number_); + hicn_format_t format; + if (interest_name.getAddressFamily() == AF_INET) { + format = HF_INET_TCP; + } else { + format = HF_INET6_TCP; + } + + Interest::Ptr interest(new Interest(std::move(interest_name), format), + nullptr); + + interest->setLifetime(uint32_t(config_->interestLifetime_)); + + interest->resetFlags(); + + if (config_->open_ || config_->always_syn_) { + if (state_ == SYN_STATE) { + interest->setSyn(); + } else if (state_ == ACK_STATE) { + interest->setAck(); + } + } else if (config_->always_ack_) { + interest->setAck(); + } + + interest->setSrcPort(config_->srcPort_); + interest->setDstPort(config_->dstPort_); + interest->setTTL(config_->ttl_); + + if (config_->verbose_) { + std::cout << ">>> send interest " << interest->getName() + << " src port: " << interest->getSrcPort() + << " dst port: " << interest->getDstPort() + << " flags: " << interest->printFlags() + << " TTL: " << (int)interest->getTTL() << std::endl; + } else if (!config_->quiet_) { + std::cout << ">>> send interest " << interest->getName() << std::endl; + } + + if (config_->dump_) { + std::cout << "----- interest dump -----" << std::endl; + interest->dump(); + std::cout << "-------------------------" << std::endl; + } + + if (!config_->quiet_) std::cout << std::endl; + + send_timestamps_[sequence_number_] = + std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + + portal_.sendInterest(std::move(interest)); + + sequence_number_++; + sent_++; + + if (sent_ < config_->maxPing_) { + this->timer_->expires_from_now( + std::chrono::microseconds(config_->pingInterval_)); + this->timer_->async_wait([this](const std::error_code e) { doPing(); }); + } + } + + void afterSignal() { + std::cout << "Stop ping" << std::endl; + std::cout << "Sent: " << sent_ << " Received: " << received_ + << " Timeouts: " << timedout_ << std::endl; + portal_.stopEventsLoop(); + } + + void reset() { + timer_.reset(new asio::steady_timer(portal_.getIoService())); + sequence_number_ = config_->first_suffix_; + last_jump_ = 0; + processed_ = 0; + state_ = SYN_STATE; + sent_ = 0; + received_ = 0; + timedout_ = 0; + } + + private: + SendTimeMap send_timestamps_; + interface::BasePortal portal_; + uint64_t sequence_number_; + uint64_t last_jump_; + uint64_t processed_; + uint32_t state_; + uint32_t sent_; + uint32_t received_; + uint32_t timedout_; + std::unique_ptr timer_; + Configuration *config_; + Verifier verifier_; + PARCKeyId *key_id_; +}; + +void help() { + std::cout << "usage: hicn-consumer-ping [options]" << std::endl; + std::cout << "PING options" << std::endl; + std::cout + << "-i ping interval in microseconds (default 1000000ms)" + << std::endl; + std::cout << "-m maximum number of pings to send (default 10)" + << std::endl; + std::cout << "-s sorce port (default 9695)" << std::endl; + std::cout << "-d destination port (default 8080)" << std::endl; + std::cout << "-t set packet ttl (default 64)" << std::endl; + std::cout << "-O open tcp connection (three way handshake) " + "(default false)" + << std::endl; + std::cout << "-S send always syn messages (default false)" + << std::endl; + std::cout << "-A send always ack messages (default false)" + << std::endl; + std::cout << "HICN options" << std::endl; + std::cout << "-n hicn name (default b001::1)" << std::endl; + std::cout + << "-l interest lifetime in milliseconds (default 500ms)" + << std::endl; + std::cout << "OUTPUT options" << std::endl; + std::cout << "-V verbose, prints statistics about the " + "messagges sent and received (default false)" + << std::endl; + std::cout << "-D dump, dumps sent and received packets " + "(default false)" + << std::endl; + std::cout << "-q quiet, not prints (default false)" + << std::endl; + std::cout << "-H prints this message" << std::endl; +} + +int main(int argc, char *argv[]) { + Configuration *c = new Configuration(); + int opt; + std::string producer_certificate = ""; + + while ((opt = getopt(argc, argv, "j::t:i:m:s:d:n:l:f:c:SAOqVDH")) != -1) { + switch (opt) { + case 't': + c->ttl_ = (uint8_t)std::stoi(optarg); + break; + case 'i': + c->pingInterval_ = std::stoi(optarg); + break; + case 'm': + c->maxPing_ = std::stoi(optarg); + break; + case 'f': + c->first_suffix_ = std::stoul(optarg); + break; + case 's': + c->srcPort_ = std::stoi(optarg); + break; + case 'd': + c->dstPort_ = std::stoi(optarg); + break; + case 'n': + c->name_ = optarg; + break; + case 'l': + c->interestLifetime_ = std::stoi(optarg); + break; + case 'V': + c->verbose_ = true; + ; + break; + case 'D': + c->dump_ = true; + break; + case 'O': + c->always_syn_ = false; + c->always_ack_ = false; + c->open_ = true; + break; + case 'S': + c->always_syn_ = true; + c->always_ack_ = false; + c->open_ = false; + break; + case 'A': + c->always_syn_ = false; + c->always_ack_ = true; + c->open_ = false; + break; + case 'q': + c->quiet_ = true; + c->verbose_ = false; + c->dump_ = false; + break; + case 'c': + c->certificate_ = std::string(optarg); + break; + case 'H': + default: + help(); + exit(EXIT_FAILURE); + } + } + + Client *ping = new Client(c); + + auto t0 = std::chrono::steady_clock::now(); + ping->ping(); + auto t1 = std::chrono::steady_clock::now(); + + std::cout + << "Elapsed time: " + << std::chrono::duration_cast(t1 - t0).count() + << std::endl; + + return 0; +} + +} // namespace ping + +} // namespace core + +} // namespace transport + +int main(int argc, char *argv[]) { + return transport::core::ping::main(argc, argv); +} diff --git a/utils/src/ping_server.cc b/utils/src/ping_server.cc new file mode 100755 index 000000000..19de34fec --- /dev/null +++ b/utils/src/ping_server.cc @@ -0,0 +1,300 @@ +/* + * 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 + +namespace transport { + +namespace interface { + +using HashAlgorithm = core::HashAlgorithm; +using CryptoSuite = utils::CryptoSuite; + +utils::Identity setProducerIdentity(std::string keystore_name, + std::string keystore_password, + HashAlgorithm hash_algorithm) { + if (access(keystore_name.c_str(), F_OK) != -1) { + return utils::Identity(keystore_name, keystore_password, hash_algorithm); + } else { + return utils::Identity(keystore_name, keystore_password, + CryptoSuite::RSA_SHA256, 1024, 365, "producer-test"); + } +} + +class CallbackContainer { + const std::size_t log2_content_object_buffer_size = 12; + + 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) + : buffer_(object_size, 'X'), + content_objects_(1 << log2_content_object_buffer_size), + mask_((1 << log2_content_object_buffer_size) - 1), + content_objects_index_(0), + verbose_(verbose), + dump_(dump), + quite_(quite), + flags_(flags), + reset_(reset), + ttl_(ttl), + identity_(identity), + sign_(sign) { + core::Packet::Format format; + + if (prefix.getAddressFamily() == AF_INET) { + format = core::Packet::Format::HF_INET_TCP; + if (sign_) { + format = core::Packet::Format::HF_INET_TCP_AH; + } + } else { + format = core::Packet::Format::HF_INET6_TCP; + if (sign_) { + format = core::Packet::Format::HF_INET6_TCP_AH; + } + } + + 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()); + content_objects_[i]->setLifetime( + default_values::content_object_expiry_time); + } + } + + void processInterest(ProducerSocket &p, const Interest &interest) { + if (verbose_) { + std::cout << "<<< received interest " << interest.getName() + << " src port: " << interest.getSrcPort() + << " dst port: " << interest.getDstPort() + << " flags: " << interest.printFlags() + << "TTL: " << (int)interest.getTTL() << std::endl; + } else if (!quite_) { + std::cout << "<<< received interest " << interest.getName() << std::endl; + } + + if (dump_) { + std::cout << "----- interest dump -----" << std::endl; + interest.dump(); + std::cout << "-------------------------" << std::endl; + } + + if (interest.testRst()) { + std::cout << "!!!got a reset, I don't reply" << std::endl; + } else { + auto &content_object = content_objects_[content_objects_index_++ & mask_]; + + content_object->setName(interest.getName()); + content_object->setLifetime(default_values::content_object_expiry_time); + content_object->setLocator(interest.getLocator()); + content_object->setSrcPort(interest.getDstPort()); + content_object->setDstPort(interest.getSrcPort()); + content_object->setTTL(ttl_); + + if (sign_) { + content_object->setSignatureSize(identity_->getSignatureLength()); + } else { + content_object->resetFlags(); + } + + if (flags_) { + if (interest.testSyn()) { + content_object->setSyn(); + content_object->setAck(); + } else if (interest.testAck()) { + content_object->setAck(); + } // here I may need to handle the FIN flag; + } else if (reset_) { + content_object->setRst(); + } + + if (verbose_) { + std::cout << ">>> send object " << content_object->getName() + << " src port: " << content_object->getSrcPort() + << " dst port: " << content_object->getDstPort() + << " flags: " << content_object->printFlags() + << " TTL: " << (int)content_object->getTTL() << std::endl; + } else if (!quite_) { + std::cout << ">>> send object " << content_object->getName() + << std::endl; + } + + if (dump_) { + std::cout << "----- object dump -----" << std::endl; + content_object->dump(); + std::cout << "-----------------------" << std::endl; + } + + if (!quite_) std::cout << std::endl; + + if (sign_) { + identity_->getSigner().sign(*content_object); + } + + p.produce(*content_object); + } + } + + private: + std::string buffer_; + std::vector> content_objects_; + std::uint16_t mask_; + std::uint16_t content_objects_index_; + bool verbose_; + bool dump_; + bool quite_; + bool flags_; + bool reset_; + uint8_t ttl_; + utils::Identity *identity_; + bool sign_; +}; + +void help() { + std::cout << "usage: hicn-preoducer-ping [options]" << std::endl; + std::cout << "PING options" << std::endl; + std::cout << "-s object content size (default 1350B)" << std::endl; + std::cout << "-n hicn name (default b001::/64)" << std::endl; + std::cout << "-f set tcp flags according to the flag received " + "(default false)" + << std::endl; + std::cout << "-r always reply with a reset flag (default false)" + << std::endl; + std::cout << "-t set ttl (default 64)" << std::endl; + std::cout << "OUTPUT options" << std::endl; + std::cout << "-V verbose, prints statistics about the messagges sent " + "and received (default false)" + << std::endl; + std::cout << "-D dump, dumps sent and received packets (default false)" + << std::endl; + std::cout << "-q quite, not prints (default false)" << std::endl; + std::cout << "-d daemon mode" << std::endl; + std::cout << "-H prints this message" << std::endl; +} + +int main(int argc, char **argv) { + std::string name_prefix = "b001::0/64"; + std::string delimiter = "/"; + bool daemon = false; + bool verbose = false; + bool dump = false; + bool quite = false; + bool flags = false; + bool reset = false; + uint32_t object_size = 1350; + uint8_t ttl = 64; + std::string keystore_path = "./rsa_crypto_material.p12"; + std::string keystore_password = "cisco"; + bool sign = false; + + int opt; + while ((opt = getopt(argc, argv, "s:n:t:qfrVDdHk:p:")) != -1) { + switch (opt) { + case 's': + object_size = std::stoi(optarg); + break; + case 'n': + name_prefix = optarg; + break; + case 't': + ttl = (uint8_t)std::stoi(optarg); + break; + case 'V': + verbose = true; + break; + case 'D': + dump = true; + break; + case 'q': + verbose = false; + dump = false; + quite = true; + break; + case 'd': + daemon = true; + break; + case 'f': + flags = true; + break; + case 'r': + reset = true; + break; + case 'k': + keystore_path = optarg; + sign = true; + break; + case 'p': + keystore_password = optarg; + break; + case 'H': + default: + help(); + exit(EXIT_FAILURE); + } + } + + if (daemon) { + utils::Daemonizator::daemonize(); + } + + core::Prefix producer_namespace(name_prefix); + + utils::StringTokenizer tokenizer(name_prefix, delimiter); + std::string ip_address = tokenizer.nextToken(); + Name n(ip_address); + + if (object_size > 1350) object_size = 1350; + + CallbackContainer *stubs; + utils::Identity identity = setProducerIdentity( + keystore_path, keystore_password, HashAlgorithm::SHA_256); + + if (sign) { + stubs = new CallbackContainer(n, object_size, verbose, dump, quite, flags, + reset, ttl, &identity, sign); + } else { + utils::Identity *identity = nullptr; + stubs = new CallbackContainer(n, object_size, verbose, dump, quite, flags, + reset, ttl, identity, sign); + } + + asio::io_service io_service; + + ProducerSocket p(io_service); // , setProducerIdentity()); + p.registerPrefix(producer_namespace); + + p.setSocketOption(GeneralTransportOptions::OUTPUT_BUFFER_SIZE, 0U); + p.setSocketOption(ProducerCallbacksOptions::CACHE_MISS, + (ProducerInterestCallback)bind( + &CallbackContainer::processInterest, stubs, + std::placeholders::_1, std::placeholders::_2)); + + p.connect(); + + p.serveForever(); + + return 0; +} + +} // namespace interface + +} // end namespace transport + +int main(int argc, char **argv) { + return transport::interface::main(argc, argv); +} -- cgit 1.2.3-korg