From 35185338b1bdfc47207a2dc6529ed547b188fd8a Mon Sep 17 00:00:00 2001 From: Davide Pesavento Date: Mon, 14 Jan 2019 04:00:15 -0500 Subject: [PATCH] Code modernization Change-Id: Ia15bb9e988b08bc81c0709d95d7d17e6078a5385 --- README.md | 35 +- ndn-traffic-client.conf.sample | 4 +- src/logger.hpp | 134 ++-- src/ndn-traffic-client.cpp | 1061 +++++++++++++++----------------- src/ndn-traffic-server.cpp | 609 +++++++++--------- src/util.hpp | 61 ++ 6 files changed, 925 insertions(+), 979 deletions(-) create mode 100644 src/util.hpp diff --git a/README.md b/README.md index 7a3f250..5bc9b26 100644 --- a/README.md +++ b/README.md @@ -31,23 +31,25 @@ Compiling and running ndn-traffic-generator requires the following dependencies: ## 2. Tool Run Instructions & Command Line Options: ## - Usage: ndn-traffic-server [options] - Respond to Interest as per provided traffic configuration file + Usage: ndn-traffic-server [options] + Respond to Interests as per provided Traffic_Configuration_File. Multiple prefixes can be configured for handling. - Set environment variable NDN_TRAFFIC_LOGFOLDER for redirecting output to a log. - [-d interval] - set delay before responding to interest in milliseconds - [-c count] - specify maximum number of interests to be satisfied - [-q] - quiet logging - no interest reception/data generation messages - [-h] - print help and exit + Set the environment variable NDN_TRAFFIC_LOGFOLDER to redirect output to a log file. + Options: + -h [ --help ] print this help message and exit + -c [ --count ] arg maximum number of Interests to respond to + -d [ --delay ] arg (=0) wait this amount of milliseconds before responding to each Interest + -q [ --quiet ] turn off logging of Interest reception/Data generation - Usage: ndn-traffic [options] - Generate Interest traffic as per provided traffic configuration file + Usage: ndn-traffic [options] + Generate Interest traffic as per provided Traffic_Configuration_File. Interests are continuously generated unless a total number is specified. - Set environment variable NDN_TRAFFIC_LOGFOLDER for redirecting output to a log. - [-i interval] - set interest generation interval in milliseconds (default 1000 milliseconds) - [-c count] - set total number of interests to be generated - [-q] - quiet logging - no interest reception/data generation messages - [-h] - print help and exit + Set the environment variable NDN_TRAFFIC_LOGFOLDER to redirect output to a log file. + Options: + -h [ --help ] print this help message and exit + -c [ --count ] arg total number of Interests to be generated + -i [ --interval ] arg (=1000) Interest generation interval in milliseconds + -q [ --quiet ] turn off logging of Interest generation/Data reception ## 3. Sample Run Instructions ## @@ -68,7 +70,6 @@ Start the traffic client: ndn-traffic ndn-traffic-client.conf - * These tools need not be used together and can be used individually as well. -* Please refer sample configuration files provided for details on how to create your own. -* Use command line options shown above to adjust traffic configuration. +* Please refer to the sample configuration files provided for details on how to create your own. +* Use the command line options shown above to adjust traffic configuration. diff --git a/ndn-traffic-client.conf.sample b/ndn-traffic-client.conf.sample index c038d35..9965d3a 100644 --- a/ndn-traffic-client.conf.sample +++ b/ndn-traffic-client.conf.sample @@ -11,8 +11,8 @@ # IT FOR BEING CONSIDERED VALID # (Optional) MEANS OTHERWISE # * ALSO PROVIDED ARE VALID VALUES POSSIBLE FOR EACH PARAMETER -# * PLEASE ENSURE THAT THE SUM OF TRAFFIC PERCENTAGE FOR ALL PATTERNS -# PROVIDED DOES NOT EXCEED 100 IN ORDER TO MAINTAIN CORRECT BEHAVIOR +# * PLEASE ENSURE THAT THE SUM OF TrafficPercentage FOR ALL DECLARED +# PATTERNS DOES NOT EXCEED 100 IN ORDER TO MAINTAIN CORRECT BEHAVIOR # * NNI STANDS FOR NON-NEGATIVE INTEGER # * RANGE OF POSSIBLE VALUES IS SPECIFIED IN [] # diff --git a/src/logger.hpp b/src/logger.hpp index 0ed35b3..71cfbc2 100644 --- a/src/logger.hpp +++ b/src/logger.hpp @@ -1,6 +1,6 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */ -/** - * Copyright (C) 2014-2015 University of Arizona. +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2014-2019, Arizona Board of Regents. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,21 +33,74 @@ namespace ndn { class Logger { public: + explicit Logger(const std::string& module) : m_module(module) { } void - shutdownLogger() + log(const std::string& logLine, bool printTime, bool printToConsole) { - if (m_logFile.is_open()) - { - log("Terminating Logging Operations" , true, true); - m_logFile.close(); + if (m_logLocation.length() > 0) { + if (printTime) + m_logFile << getTimestamp() << " - "; + m_logFile << logLine << std::endl; + m_logFile.flush(); + if (printToConsole) { + if (printTime) + std::cout << getTimestamp() << " - "; + std::cout << logLine << std::endl; } + } + else { + if (printTime) + std::cout << getTimestamp() << " - "; + std::cout << logLine << std::endl; + } } + void + initializeLog(const std::string& instanceId) + { + m_logLocation = ""; + const char* envVar = std::getenv("NDN_TRAFFIC_LOGFOLDER"); + if (envVar != nullptr) + m_logLocation = envVar; + + if (m_logLocation.empty()) { + std::cout << "Environment variable NDN_TRAFFIC_LOGFOLDER not set.\n" + << "Using default output for logging." << std::endl; + return; + } + + if (boost::filesystem::exists(boost::filesystem::path(m_logLocation))) { + if (boost::filesystem::is_directory(boost::filesystem::path(m_logLocation))) { + std::string logFilename = m_logLocation + "/" + m_module + "_" + instanceId + ".log"; + m_logFile.open(logFilename.data(), std::ofstream::out | std::ofstream::trunc); + if (m_logFile.is_open()) { + std::cout << "Log file initialized: " << logFilename << std::endl; + } + else { + std::cout << "ERROR: Unable to initialize a log file at: " << m_logLocation << std::endl + << "Using default output for logging." << std::endl; + m_logLocation = ""; + } + } + else { + std::cout << "NDN_TRAFFIC_LOGFOLDER should be a directory.\n" + << "Using default output for logging." << std::endl; + m_logLocation = ""; + } + } + else { + std::cout << "NDN_TRAFFIC_LOGFOLDER does not exist.\n" + << "Using default output for logging." << std::endl; + m_logLocation = ""; + } + } + +private: static std::string getTimestamp() { @@ -56,71 +109,6 @@ public: return to_simple_string(now); } - void - log(const std::string& logLine, bool printTime, bool printToConsole) - { - if (m_logLocation.length() > 0) - { - if (printTime) - m_logFile << getTimestamp() << " - "; - m_logFile << logLine << std::endl; - m_logFile.flush(); - if (printToConsole) - { - if (printTime) - std::cout << getTimestamp() << " - "; - std::cout << logLine << std::endl; - } - } - else - { - if (printTime) - std::cout << getTimestamp() << " - "; - std::cout << logLine << std::endl; - } - } - - void - initializeLog(const std::string& instanceId) - { - const char* envVar = std::getenv("NDN_TRAFFIC_LOGFOLDER"); - m_logLocation = ""; - if (envVar != nullptr) - m_logLocation = envVar; - - std::string logFilename; - if (boost::filesystem::exists(boost::filesystem::path(m_logLocation))) - { - if (boost::filesystem::is_directory(boost::filesystem::path(m_logLocation))) - { - logFilename = m_logLocation + "/" + m_module + "_" + instanceId + ".log"; - m_logFile.open(logFilename.c_str(), std::ofstream::out | std::ofstream::trunc); - if (m_logFile.is_open()) - std::cout << "Log File Initialized: " << logFilename << std::endl; - else - { - std::cout << "ERROR - Unable To Initialize A Log File At: " - << m_logLocation << std::endl - << "Using Default Output For Logging." << std::endl; - m_logLocation = ""; - } - } - else - { - std::cout << "Environment Variable NDN_TRAFFIC_LOGFOLDER Should Be A Folder." - << std::endl - << "Using Default Output For Logging." << std::endl; - m_logLocation = ""; - } - } - else - { - std::cout << "Environment Variable NDN_TRAFFIC_LOGFOLDER Not Set." << std::endl - << "Using Default Output For Logging." << std::endl; - m_logLocation = ""; - } - } - private: std::string m_module; std::string m_logLocation; diff --git a/src/ndn-traffic-client.cpp b/src/ndn-traffic-client.cpp index 062b54d..adba253 100644 --- a/src/ndn-traffic-client.cpp +++ b/src/ndn-traffic-client.cpp @@ -19,9 +19,16 @@ */ #include "logger.hpp" +#include "util.hpp" + +#include +#include +#include +#include +#include #include -#include +#include #include #include @@ -29,12 +36,13 @@ #include #include #include +#include #include +#include +#include +#include -#include -#include -#include -#include +namespace po = boost::program_options; namespace ndn { @@ -42,178 +50,14 @@ class NdnTrafficClient : boost::noncopyable { public: explicit - NdnTrafficClient(const char* programName) - : m_programName(programName) + NdnTrafficClient(const std::string& configFile) + : m_signalSet(m_ioService, SIGINT, SIGTERM) , m_logger("NdnTrafficClient") - , m_instanceId(to_string(std::rand())) - , m_hasError(false) - , m_hasQuietLogging(false) - , m_interestInterval(getDefaultInterestInterval()) - , m_nMaximumInterests(-1) , m_face(m_ioService) - , m_nInterestsSent(0) - , m_nInterestsReceived(0) - , m_nNacks(0) - , m_nContentInconsistencies(0) - , m_minimumInterestRoundTripTime(std::numeric_limits::max()) - , m_maximumInterestRoundTripTime(0) - , m_totalInterestRoundTripTime(0) + , m_configurationFile(configFile) { } - class InterestTrafficConfiguration - { - public: - InterestTrafficConfiguration() - : m_trafficPercentage(-1) - , m_nameAppendBytes(-1) - , m_nameAppendSequenceNumber(-1) - , m_mustBeFresh(-1) - , m_nonceDuplicationPercentage(-1) - , m_interestLifetime(getDefaultInterestLifetime()) - , m_nextHopFaceId(0) - , m_nInterestsSent(0) - , m_nInterestsReceived(0) - , m_nNacks(0) - , m_minimumInterestRoundTripTime(std::numeric_limits::max()) - , m_maximumInterestRoundTripTime(0) - , m_totalInterestRoundTripTime(0) - , m_nContentInconsistencies(0) - { - } - - static time::milliseconds - getDefaultInterestLifetime() - { - return -1_ms; - } - - void - printTrafficConfiguration(Logger& logger) - { - std::string detail; - - if (m_trafficPercentage > 0) - detail += "TrafficPercentage=" + to_string(m_trafficPercentage) + ", "; - if (!m_name.empty()) - detail += "Name=" + m_name + ", "; - if (m_nameAppendBytes > 0) - detail += "NameAppendBytes=" + to_string(m_nameAppendBytes) + ", "; - if (m_nameAppendSequenceNumber > 0) - detail += "NameAppendSequenceNumber=" + to_string(m_nameAppendSequenceNumber) + ", "; - if (m_mustBeFresh >= 0) - detail += "MustBeFresh=" + to_string(m_mustBeFresh) + ", "; - if (m_nonceDuplicationPercentage > 0) - detail += "NonceDuplicationPercentage=" + to_string(m_nonceDuplicationPercentage) + ", "; - if (m_interestLifetime >= 0_ms) - detail += "InterestLifetime=" + to_string(m_interestLifetime.count()) + ", "; - if (m_nextHopFaceId > 0) - detail += "NextHopFaceId=" + to_string(m_nextHopFaceId) + ", "; - if (!m_expectedContent.empty()) - detail += "ExpectedContent=" + m_expectedContent + ", "; - if (detail.length() >= 2) - detail = detail.substr(0, detail.length() - 2); // Removing suffix ", " - - logger.log(detail, false, false); - } - - bool - extractParameterValue(const std::string& detail, std::string& parameter, std::string& value) - { - std::string allowedCharacters = ":/+.,_-%"; - std::size_t i = 0; - - parameter = ""; - value = ""; - while (detail[i] != '=' && i < detail.length()) { - parameter += detail[i]; - i++; - } - if (i == detail.length()) - return false; - - i++; - while ((std::isalnum(detail[i]) || - allowedCharacters.find(detail[i]) != std::string::npos) && - i < detail.length()) { - value += detail[i]; - i++; - } - - if (parameter.empty() || value.empty()) - return false; - else - return true; - } - - bool - processConfigurationDetail(const std::string& detail, Logger& logger, int lineNumber) - { - std::string parameter, value; - if (extractParameterValue(detail, parameter, value)) - { - if (parameter == "TrafficPercentage") - m_trafficPercentage = std::stoi(value); - else if (parameter == "Name") - m_name = value; - else if (parameter == "NameAppendBytes") - m_nameAppendBytes = std::stoi(value); - else if (parameter == "NameAppendSequenceNumber") - m_nameAppendSequenceNumber = std::stoi(value); - else if (parameter == "MustBeFresh") - m_mustBeFresh = std::stoi(value); - else if (parameter == "NonceDuplicationPercentage") - m_nonceDuplicationPercentage = std::stoi(value); - else if (parameter == "InterestLifetime") - m_interestLifetime = time::milliseconds(std::stoi(value)); - else if (parameter == "NextHopFaceId") - m_nextHopFaceId = std::stoi(value); - else if (parameter == "ExpectedContent") - m_expectedContent = value; - else - logger.log("Line " + to_string(lineNumber) + - " \t- Invalid Parameter='" + parameter + "'", false, true); - } - else - { - logger.log("Line " + to_string(lineNumber) + - " \t- Improper Traffic Configuration Line- " + detail, false, true); - return false; - } - return true; - } - - bool - checkTrafficDetailCorrectness() - { - return true; - } - - private: - int m_trafficPercentage; - std::string m_name; - int m_nameAppendBytes; - int m_nameAppendSequenceNumber; - int m_mustBeFresh; - int m_nonceDuplicationPercentage; - time::milliseconds m_interestLifetime; - uint64_t m_nextHopFaceId; - int m_nInterestsSent; - int m_nInterestsReceived; - int m_nNacks; - - //round trip time is stored as milliseconds with fractional - //sub-millisecond precision - double m_minimumInterestRoundTripTime; - double m_maximumInterestRoundTripTime; - double m_totalInterestRoundTripTime; - - int m_nContentInconsistencies; - std::string m_expectedContent; - - friend class NdnTrafficClient; - }; // class InterestTrafficConfiguration - bool hasError() const { @@ -221,145 +65,212 @@ public: } void - usage() const + setMaximumInterests(uint64_t maxInterests) { - std::cout << "Usage:\n" - << " " << m_programName << " [options] \n" - << "\n" - << "Generate Interest traffic as per provided Traffic Configuration File.\n" - << "Interests are continuously generated unless a total number is specified.\n" - << "Set environment variable NDN_TRAFFIC_LOGFOLDER to redirect output to a log file.\n" - << "\n" - << "Options:\n" - << " [-i interval] - set interest generation interval in milliseconds (default " - << getDefaultInterestInterval() << ")\n" - << " [-c count] - set total number of interests to be generated\n" - << " [-q] - quiet mode: no interest reception/data generation logging\n" - << " [-h] - print this help text and exit\n"; - exit(EXIT_FAILURE); - } - - static time::milliseconds - getDefaultInterestInterval() - { - return 1_s; + m_nMaximumInterests = maxInterests; } void - setInterestInterval(int interestInterval) + setInterestInterval(time::milliseconds interval) { - if (interestInterval <= 0) - usage(); - m_interestInterval = time::milliseconds(interestInterval); - } - - void - setMaximumInterests(int maximumInterests) - { - if (maximumInterests <= 0) - usage(); - m_nMaximumInterests = maximumInterests; - } - - void - setConfigurationFile(char* configurationFile) - { - m_configurationFile = configurationFile; + BOOST_ASSERT(interval > 0_ms); + m_interestInterval = interval; } void setQuietLogging() { - m_hasQuietLogging = true; + m_wantQuiet = true; } void - signalHandler() + run() { - logStatistics(); + m_logger.initializeLog(to_string(random::generateWord32())); + initializeTrafficConfiguration(); - m_logger.shutdownLogger(); - m_face.shutdown(); - m_ioService.stop(); + if (m_nMaximumInterests == 0) { + logStatistics(); + return; + } - exit(m_hasError ? EXIT_FAILURE : EXIT_SUCCESS); + m_signalSet.async_wait([this] (auto&&...) { this->stop(); }); + + boost::asio::deadline_timer timer(m_ioService, + boost::posix_time::millisec(m_interestInterval.count())); + timer.async_wait([this, &timer] (auto&&...) { this->generateTraffic(timer); }); + + try { + m_face.processEvents(); + } + catch (const std::exception& e) { + m_logger.log("ERROR: "s + e.what(), true, true); + m_hasError = true; + m_ioService.stop(); + } } +private: + class InterestTrafficConfiguration + { + public: + void + printTrafficConfiguration(Logger& logger) const + { + std::string detail; + + detail += "TrafficPercentage=" + to_string(m_trafficPercentage) + ", "; + if (!m_name.empty()) + detail += "Name=" + m_name + ", "; + if (m_nameAppendBytes) + detail += "NameAppendBytes=" + to_string(*m_nameAppendBytes) + ", "; + if (m_nameAppendSeqNum) + detail += "NameAppendSequenceNumber=" + to_string(*m_nameAppendSeqNum) + ", "; + if (!boost::logic::indeterminate(m_mustBeFresh)) + detail += "MustBeFresh=" + to_string(bool(m_mustBeFresh)) + ", "; + if (m_nonceDuplicationPercentage > 0) + detail += "NonceDuplicationPercentage=" + to_string(m_nonceDuplicationPercentage) + ", "; + if (m_interestLifetime >= 0_ms) + detail += "InterestLifetime=" + to_string(m_interestLifetime.count()) + ", "; + if (m_nextHopFaceId > 0) + detail += "NextHopFaceId=" + to_string(m_nextHopFaceId) + ", "; + if (m_expectedContent) + detail += "ExpectedContent=" + *m_expectedContent + ", "; + if (detail.length() >= 2) + detail = detail.substr(0, detail.length() - 2); // Removing suffix ", " + + logger.log(detail, false, false); + } + + bool + processConfigurationDetail(const std::string& detail, Logger& logger, int lineNumber) + { + std::string parameter, value; + if (parseParameterAndValue(detail, parameter, value)) { + if (parameter == "TrafficPercentage") + m_trafficPercentage = std::stoul(value); + else if (parameter == "Name") + m_name = value; + else if (parameter == "NameAppendBytes") + m_nameAppendBytes = std::stoul(value); + else if (parameter == "NameAppendSequenceNumber") + m_nameAppendSeqNum = std::stoull(value); + else if (parameter == "MustBeFresh") + m_mustBeFresh = bool(std::stoul(value)); + else if (parameter == "NonceDuplicationPercentage") + m_nonceDuplicationPercentage = std::stoul(value); + else if (parameter == "InterestLifetime") + m_interestLifetime = time::milliseconds(std::stoul(value)); + else if (parameter == "NextHopFaceId") + m_nextHopFaceId = std::stoull(value); + else if (parameter == "ExpectedContent") + m_expectedContent = value; + else + logger.log("Line " + to_string(lineNumber) + + " - Unknown parameter '" + parameter + "'", false, true); + } + else { + logger.log("Line " + to_string(lineNumber) + + " - Invalid traffic configuration line: " + detail, false, true); + return false; + } + return true; + } + + bool + checkTrafficDetailCorrectness() const + { + return true; + } + + public: + uint8_t m_trafficPercentage = 0; + std::string m_name; + optional m_nameAppendBytes; + optional m_nameAppendSeqNum; + boost::logic::tribool m_mustBeFresh = boost::logic::indeterminate; + uint8_t m_nonceDuplicationPercentage = 0; + time::milliseconds m_interestLifetime = -1_ms; + uint64_t m_nextHopFaceId = 0; + optional m_expectedContent; + + uint64_t m_nInterestsSent = 0; + uint64_t m_nInterestsReceived = 0; + uint64_t m_nNacks = 0; + uint64_t m_nContentInconsistencies = 0; + + // RTT is stored as milliseconds with fractional sub-milliseconds precision + double m_minimumInterestRoundTripTime = std::numeric_limits::max(); + double m_maximumInterestRoundTripTime = 0; + double m_totalInterestRoundTripTime = 0; + }; + void logStatistics() { m_logger.log("\n\n== Interest Traffic Report ==\n", false, true); m_logger.log("Total Traffic Pattern Types = " + - to_string(m_trafficPatterns.size()), false, true); + to_string(m_trafficPatterns.size()), false, true); m_logger.log("Total Interests Sent = " + - to_string(m_nInterestsSent), false, true); + to_string(m_nInterestsSent), false, true); m_logger.log("Total Responses Received = " + - to_string(m_nInterestsReceived), false, true); + to_string(m_nInterestsReceived), false, true); m_logger.log("Total Nacks Received = " + - to_string(m_nNacks), false, true); + to_string(m_nNacks), false, true); - double loss = 0; - if (m_nInterestsSent > 0) + double loss = 0.0; + if (m_nInterestsSent > 0) { loss = (m_nInterestsSent - m_nInterestsReceived) * 100.0 / m_nInterestsSent; + } m_logger.log("Total Interest Loss = " + to_string(loss) + "%", false, true); - if (m_nContentInconsistencies != 0 || m_nInterestsSent != m_nInterestsReceived) - m_hasError = true; - double average = 0; - double inconsistency = 0; - if (m_nInterestsReceived > 0) - { - average = m_totalInterestRoundTripTime / m_nInterestsReceived; - inconsistency = m_nContentInconsistencies * 100.0 / m_nInterestsReceived; - } + double average = 0.0; + double inconsistency = 0.0; + if (m_nInterestsReceived > 0) { + average = m_totalInterestRoundTripTime / m_nInterestsReceived; + inconsistency = m_nContentInconsistencies * 100.0 / m_nInterestsReceived; + } m_logger.log("Total Data Inconsistency = " + - to_string(inconsistency) + "%", false, true); + to_string(inconsistency) + "%", false, true); m_logger.log("Total Round Trip Time = " + - to_string(m_totalInterestRoundTripTime) + "ms", false, true); + to_string(m_totalInterestRoundTripTime) + "ms", false, true); m_logger.log("Average Round Trip Time = " + - to_string(average) + "ms\n", false, true); + to_string(average) + "ms\n", false, true); - for (std::size_t patternId = 0; patternId < m_trafficPatterns.size(); patternId++) - { - m_logger.log("Traffic Pattern Type #" + - to_string(patternId + 1), false, true); - m_trafficPatterns[patternId].printTrafficConfiguration(m_logger); - m_logger.log("Total Interests Sent = " + - to_string(m_trafficPatterns[patternId].m_nInterestsSent), false, true); - m_logger.log("Total Responses Received = " + - to_string(m_trafficPatterns[patternId].m_nInterestsReceived), false, true); - m_logger.log("Total Nacks Received = " + - to_string(m_trafficPatterns[patternId].m_nNacks), false, true); - loss = 0; - if (m_trafficPatterns[patternId].m_nInterestsSent > 0) - { - loss = (m_trafficPatterns[patternId].m_nInterestsSent - - m_trafficPatterns[patternId].m_nInterestsReceived); - loss *= 100.0; - loss /= m_trafficPatterns[patternId].m_nInterestsSent; - } - m_logger.log("Total Interest Loss = " + to_string(loss) + "%", false, true); - average = 0; - inconsistency = 0; - if (m_trafficPatterns[patternId].m_nInterestsReceived > 0) - { - average = (m_trafficPatterns[patternId].m_totalInterestRoundTripTime / - m_trafficPatterns[patternId].m_nInterestsReceived); - inconsistency = m_trafficPatterns[patternId].m_nContentInconsistencies; - inconsistency *= 100.0 / m_trafficPatterns[patternId].m_nInterestsReceived; - } - m_logger.log("Total Data Inconsistency = " + - to_string(inconsistency) + "%", false, true); - m_logger.log("Total Round Trip Time = " + - to_string(m_trafficPatterns[patternId].m_totalInterestRoundTripTime) + - "ms", false, true); - m_logger.log("Average Round Trip Time = " + - to_string(average) + "ms\n", false, true); + for (std::size_t patternId = 0; patternId < m_trafficPatterns.size(); patternId++) { + m_logger.log("Traffic Pattern Type #" + to_string(patternId + 1), false, true); + m_trafficPatterns[patternId].printTrafficConfiguration(m_logger); + m_logger.log("Total Interests Sent = " + + to_string(m_trafficPatterns[patternId].m_nInterestsSent), false, true); + m_logger.log("Total Responses Received = " + + to_string(m_trafficPatterns[patternId].m_nInterestsReceived), false, true); + m_logger.log("Total Nacks Received = " + + to_string(m_trafficPatterns[patternId].m_nNacks), false, true); + loss = 0; + if (m_trafficPatterns[patternId].m_nInterestsSent > 0) { + loss = (m_trafficPatterns[patternId].m_nInterestsSent - + m_trafficPatterns[patternId].m_nInterestsReceived); + loss *= 100.0; + loss /= m_trafficPatterns[patternId].m_nInterestsSent; } + m_logger.log("Total Interest Loss = " + to_string(loss) + "%", false, true); + average = 0; + inconsistency = 0; + if (m_trafficPatterns[patternId].m_nInterestsReceived > 0) { + average = (m_trafficPatterns[patternId].m_totalInterestRoundTripTime / + m_trafficPatterns[patternId].m_nInterestsReceived); + inconsistency = m_trafficPatterns[patternId].m_nContentInconsistencies; + inconsistency *= 100.0 / m_trafficPatterns[patternId].m_nInterestsReceived; + } + m_logger.log("Total Data Inconsistency = " + to_string(inconsistency) + "%", false, true); + m_logger.log("Total Round Trip Time = " + + to_string(m_trafficPatterns[patternId].m_totalInterestRoundTripTime) + "ms", false, true); + m_logger.log("Average Round Trip Time = " + to_string(average) + "ms\n", false, true); + } } bool - checkTrafficPatternCorrectness() + checkTrafficPatternCorrectness() const { return true; } @@ -367,161 +278,188 @@ public: void parseConfigurationFile() { - std::string patternLine; std::ifstream patternFile; - m_logger.log("Analyzing Traffic Configuration File: " + m_configurationFile, true, true); + m_logger.log("Parsing traffic configuration file: " + m_configurationFile, true, true); - patternFile.open(m_configurationFile.c_str()); - if (patternFile.is_open()) - { - int lineNumber = 0; - while (getline(patternFile, patternLine)) - { - lineNumber++; - if (std::isalpha(patternLine[0])) - { - InterestTrafficConfiguration interestData; - bool shouldSkipLine = false; - if (interestData.processConfigurationDetail(patternLine, m_logger, lineNumber)) - { - while (getline(patternFile, patternLine) && std::isalpha(patternLine[0])) - { - lineNumber++; - if (!interestData.processConfigurationDetail(patternLine, - m_logger, lineNumber)) - { - shouldSkipLine = true; - break; - } - } - lineNumber++; - } - else - shouldSkipLine = true; - if (!shouldSkipLine) - { - if (interestData.checkTrafficDetailCorrectness()) - m_trafficPatterns.push_back(interestData); - } + patternFile.open(m_configurationFile.data()); + if (patternFile.is_open()) { + int lineNumber = 0; + std::string patternLine; + while (getline(patternFile, patternLine)) { + lineNumber++; + if (std::isalpha(patternLine[0])) { + InterestTrafficConfiguration interestData; + bool shouldSkipLine = false; + if (interestData.processConfigurationDetail(patternLine, m_logger, lineNumber)) { + while (getline(patternFile, patternLine) && std::isalpha(patternLine[0])) { + lineNumber++; + if (!interestData.processConfigurationDetail(patternLine, m_logger, lineNumber)) { + shouldSkipLine = true; + break; } + } + lineNumber++; } - patternFile.close(); - if (!checkTrafficPatternCorrectness()) - { - m_logger.log("ERROR - Traffic Configuration Provided Is Not Proper- " + - m_configurationFile, false, true); - m_logger.shutdownLogger(); - exit(EXIT_FAILURE); + else { + shouldSkipLine = true; } - m_logger.log("Traffic Configuration File Processing Completed\n", true, false); - for (std::size_t patternId = 0; patternId < m_trafficPatterns.size(); patternId++) - { - m_logger.log("Traffic Pattern Type #" + - to_string(patternId + 1), false, false); - m_trafficPatterns[patternId].printTrafficConfiguration(m_logger); - m_logger.log("", false, false); + if (!shouldSkipLine) { + if (interestData.checkTrafficDetailCorrectness()) { + m_trafficPatterns.push_back(interestData); + } } + } } - else - { - m_logger.log("ERROR - Unable To Open Traffic Configuration File: " + + patternFile.close(); + + if (!checkTrafficPatternCorrectness()) { + m_logger.log("ERROR: Traffic configuration provided is not proper - " + m_configurationFile, false, true); - m_logger.shutdownLogger(); - exit(EXIT_FAILURE); + exit(2); } + + m_logger.log("Traffic configuration file processing completed.\n", true, false); + for (std::size_t patternId = 0; patternId < m_trafficPatterns.size(); patternId++) { + m_logger.log("Traffic Pattern Type #" + to_string(patternId + 1), false, false); + m_trafficPatterns[patternId].printTrafficConfiguration(m_logger); + m_logger.log("", false, false); + } + } + else { + m_logger.log("ERROR: Unable to open traffic configuration file: " + m_configurationFile, false, true); + exit(2); + } } void initializeTrafficConfiguration() { - if (boost::filesystem::exists(boost::filesystem::path(m_configurationFile))) - { - if (boost::filesystem::is_regular_file(boost::filesystem::path(m_configurationFile))) - { - parseConfigurationFile(); - } - else - { - m_logger.log("ERROR - Traffic Configuration File Is Not A Regular File: " + - m_configurationFile, false, true); - m_logger.shutdownLogger(); - exit(EXIT_FAILURE); - } - } - else - { - m_logger.log("ERROR - Traffic Configuration File Does Not Exist: " + - m_configurationFile, false, true); - m_logger.shutdownLogger(); - exit(EXIT_FAILURE); - } - } + namespace fs = boost::filesystem; - uint32_t - getOldNonce() - { - if (m_nonces.size() == 0) - return getNewNonce(); - std::size_t randomNonceIndex = std::rand() % m_nonces.size(); - return m_nonces[randomNonceIndex]; + fs::path configPath(m_configurationFile); + if (fs::exists(configPath)) { + if (fs::is_regular_file(configPath)) { + parseConfigurationFile(); + } + else { + m_logger.log("ERROR: Traffic configuration file is not a regular file: " + + m_configurationFile, false, true); + exit(2); + } + } + else { + m_logger.log("ERROR: Traffic configuration file does not exist: " + + m_configurationFile, false, true); + exit(2); + } } uint32_t getNewNonce() { - //Performance Enhancement - if (m_nonces.size() > 1000) + if (m_nonces.size() >= 1000) m_nonces.clear(); - uint32_t randomNonce = static_cast(std::rand()); + auto randomNonce = random::generateWord32(); while (std::find(m_nonces.begin(), m_nonces.end(), randomNonce) != m_nonces.end()) - randomNonce = static_cast(std::rand()); + randomNonce = random::generateWord32(); m_nonces.push_back(randomNonce); return randomNonce; } + uint32_t + getOldNonce() + { + if (m_nonces.empty()) + return getNewNonce(); + + std::uniform_int_distribution dist(0, m_nonces.size() - 1); + return m_nonces[dist(random::getRandomNumberEngine())]; + } + static name::Component generateRandomNameComponent(std::size_t length) { - Buffer buffer(length); + // per ISO C++ std, cannot instantiate uniform_int_distribution with uint8_t + static std::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + Buffer buf(length); for (std::size_t i = 0; i < length; i++) { - buffer[i] = static_cast(std::rand() % 256); + buf[i] = static_cast(dist(random::getRandomNumberEngine())); } - return name::Component(buffer); + return name::Component(buf); + } + + Interest + prepareInterest(std::size_t patternId) + { + Interest interest; + auto& pattern = m_trafficPatterns[patternId]; + + Name name(pattern.m_name); + if (pattern.m_nameAppendBytes > 0) { + name.append(generateRandomNameComponent(*pattern.m_nameAppendBytes)); + } + if (pattern.m_nameAppendSeqNum) { + auto seqNum = *pattern.m_nameAppendSeqNum; + name.appendSequenceNumber(seqNum); + pattern.m_nameAppendSeqNum = seqNum + 1; + } + interest.setName(name); + + if (pattern.m_mustBeFresh) + interest.setMustBeFresh(true); + else if (!pattern.m_mustBeFresh) + interest.setMustBeFresh(false); + + static std::uniform_int_distribution<> duplicateNonceDist(1, 100); + if (duplicateNonceDist(random::getRandomNumberEngine()) <= pattern.m_nonceDuplicationPercentage) + interest.setNonce(getOldNonce()); + else + interest.setNonce(getNewNonce()); + + if (pattern.m_interestLifetime >= 0_ms) + interest.setInterestLifetime(pattern.m_interestLifetime); + + if (pattern.m_nextHopFaceId > 0) + interest.setTag(make_shared(pattern.m_nextHopFaceId)); + + return interest; } void - onData(const ndn::Interest& interest, - const ndn::Data& data, - int globalReference, - int localReference, - std::size_t patternId, - time::steady_clock::TimePoint sentTime) + onData(const Data& data, int globalRef, int localRef, std::size_t patternId, + const time::steady_clock::TimePoint& sentTime) { - std::string logLine = "Data Received - PatternType=" + to_string(patternId + 1); - logLine += ", GlobalID=" + to_string(globalReference); - logLine += ", LocalID=" + to_string(localReference); - logLine += ", Name=" + interest.getName().toUri(); + auto logLine = "Data Received - PatternType=" + to_string(patternId + 1) + + ", GlobalID=" + to_string(globalRef) + + ", LocalID=" + to_string(localRef) + + ", Name=" + data.getName().toUri(); m_nInterestsReceived++; m_trafficPatterns[patternId].m_nInterestsReceived++; - if (!m_trafficPatterns[patternId].m_expectedContent.empty()) { - std::string receivedContent = reinterpret_cast(data.getContent().value()); - std::size_t receivedContentLength = data.getContent().value_size(); - receivedContent = receivedContent.substr(0, receivedContentLength); - if (receivedContent != m_trafficPatterns[patternId].m_expectedContent) { + + if (m_trafficPatterns[patternId].m_expectedContent) { + std::string receivedContent(reinterpret_cast(data.getContent().value()), + data.getContent().value_size()); + if (receivedContent != *m_trafficPatterns[patternId].m_expectedContent) { m_nContentInconsistencies++; m_trafficPatterns[patternId].m_nContentInconsistencies++; logLine += ", IsConsistent=No"; } - else + else { logLine += ", IsConsistent=Yes"; + } } - else + else { logLine += ", IsConsistent=NotChecked"; - if (!m_hasQuietLogging) + } + if (!m_wantQuiet) { m_logger.log(logLine, true, false); + } + double roundTripTime = (time::steady_clock::now() - sentTime).count() / 1000000.0; if (m_minimumInterestRoundTripTime > roundTripTime) m_minimumInterestRoundTripTime = roundTripTime; @@ -533,245 +471,224 @@ public: m_trafficPatterns[patternId].m_maximumInterestRoundTripTime = roundTripTime; m_totalInterestRoundTripTime += roundTripTime; m_trafficPatterns[patternId].m_totalInterestRoundTripTime += roundTripTime; - if (m_nMaximumInterests >= 0 && globalReference == m_nMaximumInterests) { - logStatistics(); - m_logger.shutdownLogger(); - m_face.shutdown(); - m_ioService.stop(); + + if (m_nMaximumInterests == globalRef) { + stop(); } } void - onNack(const ndn::Interest& interest, - const ndn::lp::Nack& nack, - int globalReference, - int localReference, - std::size_t patternId) + onNack(const Interest& interest, const lp::Nack& nack, + int globalRef, int localRef, std::size_t patternId) { - std::string logLine = "Interest Nack'd - PatternType=" + to_string(patternId + 1); - logLine += ", GlobalID=" + to_string(globalReference); - logLine += ", LocalID=" + to_string(localReference); - logLine += ", Name=" + interest.getName().toUri(); - logLine += ", NackReason=" + boost::lexical_cast(nack.getReason()); + auto logLine = "Interest Nack'd - PatternType=" + to_string(patternId + 1) + + ", GlobalID=" + to_string(globalRef) + + ", LocalID=" + to_string(localRef) + + ", Name=" + interest.getName().toUri() + + ", NackReason=" + boost::lexical_cast(nack.getReason()); m_logger.log(logLine, true, false); m_nNacks++; m_trafficPatterns[patternId].m_nNacks++; - if (m_nMaximumInterests >= 0 && globalReference == m_nMaximumInterests) { - logStatistics(); - m_logger.shutdownLogger(); - m_face.shutdown(); - m_ioService.stop(); + if (m_nMaximumInterests == globalRef) { + stop(); } } void - onTimeout(const ndn::Interest& interest, - int globalReference, - int localReference, - int patternId) + onTimeout(const Interest& interest, int globalRef, int localRef, int patternId) { - std::string logLine = "Interest Timed Out - PatternType=" + to_string(patternId + 1); - logLine += ", GlobalID=" + to_string(globalReference); - logLine += ", LocalID=" + to_string(localReference); - logLine += ", Name=" + interest.getName().toUri(); + auto logLine = "Interest Timed Out - PatternType=" + to_string(patternId + 1) + + ", GlobalID=" + to_string(globalRef) + + ", LocalID=" + to_string(localRef) + + ", Name=" + interest.getName().toUri(); m_logger.log(logLine, true, false); - if (m_nMaximumInterests >= 0 && globalReference == m_nMaximumInterests) - { - logStatistics(); - m_logger.shutdownLogger(); - m_face.shutdown(); - m_ioService.stop(); - } + + if (m_nMaximumInterests == globalRef) { + stop(); + } } void - generateTraffic(boost::asio::deadline_timer* timer) + generateTraffic(boost::asio::deadline_timer& timer) { - if (m_nMaximumInterests < 0 || m_nInterestsSent < m_nMaximumInterests) - { - int trafficKey = std::rand() % 100; - int cumulativePercentage = 0; - std::size_t patternId; - for (patternId = 0; patternId < m_trafficPatterns.size(); patternId++) - { - cumulativePercentage += m_trafficPatterns[patternId].m_trafficPercentage; - if (trafficKey <= cumulativePercentage) - { - Name interestName(m_trafficPatterns[patternId].m_name); - if (m_trafficPatterns[patternId].m_nameAppendBytes > 0) - interestName.append( - generateRandomNameComponent(m_trafficPatterns[patternId].m_nameAppendBytes)); - if (m_trafficPatterns[patternId].m_nameAppendSequenceNumber >= 0) - { - interestName.append( - to_string(m_trafficPatterns[patternId].m_nameAppendSequenceNumber)); - m_trafficPatterns[patternId].m_nameAppendSequenceNumber++; - } - - Interest interest(interestName); - - if (m_trafficPatterns[patternId].m_mustBeFresh == 0) - interest.setMustBeFresh(false); - else if (m_trafficPatterns[patternId].m_mustBeFresh > 0) - interest.setMustBeFresh(true); - if (m_trafficPatterns[patternId].m_nonceDuplicationPercentage > 0) - { - int duplicationPercentage = std::rand() % 100; - if (m_trafficPatterns[patternId].m_nonceDuplicationPercentage <= - duplicationPercentage) - interest.setNonce(getOldNonce()); - else - interest.setNonce(getNewNonce()); - } - else - interest.setNonce(getNewNonce()); - if (m_trafficPatterns[patternId].m_interestLifetime >= 0_ms) - interest.setInterestLifetime(m_trafficPatterns[patternId].m_interestLifetime); - - if (m_trafficPatterns[patternId].m_nextHopFaceId > 0) { - interest.setTag(make_shared( - m_trafficPatterns[patternId].m_nextHopFaceId)); - } - - try { - m_nInterestsSent++; - m_trafficPatterns[patternId].m_nInterestsSent++; - auto sentTime = time::steady_clock::now(); - m_face.expressInterest(interest, - bind(&NdnTrafficClient::onData, - this, _1, _2, m_nInterestsSent, - m_trafficPatterns[patternId].m_nInterestsSent, - patternId, sentTime), - bind(&NdnTrafficClient::onNack, - this, _1, _2, m_nInterestsSent, - m_trafficPatterns[patternId].m_nInterestsSent, - patternId), - bind(&NdnTrafficClient::onTimeout, - this, _1, m_nInterestsSent, - m_trafficPatterns[patternId].m_nInterestsSent, - patternId)); - - if (!m_hasQuietLogging) { - std::string logLine = - "Sending Interest - PatternType=" + to_string(patternId + 1) + - ", GlobalID=" + to_string(m_nInterestsSent) + - ", LocalID=" + - to_string(m_trafficPatterns[patternId].m_nInterestsSent) + - ", Name=" + interest.getName().toUri(); - m_logger.log(logLine, true, false); - } - - timer->expires_at(timer->expires_at() + - boost::posix_time::millisec(m_interestInterval.count())); - timer->async_wait(bind(&NdnTrafficClient::generateTraffic, this, timer)); - } - catch (const std::exception& e) { - m_logger.log("ERROR: "s + e.what(), true, true); - } - break; - } - } - if (patternId == m_trafficPatterns.size()) - { - timer->expires_at(timer->expires_at() + - boost::posix_time::millisec(m_interestInterval.count())); - timer->async_wait(bind(&NdnTrafficClient::generateTraffic, this, timer)); - } - } - } - - void - run() - { - boost::asio::signal_set signalSet(m_ioService, SIGINT, SIGTERM); - signalSet.async_wait(bind(&NdnTrafficClient::signalHandler, this)); - m_logger.initializeLog(m_instanceId); - initializeTrafficConfiguration(); - - if (m_nMaximumInterests == 0) { - logStatistics(); - m_logger.shutdownLogger(); + if (m_nMaximumInterests && m_nInterestsSent >= *m_nMaximumInterests) { return; } - boost::asio::deadline_timer deadlineTimer(m_ioService, - boost::posix_time::millisec(m_interestInterval.count())); - deadlineTimer.async_wait(bind(&NdnTrafficClient::generateTraffic, this, &deadlineTimer)); + static std::uniform_int_distribution<> trafficDist(1, 100); + int trafficKey = trafficDist(random::getRandomNumberEngine()); - try { - m_face.processEvents(); + int cumulativePercentage = 0; + std::size_t patternId = 0; + for (; patternId < m_trafficPatterns.size(); patternId++) { + cumulativePercentage += m_trafficPatterns[patternId].m_trafficPercentage; + if (trafficKey <= cumulativePercentage) { + auto interest = prepareInterest(patternId); + try { + m_nInterestsSent++; + m_trafficPatterns[patternId].m_nInterestsSent++; + auto sentTime = time::steady_clock::now(); + m_face.expressInterest(interest, + bind(&NdnTrafficClient::onData, this, _2, m_nInterestsSent, + m_trafficPatterns[patternId].m_nInterestsSent, patternId, sentTime), + bind(&NdnTrafficClient::onNack, this, _1, _2, m_nInterestsSent, + m_trafficPatterns[patternId].m_nInterestsSent, patternId), + bind(&NdnTrafficClient::onTimeout, this, _1, m_nInterestsSent, + m_trafficPatterns[patternId].m_nInterestsSent, patternId)); + + if (!m_wantQuiet) { + auto logLine = "Sending Interest - PatternType=" + to_string(patternId + 1) + + ", GlobalID=" + to_string(m_nInterestsSent) + + ", LocalID=" + to_string(m_trafficPatterns[patternId].m_nInterestsSent) + + ", Name=" + interest.getName().toUri(); + m_logger.log(logLine, true, false); + } + + timer.expires_at(timer.expires_at() + + boost::posix_time::millisec(m_interestInterval.count())); + timer.async_wait([this, &timer] (auto&&...) { this->generateTraffic(timer); }); + } + catch (const std::exception& e) { + m_logger.log("ERROR: "s + e.what(), true, true); + } + break; + } } - catch (const std::exception& e) { - m_logger.log("ERROR: "s + e.what(), true, true); - m_logger.shutdownLogger(); - m_hasError = true; - m_ioService.stop(); + if (patternId == m_trafficPatterns.size()) { + timer.expires_at(timer.expires_at() + + boost::posix_time::millisec(m_interestInterval.count())); + timer.async_wait([this, &timer] (auto&&...) { this->generateTraffic(timer); }); } } + void + stop() + { + if (m_nContentInconsistencies > 0 || m_nInterestsSent != m_nInterestsReceived) { + m_hasError = true; + } + + logStatistics(); + m_face.shutdown(); + m_ioService.stop(); + } + private: - std::string m_programName; - Logger m_logger; - std::string m_instanceId; - bool m_hasError; - bool m_hasQuietLogging; - time::milliseconds m_interestInterval; - int m_nMaximumInterests; - std::string m_configurationFile; boost::asio::io_service m_ioService; + boost::asio::signal_set m_signalSet; + Logger m_logger; Face m_face; + + std::string m_configurationFile; + optional m_nMaximumInterests; + time::milliseconds m_interestInterval = 1_s; + bool m_wantQuiet = false; + std::vector m_trafficPatterns; std::vector m_nonces; - int m_nInterestsSent; - int m_nInterestsReceived; - int m_nNacks; - int m_nContentInconsistencies; + uint64_t m_nInterestsSent = 0; + uint64_t m_nInterestsReceived = 0; + uint64_t m_nNacks = 0; + uint64_t m_nContentInconsistencies = 0; // RTT is stored as milliseconds with fractional sub-milliseconds precision - double m_minimumInterestRoundTripTime; - double m_maximumInterestRoundTripTime; - double m_totalInterestRoundTripTime; + double m_minimumInterestRoundTripTime = std::numeric_limits::max(); + double m_maximumInterestRoundTripTime = 0; + double m_totalInterestRoundTripTime = 0; + + bool m_hasError = false; }; } // namespace ndn +static void +usage(std::ostream& os, const std::string& programName, const po::options_description& desc) +{ + os << "Usage: " << programName << " [options] \n" + << "\n" + << "Generate Interest traffic as per provided Traffic_Configuration_File.\n" + << "Interests are continuously generated unless a total number is specified.\n" + << "Set the environment variable NDN_TRAFFIC_LOGFOLDER to redirect output to a log file.\n" + << "\n" + << desc; +} + int main(int argc, char* argv[]) { - std::srand(std::time(nullptr)); + std::string configFile; - ndn::NdnTrafficClient client(argv[0]); - int option; - while ((option = getopt(argc, argv, "hqi:c:")) != -1) { - switch (option) { - case 'h': - client.usage(); - break; - case 'i': - client.setInterestInterval(atoi(optarg)); - break; - case 'c': - client.setMaximumInterests(atoi(optarg)); - break; - case 'q': - client.setQuietLogging(); - break; - default: - client.usage(); - break; - } + po::options_description visibleOptions("Options"); + visibleOptions.add_options() + ("help,h", "print this help message and exit") + ("count,c", po::value(), "total number of Interests to be generated") + ("interval,i", po::value()->default_value(1000), + "Interest generation interval in milliseconds") + ("quiet,q", po::bool_switch(), "turn off logging of Interest generation/Data reception") + ; + + po::options_description hiddenOptions; + hiddenOptions.add_options() + ("config-file", po::value(&configFile)) + ; + + po::positional_options_description posOptions; + posOptions.add("config-file", -1); + + po::options_description allOptions; + allOptions.add(visibleOptions).add(hiddenOptions); + + po::variables_map vm; + try { + po::store(po::command_line_parser(argc, argv).options(allOptions).positional(posOptions).run(), vm); + po::notify(vm); + } + catch (const po::error& e) { + std::cerr << "ERROR: " << e.what() << std::endl; + return 2; + } + catch (const boost::bad_any_cast& e) { + std::cerr << "ERROR: " << e.what() << std::endl; + return 2; } - argc -= optind; - argv += optind; + if (vm.count("help") > 0) { + usage(std::cout, argv[0], visibleOptions); + return 0; + } - if (!argc) - client.usage(); + if (configFile.empty()) { + usage(std::cerr, argv[0], visibleOptions); + return 2; + } + + ndn::NdnTrafficClient client(configFile); + + if (vm.count("count") > 0) { + int count = vm["count"].as(); + if (count < 0) { + std::cerr << "ERROR: the argument for option '--count' cannot be negative" << std::endl; + return 2; + } + client.setMaximumInterests(static_cast(count)); + } + + if (vm.count("interval") > 0) { + ndn::time::milliseconds interval(vm["interval"].as()); + if (interval <= ndn::time::milliseconds::zero()) { + std::cerr << "ERROR: the argument for option '--interval' must be positive" << std::endl; + return 2; + } + client.setInterestInterval(interval); + } + + if (vm["quiet"].as()) { + client.setQuietLogging(); + } - client.setConfigurationFile(argv[0]); client.run(); - return client.hasError() ? EXIT_FAILURE : EXIT_SUCCESS; + return client.hasError() ? 1 : 0; } diff --git a/src/ndn-traffic-server.cpp b/src/ndn-traffic-server.cpp index 83ea6db..a6d8fef 100644 --- a/src/ndn-traffic-server.cpp +++ b/src/ndn-traffic-server.cpp @@ -1,6 +1,6 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2014-2018 Arizona Board of Regents. + * Copyright (c) 2014-2019, Arizona Board of Regents. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,23 +19,29 @@ */ #include "logger.hpp" +#include "util.hpp" +#include #include #include #include #include +#include + +#include +#include +#include +#include #include #include -#include #include +#include +#include +#include +#include -#include -#include -#include -#include -#include -#include +namespace po = boost::program_options; namespace ndn { @@ -43,180 +49,14 @@ class NdnTrafficServer : boost::noncopyable { public: explicit - NdnTrafficServer(const char* programName) - : m_logger("NdnTrafficServer") - , m_programName(programName) - , m_hasError(false) - , m_hasQuietLogging(false) - , m_nRegistrationsFailed(0) - , m_nMaximumInterests(-1) - , m_nInterestsReceived(0) - , m_contentDelay(time::milliseconds(-1)) - , m_instanceId(to_string(std::rand())) + NdnTrafficServer(const std::string& configFile) + : m_signalSet(m_ioService, SIGINT, SIGTERM) + , m_logger("NdnTrafficServer") , m_face(m_ioService) - , m_signalSet(m_ioService, SIGINT, SIGTERM) + , m_configurationFile(configFile) { } - class DataTrafficConfiguration - { - public: - DataTrafficConfiguration() - : m_contentType(-1) - , m_freshnessPeriod(time::milliseconds(-1)) - , m_contentBytes(-1) - , m_contentDelay(time::milliseconds(-1)) - , m_nInterestsReceived(0) - , m_signingInfo() - { - } - - void - printTrafficConfiguration(Logger& logger) - { - std::stringstream detail; - - if (!m_name.empty()) { - detail << "Name=" << m_name << ", "; - } - if (m_contentType >= 0) { - detail << "ContentType=" << to_string(m_contentType) << ", "; - } - if (m_freshnessPeriod >= time::milliseconds(0)) { - detail << "FreshnessPeriod=" << - to_string(static_cast(m_freshnessPeriod.count())) << ", "; - } - if (m_contentBytes >= 0) { - detail << "ContentBytes=" << to_string(m_contentBytes) << ", "; - } - if (m_contentDelay >= time::milliseconds(0)) { - detail << "ContentDelay=" << to_string(m_contentDelay.count()) << ", "; - } - if (!m_content.empty()) { - detail << "Content=" << m_content << ", "; - } - detail << "SigningInfo=" << m_signingInfo; - - logger.log(detail.str(), false, false); - } - - bool - extractParameterValue(const std::string& detail, std::string& parameter, std::string& value) - { - std::string allowedCharacters = ":/+._-%"; - std::size_t i = 0; - - parameter = ""; - value = ""; - while (detail[i] != '=' && i < detail.length()) { - parameter += detail[i]; - i++; - } - if (i == detail.length()) - return false; - - i++; - while ((std::isalnum(detail[i]) || - allowedCharacters.find(detail[i]) != std::string::npos) && - i < detail.length()) { - value += detail[i]; - i++; - } - - if (parameter.empty() || value.empty()) - return false; - else - return true; - } - - bool - processConfigurationDetail(const std::string& detail, - Logger& logger, - int lineNumber) - { - std::string parameter, value; - if (extractParameterValue(detail, parameter, value)) { - if (parameter == "Name") { - m_name = value; - } - else if (parameter == "ContentType") { - m_contentType = std::stoi(value); - } - else if (parameter == "FreshnessPeriod") { - m_freshnessPeriod = time::milliseconds(std::stoi(value)); - } - else if (parameter == "ContentDelay") { - m_contentDelay = time::milliseconds(std::stoi(value)); - } - else if (parameter == "ContentBytes") { - m_contentBytes = std::stoi(value); - } - else if (parameter == "Content") { - m_content = value; - } - else if (parameter == "SigningInfo") { - m_signingInfo = security::SigningInfo(value); - } - else { - logger.log("Line " + to_string(lineNumber) + - " \t- Invalid Parameter='" + parameter + "'", false, true); - } - } - else { - logger.log("Line " + to_string(lineNumber) + - " \t- Improper Traffic Configuration Line - " + detail, false, true); - return false; - } - - return true; - } - - bool - checkTrafficDetailCorrectness() - { - return true; - } - - private: - std::string m_name; - int m_contentType; - time::milliseconds m_freshnessPeriod; - int m_contentBytes; - time::milliseconds m_contentDelay; - std::string m_content; - int m_nInterestsReceived; - security::SigningInfo m_signingInfo; - - friend class NdnTrafficServer; - }; - - void - usage() const - { - std::cout << "Usage:\n" - << " " << m_programName << " [options] \n" - << "\n" - << "Respond to Interests as per provided Traffic Configuration File.\n" - << "Multiple prefixes can be configured for handling.\n" - << "Set environment variable NDN_TRAFFIC_LOGFOLDER to redirect output to a log file.\n" - << "\n" - << "Options:\n" - << " [-d interval] - set delay before responding to interest, in milliseconds\n" - << " [-c count] - specify maximum number of interests to be satisfied\n" - << " [-q] - quiet mode: no interest reception/data generation logging\n" - << " [-h] - print this help text and exit\n"; - exit(EXIT_FAILURE); - } - - void - setMaximumInterests(int maximumInterests) - { - if (maximumInterests < 0) { - usage(); - } - m_nMaximumInterests = maximumInterests; - } - bool hasError() const { @@ -224,37 +64,148 @@ public: } void - setContentDelay(int contentDelay) + setMaximumInterests(uint64_t maxInterests) { - if (contentDelay < 0) { - usage(); - } - m_contentDelay = time::milliseconds(contentDelay); + m_nMaximumInterests = maxInterests; } void - setConfigurationFile(const char* configurationFile) + setContentDelay(time::milliseconds delay) { - m_configurationFile = configurationFile; + BOOST_ASSERT(delay >= 0_ms); + m_contentDelay = delay; } void setQuietLogging() { - m_hasQuietLogging = true; + m_wantQuiet = true; } void - signalHandler() + run() { - logStatistics(); + m_logger.initializeLog(to_string(random::generateWord32())); + initializeTrafficConfiguration(); - m_face.shutdown(); - m_ioService.stop(); + if (m_nMaximumInterests == 0) { + logStatistics(); + return; + } - exit(m_hasError ? EXIT_FAILURE : EXIT_SUCCESS); + m_signalSet.async_wait([this] (const boost::system::error_code&, int) { + if (m_nMaximumInterests && m_nInterestsReceived < *m_nMaximumInterests) { + m_hasError = true; + } + stop(); + }); + + for (std::size_t id = 0; id < m_trafficPatterns.size(); id++) { + m_registeredPrefixes.push_back( + m_face.setInterestFilter(m_trafficPatterns[id].m_name, + [=] (auto&&, const auto& interest) { this->onInterest(interest, id); }, + nullptr, + [=] (auto&&, const auto& reason) { this->onRegisterFailed(reason, id); })); + } + + try { + m_face.processEvents(); + } + catch (const std::exception& e) { + m_logger.log("ERROR: "s + e.what(), true, true); + m_hasError = true; + m_ioService.stop(); + } } +private: + class DataTrafficConfiguration + { + public: + void + printTrafficConfiguration(Logger& logger) const + { + std::ostringstream os; + + if (!m_name.empty()) { + os << "Name=" << m_name << ", "; + } + if (m_contentDelay >= 0_ms) { + os << "ContentDelay=" << m_contentDelay.count() << ", "; + } + if (m_freshnessPeriod >= 0_ms) { + os << "FreshnessPeriod=" << m_freshnessPeriod.count() << ", "; + } + if (m_contentType) { + os << "ContentType=" << *m_contentType << ", "; + } + if (m_contentLength) { + os << "ContentBytes=" << *m_contentLength << ", "; + } + if (!m_content.empty()) { + os << "Content=" << m_content << ", "; + } + os << "SigningInfo=" << m_signingInfo; + + logger.log(os.str(), false, false); + } + + bool + processConfigurationDetail(const std::string& detail, Logger& logger, int lineNumber) + { + std::string parameter, value; + if (parseParameterAndValue(detail, parameter, value)) { + if (parameter == "Name") { + m_name = value; + } + else if (parameter == "ContentDelay") { + m_contentDelay = time::milliseconds(std::stoul(value)); + } + else if (parameter == "FreshnessPeriod") { + m_freshnessPeriod = time::milliseconds(std::stoul(value)); + } + else if (parameter == "ContentType") { + m_contentType = std::stoul(value); + } + else if (parameter == "ContentBytes") { + m_contentLength = std::stoul(value); + } + else if (parameter == "Content") { + m_content = value; + } + else if (parameter == "SigningInfo") { + m_signingInfo = security::SigningInfo(value); + } + else { + logger.log("Line " + to_string(lineNumber) + + " - Unknown parameter '" + parameter + "'", false, true); + } + } + else { + logger.log("Line " + to_string(lineNumber) + + " - Invalid traffic configuration line: " + detail, false, true); + return false; + } + return true; + } + + bool + checkTrafficDetailCorrectness() const + { + return true; + } + + public: + std::string m_name; + time::milliseconds m_contentDelay = -1_ms; + time::milliseconds m_freshnessPeriod = -1_ms; + optional m_contentType; + optional m_contentLength; + std::string m_content; + security::SigningInfo m_signingInfo; + uint64_t m_nInterestsReceived = 0; + }; + void logStatistics() { @@ -264,20 +215,16 @@ public: m_logger.log("Total Interests Received = " + to_string(m_nInterestsReceived), false, true); - if (m_nInterestsReceived < m_nMaximumInterests) { - m_hasError = true; - } - for (std::size_t patternId = 0; patternId < m_trafficPatterns.size(); patternId++) { m_logger.log("\nTraffic Pattern Type #" + to_string(patternId + 1), false, true); m_trafficPatterns[patternId].printTrafficConfiguration(m_logger); - m_logger.log("Total Interests Received = " + to_string( - m_trafficPatterns[patternId].m_nInterestsReceived) + "\n", false, true); + m_logger.log("Total Interests Received = " + + to_string(m_trafficPatterns[patternId].m_nInterestsReceived) + "\n", false, true); } } bool - checkTrafficPatternCorrectness() + checkTrafficPatternCorrectness() const { return true; } @@ -285,13 +232,13 @@ public: void parseConfigurationFile() { - std::string patternLine; std::ifstream patternFile; - m_logger.log("Analyzing Traffic Configuration File: " + m_configurationFile, true, true); + m_logger.log("Parsing traffic configuration file: " + m_configurationFile, true, true); - patternFile.open(m_configurationFile.c_str()); + patternFile.open(m_configurationFile.data()); if (patternFile.is_open()) { int lineNumber = 0; + std::string patternLine; while (getline(patternFile, patternLine)) { lineNumber++; if (std::isalpha(patternLine[0])) { @@ -320,209 +267,241 @@ public: patternFile.close(); if (!checkTrafficPatternCorrectness()) { - m_logger.log("ERROR - Traffic Configuration Provided Is Not Proper - " + + m_logger.log("ERROR: Traffic configuration provided is not proper - " + m_configurationFile, false, true); - exit(EXIT_FAILURE); + exit(2); } - m_logger.log("Traffic Configuration File Processing Completed\n", true, false); + m_logger.log("Traffic configuration file processing completed.\n", true, false); for (std::size_t patternId = 0; patternId < m_trafficPatterns.size(); patternId++) { - m_logger.log("Traffic Pattern Type #" + - to_string(patternId + 1), false, false); + m_logger.log("Traffic Pattern Type #" + to_string(patternId + 1), false, false); m_trafficPatterns[patternId].printTrafficConfiguration(m_logger); m_logger.log("", false, false); } } else { - m_logger.log("ERROR - Unable To Open Traffic Configuration File: " + - m_configurationFile, false, true); - exit(EXIT_FAILURE); + m_logger.log("ERROR: Unable to open traffic configuration file: " + m_configurationFile, false, true); + exit(2); } } void initializeTrafficConfiguration() { - if (boost::filesystem::exists(boost::filesystem::path(m_configurationFile))) { - if (boost::filesystem::is_regular_file(boost::filesystem::path(m_configurationFile))) { + namespace fs = boost::filesystem; + + fs::path configPath(m_configurationFile); + if (fs::exists(configPath)) { + if (fs::is_regular_file(configPath)) { parseConfigurationFile(); } else { - m_logger.log("ERROR - Traffic Configuration File Is Not A Regular File: " + + m_logger.log("ERROR: Traffic configuration file is not a regular file: " + m_configurationFile, false, true); - exit(EXIT_FAILURE); + exit(2); } } else { - m_logger.log("ERROR - Traffic Configuration File Does Not Exist: " + + m_logger.log("ERROR: Traffic configuration file does not exist: " + m_configurationFile, false, true); - exit(EXIT_FAILURE); + exit(2); } } static std::string - getRandomByteString(std::size_t randomSize) + getRandomByteString(std::size_t length) { - std::string randomString; - for (std::size_t i = 0; i < randomSize; i++) { - randomString += static_cast(std::rand() % 128); + // per ISO C++ std, cannot instantiate uniform_int_distribution with char + static std::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + std::string s; + s.reserve(length); + for (std::size_t i = 0; i < length; i++) { + s += static_cast(dist(random::getRandomNumberEngine())); } - return randomString; + return s; } void - onInterest(const Name& name, const Interest& interest, int patternId) + onInterest(const Interest& interest, std::size_t patternId) { auto& pattern = m_trafficPatterns[patternId]; - if (m_nMaximumInterests < 0 || m_nInterestsReceived < m_nMaximumInterests) { + if (!m_nMaximumInterests || m_nInterestsReceived < *m_nMaximumInterests) { Data data(interest.getName()); - if (pattern.m_contentType >= 0) - data.setContentType(pattern.m_contentType); - - if (pattern.m_freshnessPeriod >= time::milliseconds(0)) + if (pattern.m_freshnessPeriod >= 0_ms) data.setFreshnessPeriod(pattern.m_freshnessPeriod); + if (pattern.m_contentType) + data.setContentType(*pattern.m_contentType); + std::string content; - if (pattern.m_contentBytes >= 0) - content = getRandomByteString(pattern.m_contentBytes); + if (pattern.m_contentLength > 0) + content = getRandomByteString(*pattern.m_contentLength); if (!pattern.m_content.empty()) content = pattern.m_content; + data.setContent(reinterpret_cast(content.data()), content.length()); - data.setContent(reinterpret_cast(content.c_str()), content.length()); m_keyChain.sign(data, pattern.m_signingInfo); m_nInterestsReceived++; pattern.m_nInterestsReceived++; - if (!m_hasQuietLogging) { - std::string logLine = - "Interest Received - PatternType=" + to_string(patternId + 1) + - ", GlobalID=" + to_string(m_nInterestsReceived) + - ", LocalID=" + to_string(pattern.m_nInterestsReceived) + - ", Name=" + pattern.m_name; + if (!m_wantQuiet) { + auto logLine = "Interest received - PatternType=" + to_string(patternId + 1) + + ", GlobalID=" + to_string(m_nInterestsReceived) + + ", LocalID=" + to_string(pattern.m_nInterestsReceived) + + ", Name=" + pattern.m_name; m_logger.log(logLine, true, false); } - if (pattern.m_contentDelay > time::milliseconds(-1)) - usleep(pattern.m_contentDelay.count() * 1000); - if (m_contentDelay > time::milliseconds(-1)) - usleep(m_contentDelay.count() * 1000); + if (pattern.m_contentDelay > 0_ms) + boost::this_thread::sleep_for(pattern.m_contentDelay); + if (m_contentDelay > 0_ms) + boost::this_thread::sleep_for(m_contentDelay); + m_face.put(data); } - if (m_nMaximumInterests >= 0 && m_nInterestsReceived == m_nMaximumInterests) { + + if (m_nMaximumInterests && m_nInterestsReceived >= *m_nMaximumInterests) { logStatistics(); - for (auto registeredPrefix : m_registeredPrefixes) { - m_face.unsetInterestFilter(registeredPrefix); - } + m_registeredPrefixes.clear(); m_signalSet.cancel(); } } void - onRegisterFailed(const ndn::Name& prefix, const std::string& reason, int patternId) + onRegisterFailed(const std::string& reason, std::size_t patternId) { - std::string logLine; - logLine += "Prefix Registration Failed - PatternType=" + to_string(patternId + 1); - logLine += ", Name=" + m_trafficPatterns[patternId].m_name; + auto logLine = "Prefix registration failed - PatternType=" + to_string(patternId + 1) + + ", Name=" + m_trafficPatterns[patternId].m_name + + ", Reason=" + reason; m_logger.log(logLine, true, true); m_nRegistrationsFailed++; if (m_nRegistrationsFailed == m_trafficPatterns.size()) { m_hasError = true; - signalHandler(); + stop(); } } void - run() + stop() { - m_signalSet.async_wait(bind(&NdnTrafficServer::signalHandler, this)); - - m_logger.initializeLog(m_instanceId); - initializeTrafficConfiguration(); - if (m_nMaximumInterests == 0) { - logStatistics(); - return; - } - - for (std::size_t patternId = 0; patternId < m_trafficPatterns.size(); patternId++) { - m_registeredPrefixes.push_back( - m_face.setInterestFilter(m_trafficPatterns[patternId].m_name, - bind(&NdnTrafficServer::onInterest, this, _1, _2, patternId), - nullptr, - bind(&NdnTrafficServer::onRegisterFailed, this, _1, _2, patternId))); - } - - try { - m_face.processEvents(); - } - catch (const std::exception& e) { - m_logger.log("ERROR: " + std::string(e.what()), true, true); - m_hasError = true; - m_ioService.stop(); - } + logStatistics(); + m_face.shutdown(); + m_ioService.stop(); } private: - KeyChain m_keyChain; - Logger m_logger; - std::string m_programName; - bool m_hasError; - bool m_hasQuietLogging; - std::size_t m_nRegistrationsFailed; - int m_nMaximumInterests; - int m_nInterestsReceived; - time::milliseconds m_contentDelay; - std::string m_instanceId; - std::string m_configurationFile; - boost::asio::io_service m_ioService; - Face m_face; boost::asio::signal_set m_signalSet; + Logger m_logger; + Face m_face; + KeyChain m_keyChain; + + std::string m_configurationFile; + optional m_nMaximumInterests; + time::milliseconds m_contentDelay = 0_ms; + bool m_wantQuiet = false; + std::vector m_trafficPatterns; - std::vector m_registeredPrefixes; + std::vector m_registeredPrefixes; + uint64_t m_nRegistrationsFailed = 0; + uint64_t m_nInterestsReceived = 0; + bool m_hasError = false; }; } // namespace ndn +static void +usage(std::ostream& os, const std::string& programName, const po::options_description& desc) +{ + os << "Usage: " << programName << " [options] \n" + << "\n" + << "Respond to Interests as per provided Traffic_Configuration_File.\n" + << "Multiple prefixes can be configured for handling.\n" + << "Set the environment variable NDN_TRAFFIC_LOGFOLDER to redirect output to a log file.\n" + << "\n" + << desc; +} + int main(int argc, char* argv[]) { - std::srand(std::time(nullptr)); + std::string configFile; - ndn::NdnTrafficServer server(argv[0]); - int option; - while ((option = getopt(argc, argv, "hqc:d:")) != -1) { - switch (option) { - case 'h': - server.usage(); - break; - case 'c': - server.setMaximumInterests(atoi(optarg)); - break; - case 'd': - server.setContentDelay(atoi(optarg)); - break; - case 'q': - server.setQuietLogging(); - break; - default: - server.usage(); - break; + po::options_description visibleOptions("Options"); + visibleOptions.add_options() + ("help,h", "print this help message and exit") + ("count,c", po::value(), "maximum number of Interests to respond to") + ("delay,d", po::value()->default_value(0), + "wait this amount of milliseconds before responding to each Interest") + ("quiet,q", po::bool_switch(), "turn off logging of Interest reception/Data generation") + ; + + po::options_description hiddenOptions; + hiddenOptions.add_options() + ("config-file", po::value(&configFile)) + ; + + po::positional_options_description posOptions; + posOptions.add("config-file", -1); + + po::options_description allOptions; + allOptions.add(visibleOptions).add(hiddenOptions); + + po::variables_map vm; + try { + po::store(po::command_line_parser(argc, argv).options(allOptions).positional(posOptions).run(), vm); + po::notify(vm); + } + catch (const po::error& e) { + std::cerr << "ERROR: " << e.what() << std::endl; + return 2; + } + catch (const boost::bad_any_cast& e) { + std::cerr << "ERROR: " << e.what() << std::endl; + return 2; + } + + if (vm.count("help") > 0) { + usage(std::cout, argv[0], visibleOptions); + return 0; + } + + if (configFile.empty()) { + usage(std::cerr, argv[0], visibleOptions); + return 2; + } + + ndn::NdnTrafficServer server(configFile); + + if (vm.count("count") > 0) { + int count = vm["count"].as(); + if (count < 0) { + std::cerr << "ERROR: the argument for option '--count' cannot be negative" << std::endl; + return 2; } + server.setMaximumInterests(static_cast(count)); } - argc -= optind; - argv += optind; + if (vm.count("delay") > 0) { + ndn::time::milliseconds delay(vm["delay"].as()); + if (delay < ndn::time::milliseconds::zero()) { + std::cerr << "ERROR: the argument for option '--delay' cannot be negative" << std::endl; + return 2; + } + server.setContentDelay(delay); + } - if (!argc) { - server.usage(); + if (vm["quiet"].as()) { + server.setQuietLogging(); } - server.setConfigurationFile(argv[0]); server.run(); - return server.hasError() ? EXIT_FAILURE : EXIT_SUCCESS; + return server.hasError() ? 1 : 0; } diff --git a/src/util.hpp b/src/util.hpp new file mode 100644 index 0000000..65052d8 --- /dev/null +++ b/src/util.hpp @@ -0,0 +1,61 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2014-2019, Arizona Board of Regents. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Author: Jerald Paul Abraham + */ + +#ifndef NTG_UTIL_HPP +#define NTG_UTIL_HPP + +#include +#include + +namespace ndn { + +static inline bool +parseParameterAndValue(const std::string& input, std::string& parameter, std::string& value) +{ + static const std::string allowedCharacters = ":/+._-%"; + parameter = ""; + value = ""; + + std::size_t i = 0; + // parse the parameter name + while (i < input.length() && input[i] != '=') { + parameter += input[i]; + i++; + } + if (i == input.length()) { + return false; + } + + // skip the equal sign + i++; + + // parse the value + while (i < input.length() && + (std::isalnum(input[i]) || allowedCharacters.find(input[i]) != std::string::npos)) { + value += input[i]; + i++; + } + + return !parameter.empty() && !value.empty() && i == input.length(); +} + +} // namespace ndn + +#endif // NTG_UTIL_HPP