blob: 403bdafcf702706c4b94b73c46b55a9f79492833 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
/**
* Copyright (C) 2014 University of Arizona.
*
* GNU 3.0 License, see the LICENSE file for more information
*
* Author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
*/
#include <string>
#include <sstream>
#include <fstream>
#include <vector>
#include "logger.hpp"
#include <boost/asio.hpp>
#include <boost/filesystem.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <ndn-cpp-dev/face.hpp>
#include <ndn-cpp-dev/exclude.hpp>
#include <ndn-cpp-dev/name-component.hpp>
#include <ndn-cpp-dev/security/key-chain.hpp>
namespace ndn {
class NdnTrafficClient
{
public:
NdnTrafficClient( char* programName )
: m_logger("NDNTrafficClient")
, ioService_(new boost::asio::io_service)
, face_(ioService_)
, keyChain_()
{
std::srand(std::time(0));
instanceId_ = toString(std::rand());
programName_ = programName;
interestInterval_ = getDefaultInterestInterval();
interestCount_ = getDefaultInterestCount();
configurationFile_ = "";
totalInterestSent_ = 0;
totalInterestReceived_ = 0;
minimumInterestRoundTripTime_ = std::numeric_limits<double>::max();
maximumInterestRoundTripTime_ = 0;
totalInterestRoundTripTime_ = 0;
}
class InterestTrafficConfiguration
{
public:
InterestTrafficConfiguration()
{
trafficPercentage = -1;
name = "";
nameAppendBytes = -1;
nameAppendSequenceNumber = -1;
minSuffixComponents = -1;
maxSuffixComponents = -1;
excludeBefore = "";
excludeAfter = "";
excludeBeforeBytes = -1;
excludeAfterBytes = -1;
childSelector = -1;
mustBeFresh = -1;
nonceDuplicationPercentage = -1;
scope = -1;
interestLifetime = -1;
totalInterestSent = 0;
totalInterestReceived = 0;
minimumInterestRoundTripTime = std::numeric_limits<double>::max();
maximumInterestRoundTripTime = 0;
totalInterestRoundTripTime = 0;
}
void
printTrafficConfiguration( Logger& logger )
{
std::string detail;
detail = "";
if (trafficPercentage > 0)
detail += "TrafficPercentage="+toString(trafficPercentage)+", ";
if (name != "")
detail += "Name="+name+", ";
if (nameAppendBytes > 0)
detail += "NameAppendBytes="+toString(nameAppendBytes)+", ";
if (nameAppendSequenceNumber > 0)
detail += "NameAppendSequenceNumber="+toString(nameAppendSequenceNumber)+", ";
if (minSuffixComponents >= 0)
detail += "MinSuffixComponents="+toString(minSuffixComponents)+", ";
if (maxSuffixComponents >= 0)
detail += "MaxSuffixComponents="+toString(maxSuffixComponents)+", ";
if (excludeBefore != "")
detail += "ExcludeBefore="+excludeBefore+", ";
if (excludeAfter != "")
detail += "ExcludeAfter="+excludeAfter+", ";
if (excludeBeforeBytes > 0)
detail += "ExcludeBeforeBytes="+toString(excludeBeforeBytes)+", ";
if (excludeAfterBytes > 0)
detail += "ExcludeAfterBytes="+toString(excludeAfterBytes)+", ";
if (childSelector >= 0)
detail += "ChildSelector="+toString(childSelector)+", ";
if (mustBeFresh >= 0)
detail += "MustBeFresh="+toString(mustBeFresh)+", ";
if (nonceDuplicationPercentage > 0)
detail += "NonceDuplicationPercentage="+toString(nonceDuplicationPercentage)+", ";
if (scope >= 0)
detail += "Scope="+toString(scope)+", ";
if (interestLifetime >= 0)
detail += "InterestLifetime="+toString(interestLifetime)+", ";
if (detail.length() >= 2)
detail = detail.substr(0, detail.length()-2);
logger.log(detail, false, false);
}
bool
extractParameterValue( std::string detail, std::string& parameter, std::string& value )
{
int i;
std::string allowedCharacters = ":/+._-%";
parameter = "";
value = "";
i = 0;
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 == "" || value == "")
return false;
return true;
}
bool
processConfigurationDetail( std::string detail, Logger& logger, int lineNumber )
{
std::string parameter, value;
if (extractParameterValue(detail, parameter, value))
{
if (parameter == "TrafficPercentage")
trafficPercentage = toInteger(value);
else if (parameter == "Name")
name = value;
else if (parameter == "NameAppendBytes")
nameAppendBytes = toInteger(value);
else if (parameter == "NameAppendSequenceNumber")
nameAppendSequenceNumber = toInteger(value);
else if (parameter == "MinSuffixComponents")
minSuffixComponents = toInteger(value);
else if (parameter == "MaxSuffixComponents")
maxSuffixComponents = toInteger(value);
else if (parameter == "ExcludeBefore")
excludeBefore = value;
else if (parameter == "ExcludeAfter")
excludeAfter = value;
else if (parameter == "ExcludeBeforeBytes")
excludeBeforeBytes = toInteger(value);
else if (parameter == "ExcludeAfterBytes")
excludeAfterBytes = toInteger(value);
else if (parameter == "ChildSelector")
childSelector = toInteger(value);
else if (parameter == "MustBeFresh")
mustBeFresh = toInteger(value);
else if (parameter == "NonceDuplicationPercentage")
nonceDuplicationPercentage = toInteger(value);
else if (parameter == "Scope")
scope = toInteger(value);
else if (parameter == "InterestLifetime")
interestLifetime = toInteger(value);
else
logger.log("Line "+toString(lineNumber)+" \t- Invalid Parameter='"+parameter+"'", false, true);
}
else
{
logger.log("Line "+toString(lineNumber)+" \t- Improper Traffic Configuration Line- "+detail, false, true);
return false;
}
return true;
}
bool
checkTrafficDetailCorrectness()
{
return true;
}
int trafficPercentage;
std::string name;
int nameAppendBytes;
int nameAppendSequenceNumber;
int minSuffixComponents;
int maxSuffixComponents;
std::string excludeBefore;
std::string excludeAfter;
int excludeBeforeBytes;
int excludeAfterBytes;
int childSelector;
int mustBeFresh;
int nonceDuplicationPercentage;
int scope;
int interestLifetime;
int totalInterestSent;
int totalInterestReceived;
double minimumInterestRoundTripTime;
double maximumInterestRoundTripTime;
double totalInterestRoundTripTime;
};
int
getDefaultInterestLifetime()
{
return 4000;
}
static std::string
toString( int integerValue )
{
std::stringstream stream;
stream << integerValue;
return stream.str();
}
static std::string
toString( double doubleValue )
{
std::stringstream stream;
stream << doubleValue;
return stream.str();
}
static int
toInteger( std::string stringValue )
{
int integerValue;
std::stringstream stream(stringValue);
stream >> integerValue;
return integerValue;
}
void
usage()
{
std::cout << "\nUsage: " << programName_ << " [options] <Traffic_Configuration_File>\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 for redirecting output to a log.\n"
" [-i interval] - set interest generation interval in milliseconds (minimum "
<< getDefaultInterestInterval() << " milliseconds)\n"
" [-c count] - set total number of interests to be generated\n"
" [-h] - print help and exit\n\n";
exit(1);
}
int
getDefaultInterestInterval()
{
return 1000;
}
int
getDefaultInterestCount()
{
return -1;
}
void
setInterestInterval( int interestInterval )
{
if (interestInterval < 0)
usage();
interestInterval_ = interestInterval;
}
void
setInterestCount( int interestCount )
{
if (interestCount < 0)
usage();
interestCount_ = interestCount;
}
void
setConfigurationFile( char* configurationFile )
{
configurationFile_ = configurationFile;
}
void
signalHandler()
{
m_logger.shutdownLogger();
face_.shutdown();
ioService_.reset();
logStatistics();
exit(1);
}
void
logStatistics()
{
int patternId;
double loss, average;
m_logger.log("\n\n== Interest Traffic Report ==\n", false, true);
m_logger.log("Total Traffic Pattern Types = "+toString((int)trafficPattern_.size()), false, true);
m_logger.log("Total Interests Sent = "+toString(totalInterestSent_), false, true);
m_logger.log("Total Responses Received = "+toString(totalInterestReceived_), false, true);
if (totalInterestSent_ > 0)
loss = (totalInterestSent_-totalInterestReceived_)*100.0/totalInterestSent_;
else
loss = 0;
m_logger.log("Total Interest Loss = "+toString(loss)+"%", false, true);
if (totalInterestReceived_ > 0)
average = totalInterestRoundTripTime_/totalInterestReceived_;
else
average = 0;
m_logger.log("Total Round Trip Time = "+toString(totalInterestRoundTripTime_)+"ms", false, true);
m_logger.log("Average Round Trip Time = "+toString(average)+"ms\n", false, true);
for (patternId=0; patternId<trafficPattern_.size(); patternId++)
{
m_logger.log("Traffic Pattern Type #"+toString(patternId+1), false, true);
trafficPattern_[patternId].printTrafficConfiguration(m_logger);
m_logger.log("Total Interests Sent = "+toString(trafficPattern_[patternId].totalInterestSent), false, true);
m_logger.log("Total Responses Received = "+toString(trafficPattern_[patternId].totalInterestReceived), false, true);
if (trafficPattern_[patternId].totalInterestSent > 0)
{
loss = (trafficPattern_[patternId].totalInterestSent-trafficPattern_[patternId].totalInterestReceived);
loss *= 100.0;
loss /= trafficPattern_[patternId].totalInterestSent;
}
else
loss = 0;
m_logger.log("Total Interest Loss = "+toString(loss)+"%", false, true);
if (trafficPattern_[patternId].totalInterestReceived > 0)
average = trafficPattern_[patternId].totalInterestRoundTripTime/trafficPattern_[patternId].totalInterestReceived;
else
average = 0;
m_logger.log("Total Round Trip Time = "+toString(trafficPattern_[patternId].totalInterestRoundTripTime)+"ms", false, true);
m_logger.log("Average Round Trip Time = "+toString(average)+"ms\n", false, true);
}
}
bool
checkTrafficPatternCorrectness()
{
return true;
}
void
analyzeConfigurationFile()
{
int patternId;
int lineNumber;
bool skipLine;
std::string patternLine;
std::ifstream patternFile;
m_logger.log("Analyzing Traffic Configuration File: " + configurationFile_, true, true);
patternFile.open(configurationFile_.c_str());
if (patternFile.is_open())
{
patternId = 0;
lineNumber = 0;
while (getline(patternFile, patternLine))
{
lineNumber++;
if (std::isalpha(patternLine[0]))
{
InterestTrafficConfiguration interestData;
skipLine = false;
patternId++;
if (interestData.processConfigurationDetail(patternLine, m_logger, lineNumber))
{
while (getline(patternFile, patternLine) && std::isalpha(patternLine[0]))
{
lineNumber++;
if (!interestData.processConfigurationDetail(patternLine, m_logger, lineNumber))
{
skipLine = true;
break;
}
}
lineNumber++;
}
else
skipLine = true;
if( !skipLine )
{
if (interestData.checkTrafficDetailCorrectness())
trafficPattern_.push_back(interestData);
}
}
}
patternFile.close();
if (!checkTrafficPatternCorrectness())
{
m_logger.log("ERROR - Traffic Configuration Provided Is Not Proper- " + configurationFile_, false, true);
m_logger.shutdownLogger();
exit(1);
}
m_logger.log("Traffic Configuration File Processing Completed\n", true, false);
for (patternId=0; patternId<trafficPattern_.size(); patternId++)
{
m_logger.log("Traffic Pattern Type #"+toString(patternId+1), false, false);
trafficPattern_[patternId].printTrafficConfiguration(m_logger);
m_logger.log("", false, false);
}
}
else
{
m_logger.log("ERROR - Unable To Open Traffic Configuration File: " + configurationFile_, false, true);
m_logger.shutdownLogger();
exit(1);
}
}
void
initializeTrafficConfiguration()
{
if (boost::filesystem::exists(boost::filesystem::path(configurationFile_)))
{
if(boost::filesystem::is_regular_file(boost::filesystem::path(configurationFile_)))
{
analyzeConfigurationFile();
}
else
{
m_logger.log("ERROR - Traffic Configuration File Is Not A Regular File: " + configurationFile_, false, true);
m_logger.shutdownLogger();
exit(1);
}
}
else
{
m_logger.log("ERROR - Traffic Configuration File Does Not Exist: " + configurationFile_, false, true);
m_logger.shutdownLogger();
exit(1);
}
}
int
getOldNonce()
{
int randomNonceKey;
if (nonceList_.size() == 0)
return getNewNonce();
std::srand(std::time(0));
randomNonceKey = std::rand() % nonceList_.size();
return nonceList_[randomNonceKey];
}
int
getNewNonce()
{
int randomNonceKey, i;
bool isOld;
isOld = true;
std::srand(std::time(0));
do
{
randomNonceKey = std::rand();
isOld = false;
for (i=0; i<nonceList_.size(); i++)
if (nonceList_[i] == randomNonceKey)
isOld = true;
} while(isOld);
nonceList_.push_back(randomNonceKey);
return randomNonceKey;
}
static std::string
getRandomByteString( int randomSize )
{
int i;
std::string characterSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw0123456789";
std::string randomData;
for (i=0; i<randomSize; i++)
randomData += characterSet[std::rand() % characterSet.length()];
return randomData;
}
void
onData( ndn::Face &face,
const ndn::Interest& interest,
ndn::Data& data,
int globalReference,
int localReference,
int patternId,
boost::posix_time::ptime sentTime )
{
double roundTripTime;
std::string logLine;
logLine = "";
logLine += "Data Received - PatternType="+toString(patternId+1);
logLine += ", GlobalID="+toString(globalReference);
logLine += ", LocalID="+toString(localReference);
logLine += ", Name="+interest.getName().toUri();
m_logger.log(logLine, true, false);
boost::posix_time::time_duration roundTripDuration;
totalInterestReceived_++;
trafficPattern_[patternId].totalInterestReceived++;
roundTripDuration = boost::posix_time::microsec_clock::local_time() - sentTime;
roundTripTime = roundTripDuration.total_microseconds()/1000.0;
if (minimumInterestRoundTripTime_ > roundTripTime)
minimumInterestRoundTripTime_ = roundTripTime;
if (maximumInterestRoundTripTime_ < roundTripTime)
maximumInterestRoundTripTime_ = roundTripTime;
if (trafficPattern_[patternId].minimumInterestRoundTripTime > roundTripTime)
trafficPattern_[patternId].minimumInterestRoundTripTime = roundTripTime;
if (trafficPattern_[patternId].maximumInterestRoundTripTime < roundTripTime)
trafficPattern_[patternId].maximumInterestRoundTripTime = roundTripTime;
totalInterestRoundTripTime_ += roundTripTime;
trafficPattern_[patternId].totalInterestRoundTripTime += roundTripTime;
if (totalInterestSent_ == interestCount_)
signalHandler();
}
void
onTimeout( ndn::Face &face,
const ndn::Interest& interest,
int globalReference,
int localReference,
int patternId)
{
std::string logLine;
logLine = "";
logLine += "Interest Timed Out - PatternType="+toString(patternId+1);
logLine += ", GlobalID="+toString(globalReference);
logLine += ", LocalID="+toString(localReference);
logLine += ", Name="+interest.getName().toUri();
m_logger.log(logLine, true, false);
if (totalInterestSent_ == interestCount_)
signalHandler();
}
void
generateTraffic( const boost::system::error_code& errorCode,
boost::asio::deadline_timer* deadlineTimer )
{
if ((interestCount_ < 0) || (totalInterestSent_ < interestCount_))
{
int trafficKey, patternId, cumulativePercentage;
std::srand(std::time(0));
trafficKey = std::rand() % 100;
cumulativePercentage = 0;
for (patternId=0; patternId<trafficPattern_.size(); patternId++)
{
cumulativePercentage += trafficPattern_[patternId].trafficPercentage;
if (trafficKey <= cumulativePercentage)
{
Name interestName(trafficPattern_[patternId].name);
if (trafficPattern_[patternId].nameAppendBytes > 0)
interestName.append(getRandomByteString(trafficPattern_[patternId].nameAppendBytes));
if (trafficPattern_[patternId].nameAppendSequenceNumber >= 0)
{
interestName.append(toString(trafficPattern_[patternId].nameAppendSequenceNumber));
trafficPattern_[patternId].nameAppendSequenceNumber++;
}
Interest interest(interestName);
if (trafficPattern_[patternId].minSuffixComponents >= 0)
interest.setMinSuffixComponents(trafficPattern_[patternId].minSuffixComponents);
if (trafficPattern_[patternId].maxSuffixComponents >= 0)
interest.setMaxSuffixComponents(trafficPattern_[patternId].maxSuffixComponents);
Exclude exclude;
if (trafficPattern_[patternId].excludeBefore != "" && trafficPattern_[patternId].excludeAfter != "")
{
exclude.excludeRange(name::Component(trafficPattern_[patternId].excludeAfter),
name::Component(trafficPattern_[patternId].excludeBefore));
interest.setExclude(exclude);
}
else if (trafficPattern_[patternId].excludeBefore != "")
{
exclude.excludeBefore(name::Component(trafficPattern_[patternId].excludeBefore));
interest.setExclude(exclude);
}
else if (trafficPattern_[patternId].excludeAfter != "")
{
exclude.excludeAfter(name::Component(trafficPattern_[patternId].excludeAfter));
interest.setExclude(exclude);
}
if (trafficPattern_[patternId].excludeBeforeBytes > 0 && trafficPattern_[patternId].excludeAfterBytes > 0)
{
exclude.excludeRange(name::Component(getRandomByteString(trafficPattern_[patternId].excludeAfterBytes)),
name::Component(getRandomByteString(trafficPattern_[patternId].excludeBeforeBytes)));
interest.setExclude(exclude);
}
else if (trafficPattern_[patternId].excludeBeforeBytes > 0)
{
exclude.excludeBefore(name::Component(getRandomByteString(trafficPattern_[patternId].excludeBeforeBytes)));
interest.setExclude(exclude);
}
else if (trafficPattern_[patternId].excludeAfterBytes > 0)
{
exclude.excludeAfter(name::Component(getRandomByteString(trafficPattern_[patternId].excludeAfterBytes)));
interest.setExclude(exclude);
}
if (trafficPattern_[patternId].childSelector >= 0)
interest.setChildSelector(trafficPattern_[patternId].childSelector);
if (trafficPattern_[patternId].mustBeFresh == 0)
interest.setMustBeFresh(false);
else if (trafficPattern_[patternId].mustBeFresh > 0)
interest.setMustBeFresh(true);
if (trafficPattern_[patternId].nonceDuplicationPercentage > 0)
{
int duplicationKey;
std::srand(std::time(0));
duplicationKey = std::rand() % 100;
if (trafficPattern_[patternId].nonceDuplicationPercentage <= duplicationKey)
interest.setNonce(getOldNonce());
else
interest.setNonce(getNewNonce());
}
else
interest.setNonce(getNewNonce());
if (trafficPattern_[patternId].scope >= 0)
interest.setScope(trafficPattern_[patternId].scope);
if (trafficPattern_[patternId].interestLifetime >= 0)
interest.setInterestLifetime(time::milliseconds(trafficPattern_[patternId].interestLifetime));
else
interest.setInterestLifetime(time::milliseconds(getDefaultInterestLifetime()));
try {
totalInterestSent_++;
trafficPattern_[patternId].totalInterestSent++;
boost::posix_time::ptime sentTime;
sentTime = boost::posix_time::microsec_clock::local_time();
face_.expressInterest(interest,
func_lib::bind( &NdnTrafficClient::onData,
this, boost::ref(face_),
_1, _2, totalInterestSent_,
trafficPattern_[patternId].totalInterestSent,
patternId,
sentTime),
func_lib::bind( &NdnTrafficClient::onTimeout,
this, boost::ref(face_),
_1, totalInterestSent_,
trafficPattern_[patternId].totalInterestSent,
patternId));
std::string logLine;
logLine = "";
logLine += "Sending Interest - PatternType="+toString(patternId+1);
logLine += ", GlobalID="+toString(totalInterestSent_);
logLine += ", LocalID="+toString(trafficPattern_[patternId].totalInterestSent);
logLine += ", Name="+interest.getName().toUri();
m_logger.log(logLine, true, false);
deadlineTimer->expires_at(deadlineTimer->expires_at() +
boost::posix_time::millisec(interestInterval_));
deadlineTimer->async_wait(boost::bind(&NdnTrafficClient::generateTraffic,
this,
boost::asio::placeholders::error,
deadlineTimer));
}
catch (std::exception &e) {
m_logger.log("ERROR: "+(std::string)e.what(), true, true);
}
break;
}
}
if (patternId==trafficPattern_.size())
{
deadlineTimer->expires_at(deadlineTimer->expires_at() +
boost::posix_time::millisec(interestInterval_));
deadlineTimer->async_wait(boost::bind(&NdnTrafficClient::generateTraffic,
this,
boost::asio::placeholders::error,
deadlineTimer));
}
}
}
void
initialize()
{
boost::asio::signal_set signalSet(*ioService_, SIGINT, SIGTERM);
signalSet.async_wait(boost::bind(&NdnTrafficClient::signalHandler, this));
m_logger.initializeLog(instanceId_);
initializeTrafficConfiguration();
boost::asio::deadline_timer deadlineTimer(*ioService_,
boost::posix_time::millisec(interestInterval_));
deadlineTimer.async_wait(boost::bind(&NdnTrafficClient::generateTraffic,
this,
boost::asio::placeholders::error,
&deadlineTimer));
try {
face_.processEvents();
}
catch(std::exception &e) {
m_logger.log("ERROR: "+(std::string)e.what(), true, true);
m_logger.shutdownLogger();
}
}
private:
KeyChain keyChain_;
std::string programName_;
std::string instanceId_;
int interestInterval_;
int interestCount_;
Logger m_logger;
std::string configurationFile_;
ptr_lib::shared_ptr<boost::asio::io_service> ioService_;
Face face_;
std::vector<InterestTrafficConfiguration> trafficPattern_;
std::vector<int> nonceList_;
int totalInterestSent_;
int totalInterestReceived_;
double minimumInterestRoundTripTime_;
double maximumInterestRoundTripTime_;
double totalInterestRoundTripTime_;
};
} // namespace ndn
int main( int argc, char* argv[] )
{
int option;
ndn::NdnTrafficClient ndnTrafficClient (argv[0]);
while ((option = getopt(argc, argv, "hi:c:")) != -1) {
switch (option) {
case 'h' :
ndnTrafficClient.usage();
break;
case 'i' :
ndnTrafficClient.setInterestInterval(atoi(optarg));
break;
case 'c' :
ndnTrafficClient.setInterestCount(atoi(optarg));
break;
default :
ndnTrafficClient.usage();
break;
}
}
argc -= optind;
argv += optind;
if (argv[0] == NULL)
ndnTrafficClient.usage();
ndnTrafficClient.setConfigurationFile(argv[0]);
ndnTrafficClient.initialize();
return 0;
}