diff options
Diffstat (limited to 'apps/ping/src/ping_client.cc')
-rw-r--r-- | apps/ping/src/ping_client.cc | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/apps/ping/src/ping_client.cc b/apps/ping/src/ping_client.cc new file mode 100644 index 000000000..08938734b --- /dev/null +++ b/apps/ping/src/ping_client.cc @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hicn/apps/utils/logger.h> +#include <hicn/transport/auth/signer.h> +#include <hicn/transport/auth/verifier.h> +#include <hicn/transport/core/global_object_pool.h> +#include <hicn/transport/core/interest.h> +#include <hicn/transport/interfaces/global_conf_interface.h> +#include <hicn/transport/interfaces/portal.h> +#include <hicn/transport/utils/chrono_typedefs.h> +#include <hicn/transport/utils/traffic_generator.h> + +#include <asio/signal_set.hpp> +#include <asio/steady_timer.hpp> +#include <chrono> +#include <map> + +static constexpr uint32_t SYN_STATE = 1; + +namespace transport { + +namespace core { + +namespace ping { + +using SendTimeMap = std::map<uint64_t, utils::SteadyTime::TimePoint>; +using Verifier = auth::AsymmetricVerifier; + +class Configuration { + public: + static constexpr char TRAFFIC_GENERATOR_RAND[] = "RANDOM"; + + uint32_t num_int_manifest_suffixes_ = + 0; // Number of suffixes in interest manifest (suffix in the header + // is not included in the count) + uint64_t interestLifetime_ = 500; // ms + uint64_t pingInterval_ = 1000000; // us + uint32_t maxPing_ = 10; // number of interests + uint32_t first_suffix_ = 0; + std::string name_ = "b001::1"; + std::string certificate_; + std::string passphrase_; + std::string traffic_generator_type_; + bool jump_ = false; + uint32_t jump_freq_ = 0; + uint32_t jump_size_ = 0; + hicn_packet_format_t packet_format_ = HICN_PACKET_FORMAT_DEFAULT; + + Configuration() = default; +}; + +class Client : public interface::Portal::TransportCallback { + public: + explicit Client(Configuration *c) + : signals_(io_service_, SIGINT), + config_(c), + timer_(std::make_unique<asio::steady_timer>( + portal_.getThread().getIoService())) { + // Let the main thread to catch SIGINT + signals_.async_wait(std::bind(&Client::afterSignal, this)); + + if (!c->certificate_.empty()) { + verifier_.useCertificate(c->certificate_); + } + + // If interst manifest, sign it + if (c->num_int_manifest_suffixes_ != 0) { + assert(!c->passphrase_.empty()); + signer_ = std::make_unique<auth::SymmetricSigner>( + auth::CryptoSuite::HMAC_SHA256, c->passphrase_); + } + + if (c->traffic_generator_type_ == + std::string(Configuration::TRAFFIC_GENERATOR_RAND)) { + traffic_generator_ = + std::make_unique<RandomTrafficGenerator>(config_->maxPing_); + } else { + traffic_generator_ = std::make_unique<IncrSuffixTrafficGenerator>( + config_->name_, config_->first_suffix_, config_->maxPing_); + } + } + + virtual ~Client() = default; + + void ping() { + LoggerInfo() << "Starting ping..."; + + portal_.getThread().add([this]() { + portal_.connect(); + portal_.registerTransportCallback(this); + doPing(); + }); + + io_service_.run(); + } + + void onInterest(Interest &interest) override { + LoggerInfo() << "Unexpected interest received."; + } + + void onContentObject(Interest &interest, ContentObject &object) override { + uint64_t rtt = 0; + + if (!config_->certificate_.empty()) { + auto t0 = utils::SteadyTime::now(); + if (verifier_.verifyPacket(&object)) { + auto t1 = utils::SteadyTime::now(); + auto dt = utils::SteadyTime::getDurationUs(t0, t1); + LoggerInfo() << "Verification time: " << dt.count(); + LoggerInfo() << "<<< Signature Ok."; + } else { + LoggerErr() << "<<< Signature verification failed!"; + } + } + + if (auto it = send_timestamps_.find(interest.getName().getSuffix()); + it != send_timestamps_.end()) { + rtt = + utils::SteadyTime::getDurationUs(it->second, utils::SteadyTime::now()) + .count(); + send_timestamps_.erase(it); + } + + if (LoggerIsOn(2)) { + LoggerInfo() << "<<< recevied object. "; + LoggerInfo() << "<<< interest name: " << interest.getName().getPrefix() + << " (n_suffixes=" << config_->num_int_manifest_suffixes_ + << ")"; + LoggerInfo() << "<<< object name: " << object.getName() << " path label " + << object.getPathLabel() << " (" + << (object.getPathLabel() >> 24) << ")"; + } else if (LoggerIsOn(1)) { + LoggerInfo() << "<<< received object. "; + LoggerInfo() << "<<< round trip: " << rtt << " [us]"; + LoggerInfo() << "<<< interest name: " << interest.getName().getPrefix(); + + LoggerInfo() << "<<< object name: " << object.getName(); + LoggerInfo() << "<<< content object size: " + << object.payloadSize() + object.headerSize() << " [bytes]"; + } + + if (LoggerIsOn(3)) { + LoggerInfo() << "----- interest dump -----"; + interest.dump(); + LoggerInfo() << "-------------------------"; + LoggerInfo() << "----- object dump -------"; + object.dump(); + LoggerInfo() << "-------------------------"; + } + LoggerVerbose(1) << "\n"; + + received_++; + processed_++; + if (processed_ >= config_->maxPing_) { + afterSignal(); + } + } + + void onTimeout(Interest::Ptr &interest, const Name &name) override { + if (LoggerIsOn(2)) { + LoggerInfo() << "### timeout for " << name; + } else if (LoggerIsOn(1)) { + LoggerInfo() << "### timeout for " << name; + } + + if (LoggerIsOn(3)) { + LoggerInfo() << "----- interest dump -----"; + interest->dump(); + LoggerInfo() << "-------------------------"; + } + LoggerVerbose(1) << "\n"; + + timedout_++; + processed_++; + if (processed_ >= config_->maxPing_) afterSignal(); + } + + void onError(const std::error_code &ec) override { + LoggerErr() << "Aborting ping due to internal error: " << ec.message(); + afterSignal(); + } + + void checkFamily(hicn_packet_format_t format, int family) { + switch (HICN_PACKET_FORMAT_GET(format, 0)) { + case IPPROTO_IP: + if (family != AF_INET) throw std::runtime_error("Bad packet format"); + break; + case IPPROTO_IPV6: + if (family != AF_INET6) throw std::runtime_error("Bad packet format"); + break; + default: + throw std::runtime_error("Bad packet format"); + } + } + + void doPing() { + std::string name = traffic_generator_->getPrefix(); + uint32_t sequence_number = traffic_generator_->getSuffix(); + const Name interest_name(name, sequence_number); + + hicn_packet_format_t format = config_->packet_format_; + + switch (format) { + case HICN_PACKET_FORMAT_NEW: + /* Nothing to do */ + break; + case HICN_PACKET_FORMAT_IPV4_TCP: + case HICN_PACKET_FORMAT_IPV6_TCP: + checkFamily(format, interest_name.getAddressFamily()); + break; + default: + throw std::runtime_error("Bad packet format"); + } + + /* + * Eventually add the AH header if a signer is defined. Raise an error + * if format include the AH header but no signer is defined. + */ + if (HICN_PACKET_FORMAT_IS_AH(format)) { + if (!signer_) throw std::runtime_error("Bad packet format"); + } else { + if (signer_) format = Packet::toAHFormat(format); + } + + auto interest = core::PacketManager<>::getInstance().getPacket<Interest>( + format, signer_ ? signer_->getSignatureFieldSize() : 0); + interest->setName(interest_name); + + interest->setLifetime(uint32_t(config_->interestLifetime_)); + + if (LoggerIsOn(2)) { + LoggerInfo() << ">>> send interest " << interest->getName() + << " suffixes in manifest: " + << config_->num_int_manifest_suffixes_; + } else if (LoggerIsOn(1)) { + LoggerInfo() << ">>> send interest " << interest->getName(); + } + LoggerVerbose(1) << "\n"; + + send_timestamps_[sequence_number] = utils::SteadyTime::now(); + for (uint32_t i = 0; i < config_->num_int_manifest_suffixes_ && + !traffic_generator_->hasFinished(); + i++) { + uint32_t sequence_number = traffic_generator_->getSuffix(); + + interest->appendSuffix(sequence_number); + send_timestamps_[sequence_number] = utils::SteadyTime::now(); + } + + if (LoggerIsOn(3)) { + LoggerInfo() << "----- interest dump -----"; + interest->dump(); + LoggerInfo() << "-------------------------"; + } + + interest->encodeSuffixes(); + if (signer_) signer_->signPacket(interest.get()); + portal_.sendInterest(interest, interest->getLifetime()); + + if (!traffic_generator_->hasFinished()) { + this->timer_->expires_from_now( + std::chrono::microseconds(config_->pingInterval_)); + this->timer_->async_wait([this](const std::error_code e) { + if (!e) { + doPing(); + } + }); + } + } + + void afterSignal() { + LoggerInfo() << "Stopping ping..."; + LoggerInfo() << "Sent: " << traffic_generator_->getSentCount() + << " Received: " << received_ << " Timeouts: " << timedout_; + io_service_.stop(); + } + + void reset() { + timer_.reset(new asio::steady_timer(portal_.getThread().getIoService())); + traffic_generator_->reset(); + last_jump_ = 0; + processed_ = 0; + state_ = SYN_STATE; + received_ = 0; + timedout_ = 0; + } + + private: + SendTimeMap send_timestamps_; + asio::io_service io_service_; + interface::Portal portal_; + asio::signal_set signals_; + Configuration *config_; + std::unique_ptr<asio::steady_timer> timer_; + uint64_t last_jump_ = 0; + uint64_t processed_ = 0; + uint32_t state_ = SYN_STATE; + uint32_t received_ = 0; + uint32_t timedout_ = 0; + Verifier verifier_; + std::unique_ptr<auth::Signer> signer_; + std::unique_ptr<TrafficGenerator> traffic_generator_; +}; + +static std::unordered_map<std::string, hicn_packet_format_t> const + packet_format_map = {{"ipv4_tcp", HICN_PACKET_FORMAT_IPV4_TCP}, + {"ipv6_tcp", HICN_PACKET_FORMAT_IPV6_TCP}, + {"new", HICN_PACKET_FORMAT_NEW}}; + +std::string str_tolower(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c) { return std::tolower(c); }); + return s; +} + +void help() { + LoggerInfo() << "usage: hicn-consumer-ping [options]"; + LoggerInfo() << "PING options"; + LoggerInfo() << "-i <val> ping interval in microseconds (default " + "1000000ms)"; + LoggerInfo() + << "-m <val> maximum number of pings to send (default 10)"; + LoggerInfo() << "-a <val> <pass> set the passphrase and the number of " + "suffixes in interest manifest (default 0);"; + LoggerInfo() + << " e.g. '-m 6 -a -2' sends two interest (0 and " + "3) with 2 suffixes each (1,2 and 4,5 respectively)"; + LoggerInfo() << "HICN options"; + LoggerInfo() << "-n <val> hicn name (default b001::1)"; + LoggerInfo() + << "-l <val> interest lifetime in milliseconds (default " + "500ms)"; + LoggerInfo() << "OUTPUT options"; + LoggerInfo() << "-V verbose, prints statistics about the " + "messagges sent and received (default false)"; + LoggerInfo() << "-D dump, dumps sent and received packets " + "(default false)"; + LoggerInfo() << "-q quiet, not prints (default false)"; + LoggerInfo() + << "-z <io_module> IO module to use. Default: hicnlight_module"; + LoggerInfo() << "-F <conf_file> Path to optional configuration file for " + "libtransport"; + LoggerInfo() << "-b <type> Traffic generator type. Use 'RANDOM' for " + "random prefixes and suffixes. Default: sequential suffixes."; + LoggerInfo() + << "-w <packet_format> Packet format (without signature, defaults " + "to IPV6_TCP)"; + LoggerInfo() << "-H prints this message"; +} + +int start(int argc, char *argv[]) { +#ifdef _WIN32 + WSADATA wsaData = {0}; + WSAStartup(MAKEWORD(2, 2), &wsaData); +#endif + + transport::interface::global_config::GlobalConfigInterface global_conf; + + auto c = std::make_unique<Configuration>(); + int opt; + std::string producer_certificate = ""; + + std::string conf_file; + transport::interface::global_config::IoModuleConfiguration io_config; + io_config.name = "hicnlight_module"; + + while ((opt = getopt(argc, argv, "a:b:i:m:f:n:l:c:z:F:w:H")) != -1) { + switch (opt) { + case 'a': + c->num_int_manifest_suffixes_ = std::stoi(optarg); + c->passphrase_ = argv[optind]; + break; + case 'b': + c->traffic_generator_type_ = optarg; + break; + case 'i': + c->pingInterval_ = std::stoi(optarg); + break; + case 'm': + c->maxPing_ = std::stoi(optarg); + break; + case 'f': + c->first_suffix_ = uint32_t(std::stoul(optarg)); + break; + case 'n': + c->name_ = optarg; + break; + case 'l': + c->interestLifetime_ = std::stoi(optarg); + break; + case 'c': + c->certificate_ = std::string(optarg); + break; + case 'z': + io_config.name = optarg; + break; + case 'F': + conf_file = optarg; + break; + case 'w': { + std::string packet_format_s = std::string(optarg); + packet_format_s = str_tolower(packet_format_s); + auto it = packet_format_map.find(std::string(optarg)); + if (it == packet_format_map.end()) + throw std::runtime_error("Bad packet format"); + c->packet_format_ = it->second; + break; + } + default: + help(); + exit(EXIT_FAILURE); + } + } + + /** + * IO module configuration + */ + io_config.set(); + + /** + * Parse config file + */ + global_conf.parseConfigurationFile(conf_file); + + auto ping = std::make_unique<Client>(c.get()); + + auto t0 = std::chrono::steady_clock::now(); + ping->ping(); + auto t1 = std::chrono::steady_clock::now(); + + LoggerInfo() << "Elapsed time: " + << utils::SteadyTime::getDurationMs(t0, t1).count() << "ms"; + +#ifdef _WIN32 + WSACleanup(); +#endif + return 0; +} + +} // namespace ping + +} // namespace core + +} // namespace transport + +int main(int argc, char *argv[]) { + return transport::core::ping::start(argc, argv); +} |