552 lines
16 KiB
C++
552 lines
16 KiB
C++
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
|
|
/**
|
|
* Copyright (C) 2014-2015 University of Arizona.
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
|
|
*/
|
|
|
|
#include <cctype>
|
|
#include <cstdlib>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <unistd.h>
|
|
#include <vector>
|
|
|
|
#include <boost/asio/io_service.hpp>
|
|
#include <boost/asio/signal_set.hpp>
|
|
#include <boost/filesystem.hpp>
|
|
#include <boost/noncopyable.hpp>
|
|
|
|
#include <ndn-cxx/face.hpp>
|
|
#include <ndn-cxx/security/key-chain.hpp>
|
|
#include <ndn-cxx/security/signing-helpers.hpp>
|
|
#include <ndn-cxx/util/backports.hpp>
|
|
|
|
#include "logger.hpp"
|
|
|
|
namespace ndn {
|
|
|
|
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()))
|
|
, m_face(m_ioService)
|
|
{
|
|
}
|
|
|
|
class DataTrafficConfiguration
|
|
{
|
|
public:
|
|
DataTrafficConfiguration()
|
|
: m_contentType(-1)
|
|
, m_freshnessPeriod(time::milliseconds(-1))
|
|
, m_contentBytes(-1)
|
|
, m_contentDelay(time::milliseconds(-1))
|
|
, m_nInterestsReceived(0)
|
|
, m_signWithSha256(false)
|
|
{
|
|
}
|
|
|
|
void
|
|
printTrafficConfiguration(Logger& logger)
|
|
{
|
|
std::string 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<int>(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 += "SignWithSha256=" + to_string(m_signWithSha256) + ", ";
|
|
if (detail.length() >= 2)
|
|
detail = detail.substr(0, detail.length() - 2);
|
|
|
|
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 == "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 == "SignWithSha256") {
|
|
if (value == "0") {
|
|
m_signWithSha256 = false;
|
|
}
|
|
else if (value == "1") {
|
|
m_signWithSha256 = true;
|
|
}
|
|
else {
|
|
logger.log("Line " + to_string(lineNumber) +
|
|
" \t- Invalid SignWithSha256 Value='" + value + "'", false, true);
|
|
}
|
|
}
|
|
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;
|
|
bool m_signWithSha256;
|
|
|
|
friend class NdnTrafficServer;
|
|
};
|
|
|
|
void
|
|
usage() const
|
|
{
|
|
std::cout << "Usage:\n"
|
|
<< " " << m_programName << " [options] <Traffic_Configuration_File>\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
|
|
{
|
|
return m_hasError;
|
|
}
|
|
|
|
void
|
|
setContentDelay(int contentDelay)
|
|
{
|
|
if (contentDelay < 0)
|
|
usage();
|
|
m_contentDelay = time::milliseconds(contentDelay);
|
|
}
|
|
|
|
void
|
|
setConfigurationFile(const char* configurationFile)
|
|
{
|
|
m_configurationFile = configurationFile;
|
|
}
|
|
|
|
void
|
|
setQuietLogging()
|
|
{
|
|
m_hasQuietLogging = true;
|
|
}
|
|
|
|
void
|
|
signalHandler()
|
|
{
|
|
logStatistics();
|
|
|
|
m_logger.shutdownLogger();
|
|
m_face.shutdown();
|
|
m_ioService.stop();
|
|
|
|
exit(m_hasError ? EXIT_FAILURE : EXIT_SUCCESS);
|
|
}
|
|
|
|
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);
|
|
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);
|
|
}
|
|
}
|
|
|
|
bool
|
|
checkTrafficPatternCorrectness()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void
|
|
parseConfigurationFile()
|
|
{
|
|
std::string patternLine;
|
|
std::ifstream patternFile;
|
|
m_logger.log("Analyzing 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]))
|
|
{
|
|
DataTrafficConfiguration dataData;
|
|
bool shouldSkipLine = false;
|
|
if (dataData.processConfigurationDetail(patternLine, m_logger, lineNumber))
|
|
{
|
|
while (getline(patternFile, patternLine) && std::isalpha(patternLine[0]))
|
|
{
|
|
lineNumber++;
|
|
if (!dataData.processConfigurationDetail(patternLine, m_logger, lineNumber))
|
|
{
|
|
shouldSkipLine = true;
|
|
break;
|
|
}
|
|
}
|
|
lineNumber++;
|
|
}
|
|
else
|
|
shouldSkipLine = true;
|
|
if (!shouldSkipLine)
|
|
{
|
|
if (dataData.checkTrafficDetailCorrectness())
|
|
m_trafficPatterns.push_back(dataData);
|
|
}
|
|
}
|
|
}
|
|
patternFile.close();
|
|
|
|
if (!checkTrafficPatternCorrectness())
|
|
{
|
|
m_logger.log("ERROR - Traffic Configuration Provided Is Not Proper - " +
|
|
m_configurationFile, false, true);
|
|
m_logger.shutdownLogger();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
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);
|
|
m_logger.shutdownLogger();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
static std::string
|
|
getRandomByteString(std::size_t randomSize)
|
|
{
|
|
std::string randomString;
|
|
for (std::size_t i = 0; i < randomSize; i++)
|
|
randomString += static_cast<char>(std::rand() % 128);
|
|
return randomString;
|
|
}
|
|
|
|
void
|
|
onInterest(const Name& name, const Interest& interest, int patternId)
|
|
{
|
|
auto& pattern = m_trafficPatterns[patternId];
|
|
|
|
if (m_nMaximumInterests < 0 || 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))
|
|
data.setFreshnessPeriod(pattern.m_freshnessPeriod);
|
|
|
|
std::string content;
|
|
if (pattern.m_contentBytes >= 0)
|
|
content = getRandomByteString(pattern.m_contentBytes);
|
|
if (!pattern.m_content.empty())
|
|
content = pattern.m_content;
|
|
|
|
data.setContent(reinterpret_cast<const uint8_t*>(content.c_str()), content.length());
|
|
|
|
if (pattern.m_signWithSha256) {
|
|
m_keyChain.sign(data, security::signingWithSha256());
|
|
}
|
|
else {
|
|
m_keyChain.sign(data);
|
|
}
|
|
|
|
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;
|
|
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);
|
|
m_face.put(data);
|
|
}
|
|
if (m_nMaximumInterests >= 0 && m_nInterestsReceived == m_nMaximumInterests)
|
|
{
|
|
logStatistics();
|
|
m_logger.shutdownLogger();
|
|
m_face.shutdown();
|
|
m_ioService.stop();
|
|
}
|
|
}
|
|
|
|
void
|
|
onRegisterFailed(const ndn::Name& prefix, const std::string& reason, int patternId)
|
|
{
|
|
std::string logLine;
|
|
logLine += "Prefix Registration Failed - PatternType=" + to_string(patternId + 1);
|
|
logLine += ", Name=" + m_trafficPatterns[patternId].m_name;
|
|
m_logger.log(logLine, true, true);
|
|
|
|
m_nRegistrationsFailed++;
|
|
if (m_nRegistrationsFailed == m_trafficPatterns.size())
|
|
{
|
|
m_hasError = true;
|
|
signalHandler();
|
|
}
|
|
}
|
|
|
|
void
|
|
run()
|
|
{
|
|
boost::asio::signal_set signalSet(m_ioService, SIGINT, SIGTERM);
|
|
signalSet.async_wait(bind(&NdnTrafficServer::signalHandler, this));
|
|
|
|
m_logger.initializeLog(m_instanceId);
|
|
initializeTrafficConfiguration();
|
|
if (m_nMaximumInterests == 0)
|
|
{
|
|
logStatistics();
|
|
m_logger.shutdownLogger();
|
|
return;
|
|
}
|
|
|
|
for (std::size_t patternId = 0; patternId < m_trafficPatterns.size(); patternId++)
|
|
m_face.setInterestFilter(m_trafficPatterns[patternId].m_name,
|
|
bind(&NdnTrafficServer::onInterest, this, _1, _2, patternId),
|
|
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_logger.shutdownLogger();
|
|
m_hasError = true;
|
|
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;
|
|
std::vector<DataTrafficConfiguration> m_trafficPatterns;
|
|
};
|
|
|
|
} // namespace ndn
|
|
|
|
int
|
|
main(int argc, char* argv[])
|
|
{
|
|
std::srand(std::time(nullptr));
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (!argc)
|
|
server.usage();
|
|
|
|
server.setConfigurationFile(argv[0]);
|
|
server.run();
|
|
|
|
return server.hasError() ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
} |