blob: b18d051533a6f0eca55f0cc860e6e1fd3de4b478 [file] [log] [blame]
Davide Pesavento912d2e82019-01-10 17:04:31 -05001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
Davide Pesavento84e77302023-04-17 01:10:06 -04003 * Copyright (c) 2014-2023, Arizona Board of Regents.
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -08004 *
Davide Pesaventod0b59982015-02-27 19:15:15 +01005 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -080017 *
18 * Author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
19 */
20
Davide Pesavento35185332019-01-14 04:00:15 -050021#include "util.hpp"
22
Davide Pesaventoef064892022-04-05 02:26:03 -040023#include <ndn-cxx/data.hpp>
Davide Pesavento35185332019-01-14 04:00:15 -050024#include <ndn-cxx/face.hpp>
Davide Pesaventoef064892022-04-05 02:26:03 -040025#include <ndn-cxx/interest.hpp>
Davide Pesavento35185332019-01-14 04:00:15 -050026#include <ndn-cxx/lp/tags.hpp>
Davide Pesavento35185332019-01-14 04:00:15 -050027#include <ndn-cxx/util/random.hpp>
Davide Pesaventoef064892022-04-05 02:26:03 -040028#include <ndn-cxx/util/time.hpp>
Davide Pesavento912d2e82019-01-10 17:04:31 -050029
Davide Pesavento35185332019-01-14 04:00:15 -050030#include <limits>
Davide Pesaventoef064892022-04-05 02:26:03 -040031#include <optional>
Davide Pesavento56f79d62019-01-26 17:30:32 -050032#include <sstream>
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -080033#include <vector>
34
Davide Pesavento912d2e82019-01-10 17:04:31 -050035#include <boost/asio/deadline_timer.hpp>
Davide Pesavento915ce802023-09-17 18:10:52 -040036#include <boost/asio/io_context.hpp>
Davide Pesavento912d2e82019-01-10 17:04:31 -050037#include <boost/asio/signal_set.hpp>
Alexander Lanee9fe1872023-07-25 20:00:23 -040038#include <boost/core/noncopyable.hpp>
jeraldabraham420dbf02014-04-25 22:58:31 -070039#include <boost/date_time/posix_time/posix_time.hpp>
Davide Pesavento912d2e82019-01-10 17:04:31 -050040#include <boost/lexical_cast.hpp>
Davide Pesavento35185332019-01-14 04:00:15 -050041#include <boost/program_options/options_description.hpp>
42#include <boost/program_options/parsers.hpp>
43#include <boost/program_options/variables_map.hpp>
jeraldabraham420dbf02014-04-25 22:58:31 -070044
Davide Pesaventoef064892022-04-05 02:26:03 -040045using namespace ndn::time_literals;
46using namespace std::string_literals;
jeraldabraham420dbf02014-04-25 22:58:31 -070047
Davide Pesaventoef064892022-04-05 02:26:03 -040048namespace ndntg {
49
50namespace time = ndn::time;
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -080051
jeraldabraham420dbf02014-04-25 22:58:31 -070052class NdnTrafficClient : boost::noncopyable
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -080053{
54public:
jeraldabrahamcc3c6c92014-03-28 02:21:45 -070055 explicit
Alexander Lanee9fe1872023-07-25 20:00:23 -040056 NdnTrafficClient(std::string configFile)
57 : m_configurationFile(std::move(configFile))
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -080058 {
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -080059 }
60
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -080061 void
Davide Pesavento35185332019-01-14 04:00:15 -050062 setMaximumInterests(uint64_t maxInterests)
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -080063 {
Davide Pesavento35185332019-01-14 04:00:15 -050064 m_nMaximumInterests = maxInterests;
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -080065 }
66
67 void
Davide Pesavento35185332019-01-14 04:00:15 -050068 setInterestInterval(time::milliseconds interval)
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -080069 {
Davide Pesavento35185332019-01-14 04:00:15 -050070 BOOST_ASSERT(interval > 0_ms);
71 m_interestInterval = interval;
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -080072 }
73
74 void
Alexander Lanee9fe1872023-07-25 20:00:23 -040075 setTimestampFormat(std::string format)
76 {
77 m_timestampFormat = std::move(format);
78 }
79
80 void
jeraldabraham420dbf02014-04-25 22:58:31 -070081 setQuietLogging()
82 {
Davide Pesavento35185332019-01-14 04:00:15 -050083 m_wantQuiet = true;
jeraldabraham420dbf02014-04-25 22:58:31 -070084 }
85
Alexander Lanee9fe1872023-07-25 20:00:23 -040086 void
87 setVerboseLogging()
88 {
89 m_wantVerbose = true;
90 }
91
Davide Pesavento306e5bc2019-01-26 16:20:34 -050092 int
Davide Pesavento35185332019-01-14 04:00:15 -050093 run()
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -080094 {
Alexander Lanee9fe1872023-07-25 20:00:23 -040095 m_logger.initialize(std::to_string(ndn::random::generateWord32()), m_timestampFormat);
Davide Pesavento306e5bc2019-01-26 16:20:34 -050096
97 if (!readConfigurationFile(m_configurationFile, m_trafficPatterns, m_logger)) {
98 return 2;
99 }
100
101 if (!checkTrafficPatternCorrectness()) {
102 m_logger.log("ERROR: Traffic configuration provided is not proper", false, true);
103 return 2;
104 }
105
Alexander Lanee9fe1872023-07-25 20:00:23 -0400106 m_logger.log("Traffic configuration file processing completed\n", true, false);
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500107 for (std::size_t i = 0; i < m_trafficPatterns.size(); i++) {
Davide Pesaventoef064892022-04-05 02:26:03 -0400108 m_logger.log("Traffic Pattern Type #" + std::to_string(i + 1), false, false);
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500109 m_trafficPatterns[i].printTrafficConfiguration(m_logger);
110 m_logger.log("", false, false);
111 }
Davide Pesaventod0b59982015-02-27 19:15:15 +0100112
Davide Pesavento35185332019-01-14 04:00:15 -0500113 if (m_nMaximumInterests == 0) {
114 logStatistics();
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500115 return 0;
Davide Pesavento35185332019-01-14 04:00:15 -0500116 }
Davide Pesaventod0b59982015-02-27 19:15:15 +0100117
Davide Pesaventoef064892022-04-05 02:26:03 -0400118 m_signalSet.async_wait([this] (auto&&...) { stop(); });
Davide Pesavento35185332019-01-14 04:00:15 -0500119
Davide Pesavento915ce802023-09-17 18:10:52 -0400120 boost::asio::deadline_timer timer(m_io, boost::posix_time::millisec(m_interestInterval.count()));
Davide Pesaventoef064892022-04-05 02:26:03 -0400121 timer.async_wait([this, &timer] (auto&&...) { generateTraffic(timer); });
Davide Pesavento35185332019-01-14 04:00:15 -0500122
123 try {
124 m_face.processEvents();
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500125 return m_hasError ? 1 : 0;
Davide Pesavento35185332019-01-14 04:00:15 -0500126 }
127 catch (const std::exception& e) {
128 m_logger.log("ERROR: "s + e.what(), true, true);
Davide Pesavento915ce802023-09-17 18:10:52 -0400129 m_io.stop();
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500130 return 1;
Davide Pesavento35185332019-01-14 04:00:15 -0500131 }
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800132 }
133
Davide Pesavento35185332019-01-14 04:00:15 -0500134private:
135 class InterestTrafficConfiguration
136 {
137 public:
138 void
139 printTrafficConfiguration(Logger& logger) const
140 {
Davide Pesavento56f79d62019-01-26 17:30:32 -0500141 std::ostringstream os;
Davide Pesavento35185332019-01-14 04:00:15 -0500142
Davide Pesavento56f79d62019-01-26 17:30:32 -0500143 os << "TrafficPercentage=" << m_trafficPercentage << ", ";
144 os << "Name=" << m_name << ", ";
145 if (m_nameAppendBytes) {
146 os << "NameAppendBytes=" << *m_nameAppendBytes << ", ";
147 }
148 if (m_nameAppendSeqNum) {
149 os << "NameAppendSequenceNumber=" << *m_nameAppendSeqNum << ", ";
150 }
151 if (m_canBePrefix) {
152 os << "CanBePrefix=" << m_canBePrefix << ", ";
153 }
154 if (m_mustBeFresh) {
155 os << "MustBeFresh=" << m_mustBeFresh << ", ";
156 }
157 if (m_nonceDuplicationPercentage > 0) {
158 os << "NonceDuplicationPercentage=" << m_nonceDuplicationPercentage << ", ";
159 }
160 if (m_interestLifetime >= 0_ms) {
161 os << "InterestLifetime=" << m_interestLifetime.count() << ", ";
162 }
163 if (m_nextHopFaceId > 0) {
164 os << "NextHopFaceId=" << m_nextHopFaceId << ", ";
165 }
166 if (m_expectedContent) {
167 os << "ExpectedContent=" << *m_expectedContent << ", ";
168 }
Davide Pesavento35185332019-01-14 04:00:15 -0500169
Davide Pesavento56f79d62019-01-26 17:30:32 -0500170 auto str = os.str();
171 str = str.substr(0, str.length() - 2); // remove suffix ", "
172 logger.log(str, false, false);
Davide Pesavento35185332019-01-14 04:00:15 -0500173 }
174
175 bool
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500176 parseConfigurationLine(const std::string& line, Logger& logger, int lineNumber)
Davide Pesavento35185332019-01-14 04:00:15 -0500177 {
178 std::string parameter, value;
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500179 if (!extractParameterAndValue(line, parameter, value)) {
Davide Pesaventoef064892022-04-05 02:26:03 -0400180 logger.log("Line " + std::to_string(lineNumber) + " - Invalid syntax: " + line,
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500181 false, true);
182 return false;
183 }
184
185 if (parameter == "TrafficPercentage") {
Zeke Lewisdfbf8a12023-03-19 23:40:06 +0000186 m_trafficPercentage = std::stod(value);
187 if (!std::isfinite(m_trafficPercentage)) {
188 logger.log("Line " + std::to_string(lineNumber) +
189 " - TrafficPercentage must be a finite floating point value", false, true);
190 return false;
191 }
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500192 }
193 else if (parameter == "Name") {
194 m_name = value;
195 }
196 else if (parameter == "NameAppendBytes") {
197 m_nameAppendBytes = std::stoul(value);
198 }
199 else if (parameter == "NameAppendSequenceNumber") {
200 m_nameAppendSeqNum = std::stoull(value);
201 }
Davide Pesavento56f79d62019-01-26 17:30:32 -0500202 else if (parameter == "CanBePrefix") {
203 m_canBePrefix = parseBoolean(value);
204 }
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500205 else if (parameter == "MustBeFresh") {
206 m_mustBeFresh = parseBoolean(value);
207 }
208 else if (parameter == "NonceDuplicationPercentage") {
209 m_nonceDuplicationPercentage = std::stoul(value);
210 }
211 else if (parameter == "InterestLifetime") {
212 m_interestLifetime = time::milliseconds(std::stoul(value));
213 }
214 else if (parameter == "NextHopFaceId") {
215 m_nextHopFaceId = std::stoull(value);
216 }
217 else if (parameter == "ExpectedContent") {
218 m_expectedContent = value;
Davide Pesavento35185332019-01-14 04:00:15 -0500219 }
220 else {
Davide Pesaventoef064892022-04-05 02:26:03 -0400221 logger.log("Line " + std::to_string(lineNumber) + " - Ignoring unknown parameter: " + parameter,
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500222 false, true);
Davide Pesavento35185332019-01-14 04:00:15 -0500223 }
224 return true;
225 }
226
227 bool
228 checkTrafficDetailCorrectness() const
229 {
230 return true;
231 }
232
233 public:
Zeke Lewisdfbf8a12023-03-19 23:40:06 +0000234 double m_trafficPercentage = 0.0;
Davide Pesavento35185332019-01-14 04:00:15 -0500235 std::string m_name;
Davide Pesaventoef064892022-04-05 02:26:03 -0400236 std::optional<std::size_t> m_nameAppendBytes;
237 std::optional<uint64_t> m_nameAppendSeqNum;
Davide Pesavento56f79d62019-01-26 17:30:32 -0500238 bool m_canBePrefix = false;
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500239 bool m_mustBeFresh = false;
Zeke Lewisdfbf8a12023-03-19 23:40:06 +0000240 unsigned m_nonceDuplicationPercentage = 0;
Davide Pesavento35185332019-01-14 04:00:15 -0500241 time::milliseconds m_interestLifetime = -1_ms;
242 uint64_t m_nextHopFaceId = 0;
Davide Pesaventoef064892022-04-05 02:26:03 -0400243 std::optional<std::string> m_expectedContent;
Davide Pesavento35185332019-01-14 04:00:15 -0500244
245 uint64_t m_nInterestsSent = 0;
246 uint64_t m_nInterestsReceived = 0;
247 uint64_t m_nNacks = 0;
248 uint64_t m_nContentInconsistencies = 0;
249
250 // RTT is stored as milliseconds with fractional sub-milliseconds precision
251 double m_minimumInterestRoundTripTime = std::numeric_limits<double>::max();
252 double m_maximumInterestRoundTripTime = 0;
253 double m_totalInterestRoundTripTime = 0;
254 };
255
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800256 void
257 logStatistics()
258 {
Davide Pesavento84e77302023-04-17 01:10:06 -0400259 using std::to_string;
260
Alexander Lanee9fe1872023-07-25 20:00:23 -0400261 m_logger.log("\n\n== Traffic Report ==\n", false, true);
Davide Pesavento84e77302023-04-17 01:10:06 -0400262 m_logger.log("Total Traffic Pattern Types = " + to_string(m_trafficPatterns.size()), false, true);
263 m_logger.log("Total Interests Sent = " + to_string(m_nInterestsSent), false, true);
264 m_logger.log("Total Responses Received = " + to_string(m_nInterestsReceived), false, true);
265 m_logger.log("Total Nacks Received = " + to_string(m_nNacks), false, true);
Davide Pesaventod0b59982015-02-27 19:15:15 +0100266
Davide Pesavento35185332019-01-14 04:00:15 -0500267 double loss = 0.0;
268 if (m_nInterestsSent > 0) {
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700269 loss = (m_nInterestsSent - m_nInterestsReceived) * 100.0 / m_nInterestsSent;
Davide Pesavento35185332019-01-14 04:00:15 -0500270 }
Davide Pesavento84e77302023-04-17 01:10:06 -0400271 m_logger.log("Total Interest Loss = " + to_string(loss) + "%", false, true);
Davide Pesaventod0b59982015-02-27 19:15:15 +0100272
Davide Pesavento35185332019-01-14 04:00:15 -0500273 double average = 0.0;
274 double inconsistency = 0.0;
275 if (m_nInterestsReceived > 0) {
276 average = m_totalInterestRoundTripTime / m_nInterestsReceived;
277 inconsistency = m_nContentInconsistencies * 100.0 / m_nInterestsReceived;
278 }
Davide Pesavento84e77302023-04-17 01:10:06 -0400279 m_logger.log("Total Data Inconsistency = " + to_string(inconsistency) + "%", false, true);
280 m_logger.log("Total Round Trip Time = " + to_string(m_totalInterestRoundTripTime) + "ms", false, true);
281 m_logger.log("Average Round Trip Time = " + to_string(average) + "ms\n", false, true);
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700282
Davide Pesavento35185332019-01-14 04:00:15 -0500283 for (std::size_t patternId = 0; patternId < m_trafficPatterns.size(); patternId++) {
Davide Pesavento84e77302023-04-17 01:10:06 -0400284 const auto& pattern = m_trafficPatterns[patternId];
285
286 m_logger.log("Traffic Pattern Type #" + to_string(patternId + 1), false, true);
287 pattern.printTrafficConfiguration(m_logger);
288 m_logger.log("Total Interests Sent = " + to_string(pattern.m_nInterestsSent), false, true);
289 m_logger.log("Total Responses Received = " + to_string(pattern.m_nInterestsReceived), false, true);
290 m_logger.log("Total Nacks Received = " + to_string(pattern.m_nNacks), false, true);
291
292 loss = 0.0;
293 if (pattern.m_nInterestsSent > 0) {
294 loss = (pattern.m_nInterestsSent - pattern.m_nInterestsReceived) * 100.0 / pattern.m_nInterestsSent;
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800295 }
Davide Pesavento84e77302023-04-17 01:10:06 -0400296 m_logger.log("Total Interest Loss = " + to_string(loss) + "%", false, true);
297
298 average = 0.0;
299 inconsistency = 0.0;
300 if (pattern.m_nInterestsReceived > 0) {
301 average = pattern.m_totalInterestRoundTripTime / pattern.m_nInterestsReceived;
302 inconsistency = pattern.m_nContentInconsistencies * 100.0 / pattern.m_nInterestsReceived;
Davide Pesavento35185332019-01-14 04:00:15 -0500303 }
Davide Pesavento84e77302023-04-17 01:10:06 -0400304 m_logger.log("Total Data Inconsistency = " + to_string(inconsistency) + "%", false, true);
Davide Pesavento35185332019-01-14 04:00:15 -0500305 m_logger.log("Total Round Trip Time = " +
Davide Pesavento84e77302023-04-17 01:10:06 -0400306 to_string(pattern.m_totalInterestRoundTripTime) + "ms", false, true);
307 m_logger.log("Average Round Trip Time = " + to_string(average) + "ms\n", false, true);
Davide Pesavento35185332019-01-14 04:00:15 -0500308 }
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800309 }
310
311 bool
Davide Pesavento35185332019-01-14 04:00:15 -0500312 checkTrafficPatternCorrectness() const
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800313 {
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500314 // TODO
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800315 return true;
316 }
317
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700318 uint32_t
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800319 getNewNonce()
320 {
Davide Pesavento35185332019-01-14 04:00:15 -0500321 if (m_nonces.size() >= 1000)
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700322 m_nonces.clear();
jeraldabraham473ef3d2014-03-06 12:40:35 -0700323
Davide Pesaventoef064892022-04-05 02:26:03 -0400324 auto randomNonce = ndn::random::generateWord32();
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700325 while (std::find(m_nonces.begin(), m_nonces.end(), randomNonce) != m_nonces.end())
Davide Pesaventoef064892022-04-05 02:26:03 -0400326 randomNonce = ndn::random::generateWord32();
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700327
328 m_nonces.push_back(randomNonce);
329 return randomNonce;
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800330 }
331
Davide Pesavento35185332019-01-14 04:00:15 -0500332 uint32_t
333 getOldNonce()
334 {
335 if (m_nonces.empty())
336 return getNewNonce();
337
338 std::uniform_int_distribution<std::size_t> dist(0, m_nonces.size() - 1);
Davide Pesaventoef064892022-04-05 02:26:03 -0400339 return m_nonces[dist(ndn::random::getRandomNumberEngine())];
Davide Pesavento35185332019-01-14 04:00:15 -0500340 }
341
Davide Pesaventoef064892022-04-05 02:26:03 -0400342 static auto
Davide Pesavento912d2e82019-01-10 17:04:31 -0500343 generateRandomNameComponent(std::size_t length)
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800344 {
Davide Pesavento35185332019-01-14 04:00:15 -0500345 // per ISO C++ std, cannot instantiate uniform_int_distribution with uint8_t
Zeke Lewisdfbf8a12023-03-19 23:40:06 +0000346 static std::uniform_int_distribution<unsigned> dist(std::numeric_limits<uint8_t>::min(),
347 std::numeric_limits<uint8_t>::max());
Davide Pesavento35185332019-01-14 04:00:15 -0500348
Davide Pesaventoef064892022-04-05 02:26:03 -0400349 ndn::Buffer buf(length);
Davide Pesavento912d2e82019-01-10 17:04:31 -0500350 for (std::size_t i = 0; i < length; i++) {
Davide Pesaventoef064892022-04-05 02:26:03 -0400351 buf[i] = static_cast<uint8_t>(dist(ndn::random::getRandomNumberEngine()));
Eric Newberry3b284192015-07-06 21:44:46 -0700352 }
Davide Pesaventoef064892022-04-05 02:26:03 -0400353 return ndn::name::Component(buf);
Davide Pesavento35185332019-01-14 04:00:15 -0500354 }
355
Davide Pesaventoef064892022-04-05 02:26:03 -0400356 auto
Davide Pesavento35185332019-01-14 04:00:15 -0500357 prepareInterest(std::size_t patternId)
358 {
Davide Pesaventoef064892022-04-05 02:26:03 -0400359 ndn::Interest interest;
Davide Pesavento35185332019-01-14 04:00:15 -0500360 auto& pattern = m_trafficPatterns[patternId];
361
Davide Pesaventoef064892022-04-05 02:26:03 -0400362 ndn::Name name(pattern.m_name);
Davide Pesavento35185332019-01-14 04:00:15 -0500363 if (pattern.m_nameAppendBytes > 0) {
364 name.append(generateRandomNameComponent(*pattern.m_nameAppendBytes));
365 }
366 if (pattern.m_nameAppendSeqNum) {
367 auto seqNum = *pattern.m_nameAppendSeqNum;
368 name.appendSequenceNumber(seqNum);
369 pattern.m_nameAppendSeqNum = seqNum + 1;
370 }
371 interest.setName(name);
372
Davide Pesavento56f79d62019-01-26 17:30:32 -0500373 interest.setCanBePrefix(pattern.m_canBePrefix);
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500374 interest.setMustBeFresh(pattern.m_mustBeFresh);
Davide Pesavento35185332019-01-14 04:00:15 -0500375
Zeke Lewisdfbf8a12023-03-19 23:40:06 +0000376 static std::uniform_int_distribution<unsigned> duplicateNonceDist(1, 100);
Davide Pesaventoef064892022-04-05 02:26:03 -0400377 if (duplicateNonceDist(ndn::random::getRandomNumberEngine()) <= pattern.m_nonceDuplicationPercentage)
Davide Pesavento35185332019-01-14 04:00:15 -0500378 interest.setNonce(getOldNonce());
379 else
380 interest.setNonce(getNewNonce());
381
382 if (pattern.m_interestLifetime >= 0_ms)
383 interest.setInterestLifetime(pattern.m_interestLifetime);
384
385 if (pattern.m_nextHopFaceId > 0)
Davide Pesaventoef064892022-04-05 02:26:03 -0400386 interest.setTag(std::make_shared<ndn::lp::NextHopFaceIdTag>(pattern.m_nextHopFaceId));
Davide Pesavento35185332019-01-14 04:00:15 -0500387
388 return interest;
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800389 }
390
391 void
Davide Pesavento84e77302023-04-17 01:10:06 -0400392 onData(const ndn::Interest&, const ndn::Data& data, int globalRef, int localRef,
393 std::size_t patternId, const time::steady_clock::time_point& sentTime)
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800394 {
Alexander Lanee9fe1872023-07-25 20:00:23 -0400395 auto now = time::steady_clock::now();
Davide Pesaventoef064892022-04-05 02:26:03 -0400396 auto logLine = "Data Received - PatternType=" + std::to_string(patternId + 1) +
397 ", GlobalID=" + std::to_string(globalRef) +
398 ", LocalID=" + std::to_string(localRef) +
Davide Pesavento35185332019-01-14 04:00:15 -0500399 ", Name=" + data.getName().toUri();
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700400
401 m_nInterestsReceived++;
402 m_trafficPatterns[patternId].m_nInterestsReceived++;
Davide Pesavento35185332019-01-14 04:00:15 -0500403
404 if (m_trafficPatterns[patternId].m_expectedContent) {
Davide Pesavento92669062022-03-10 19:09:44 -0500405 std::string receivedContent = readString(data.getContent());
Davide Pesavento35185332019-01-14 04:00:15 -0500406 if (receivedContent != *m_trafficPatterns[patternId].m_expectedContent) {
Davide Pesavento912d2e82019-01-10 17:04:31 -0500407 m_nContentInconsistencies++;
408 m_trafficPatterns[patternId].m_nContentInconsistencies++;
409 logLine += ", IsConsistent=No";
jeraldabraham473ef3d2014-03-06 12:40:35 -0700410 }
Davide Pesavento35185332019-01-14 04:00:15 -0500411 else {
Davide Pesavento912d2e82019-01-10 17:04:31 -0500412 logLine += ", IsConsistent=Yes";
Davide Pesavento35185332019-01-14 04:00:15 -0500413 }
Davide Pesavento912d2e82019-01-10 17:04:31 -0500414 }
Davide Pesavento35185332019-01-14 04:00:15 -0500415 else {
jeraldabraham473ef3d2014-03-06 12:40:35 -0700416 logLine += ", IsConsistent=NotChecked";
Davide Pesavento35185332019-01-14 04:00:15 -0500417 }
418 if (!m_wantQuiet) {
jeraldabraham420dbf02014-04-25 22:58:31 -0700419 m_logger.log(logLine, true, false);
Davide Pesavento35185332019-01-14 04:00:15 -0500420 }
421
Zeke Lewisdfbf8a12023-03-19 23:40:06 +0000422 double rtt = time::duration_cast<time::nanoseconds>(now - sentTime).count() / 1e6;
Alexander Lanee9fe1872023-07-25 20:00:23 -0400423 if (m_wantVerbose) {
424 auto rttLine = "RTT - Name=" + data.getName().toUri() +
Zeke Lewisdfbf8a12023-03-19 23:40:06 +0000425 ", RTT=" + std::to_string(rtt) + "ms";
Alexander Lanee9fe1872023-07-25 20:00:23 -0400426 m_logger.log(rttLine, true, false);
427 }
Zeke Lewisdfbf8a12023-03-19 23:40:06 +0000428 if (m_minimumInterestRoundTripTime > rtt)
429 m_minimumInterestRoundTripTime = rtt;
430 if (m_maximumInterestRoundTripTime < rtt)
431 m_maximumInterestRoundTripTime = rtt;
432 if (m_trafficPatterns[patternId].m_minimumInterestRoundTripTime > rtt)
433 m_trafficPatterns[patternId].m_minimumInterestRoundTripTime = rtt;
434 if (m_trafficPatterns[patternId].m_maximumInterestRoundTripTime < rtt)
435 m_trafficPatterns[patternId].m_maximumInterestRoundTripTime = rtt;
436 m_totalInterestRoundTripTime += rtt;
437 m_trafficPatterns[patternId].m_totalInterestRoundTripTime += rtt;
Davide Pesavento35185332019-01-14 04:00:15 -0500438
439 if (m_nMaximumInterests == globalRef) {
440 stop();
Davide Pesavento912d2e82019-01-10 17:04:31 -0500441 }
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700442 }
443
444 void
Davide Pesaventoef064892022-04-05 02:26:03 -0400445 onNack(const ndn::Interest& interest, const ndn::lp::Nack& nack,
Davide Pesavento35185332019-01-14 04:00:15 -0500446 int globalRef, int localRef, std::size_t patternId)
Eric Newberry976c2042016-06-19 23:37:35 -0700447 {
Davide Pesaventoef064892022-04-05 02:26:03 -0400448 auto logLine = "Interest Nack'd - PatternType=" + std::to_string(patternId + 1) +
449 ", GlobalID=" + std::to_string(globalRef) +
450 ", LocalID=" + std::to_string(localRef) +
Davide Pesavento35185332019-01-14 04:00:15 -0500451 ", Name=" + interest.getName().toUri() +
452 ", NackReason=" + boost::lexical_cast<std::string>(nack.getReason());
Eric Newberry976c2042016-06-19 23:37:35 -0700453 m_logger.log(logLine, true, false);
454
455 m_nNacks++;
456 m_trafficPatterns[patternId].m_nNacks++;
457
Davide Pesavento35185332019-01-14 04:00:15 -0500458 if (m_nMaximumInterests == globalRef) {
459 stop();
Eric Newberry976c2042016-06-19 23:37:35 -0700460 }
461 }
462
463 void
Davide Pesavento84e77302023-04-17 01:10:06 -0400464 onTimeout(const ndn::Interest& interest, int globalRef, int localRef, std::size_t patternId)
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700465 {
Davide Pesaventoef064892022-04-05 02:26:03 -0400466 auto logLine = "Interest Timed Out - PatternType=" + std::to_string(patternId + 1) +
467 ", GlobalID=" + std::to_string(globalRef) +
468 ", LocalID=" + std::to_string(localRef) +
Davide Pesavento35185332019-01-14 04:00:15 -0500469 ", Name=" + interest.getName().toUri();
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700470 m_logger.log(logLine, true, false);
Davide Pesavento35185332019-01-14 04:00:15 -0500471
472 if (m_nMaximumInterests == globalRef) {
473 stop();
474 }
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700475 }
476
477 void
Davide Pesavento35185332019-01-14 04:00:15 -0500478 generateTraffic(boost::asio::deadline_timer& timer)
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700479 {
Davide Pesavento35185332019-01-14 04:00:15 -0500480 if (m_nMaximumInterests && m_nInterestsSent >= *m_nMaximumInterests) {
Davide Pesavento912d2e82019-01-10 17:04:31 -0500481 return;
482 }
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700483
Zeke Lewisdfbf8a12023-03-19 23:40:06 +0000484 static std::uniform_real_distribution<> trafficDist(std::numeric_limits<double>::min(), 100.0);
485 double trafficKey = trafficDist(ndn::random::getRandomNumberEngine());
Davide Pesaventod0b59982015-02-27 19:15:15 +0100486
Zeke Lewisdfbf8a12023-03-19 23:40:06 +0000487 double cumulativePercentage = 0.0;
Davide Pesavento35185332019-01-14 04:00:15 -0500488 std::size_t patternId = 0;
489 for (; patternId < m_trafficPatterns.size(); patternId++) {
Davide Pesavento84e77302023-04-17 01:10:06 -0400490 auto& pattern = m_trafficPatterns[patternId];
491 cumulativePercentage += pattern.m_trafficPercentage;
Davide Pesavento35185332019-01-14 04:00:15 -0500492 if (trafficKey <= cumulativePercentage) {
Davide Pesavento84e77302023-04-17 01:10:06 -0400493 m_nInterestsSent++;
494 pattern.m_nInterestsSent++;
Davide Pesavento35185332019-01-14 04:00:15 -0500495 auto interest = prepareInterest(patternId);
496 try {
Davide Pesavento84e77302023-04-17 01:10:06 -0400497 int globalRef = m_nInterestsSent;
498 int localRef = pattern.m_nInterestsSent;
Davide Pesavento35185332019-01-14 04:00:15 -0500499 m_face.expressInterest(interest,
Davide Pesavento84e77302023-04-17 01:10:06 -0400500 [=, now = time::steady_clock::now()] (auto&&... args) {
501 onData(std::forward<decltype(args)>(args)..., globalRef, localRef, patternId, now);
502 },
503 [=] (auto&&... args) {
504 onNack(std::forward<decltype(args)>(args)..., globalRef, localRef, patternId);
505 },
506 [=] (auto&&... args) {
507 onTimeout(std::forward<decltype(args)>(args)..., globalRef, localRef, patternId);
508 });
Davide Pesavento35185332019-01-14 04:00:15 -0500509
510 if (!m_wantQuiet) {
Davide Pesaventoef064892022-04-05 02:26:03 -0400511 auto logLine = "Sending Interest - PatternType=" + std::to_string(patternId + 1) +
512 ", GlobalID=" + std::to_string(m_nInterestsSent) +
Davide Pesavento84e77302023-04-17 01:10:06 -0400513 ", LocalID=" + std::to_string(pattern.m_nInterestsSent) +
Davide Pesavento35185332019-01-14 04:00:15 -0500514 ", Name=" + interest.getName().toUri();
515 m_logger.log(logLine, true, false);
516 }
517
Davide Pesaventoef064892022-04-05 02:26:03 -0400518 timer.expires_at(timer.expires_at() + boost::posix_time::millisec(m_interestInterval.count()));
519 timer.async_wait([this, &timer] (auto&&...) { generateTraffic(timer); });
Davide Pesavento35185332019-01-14 04:00:15 -0500520 }
521 catch (const std::exception& e) {
522 m_logger.log("ERROR: "s + e.what(), true, true);
523 }
524 break;
525 }
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800526 }
Alexander Lanee9fe1872023-07-25 20:00:23 -0400527
Davide Pesavento35185332019-01-14 04:00:15 -0500528 if (patternId == m_trafficPatterns.size()) {
Davide Pesaventoef064892022-04-05 02:26:03 -0400529 timer.expires_at(timer.expires_at() + boost::posix_time::millisec(m_interestInterval.count()));
530 timer.async_wait([this, &timer] (auto&&...) { generateTraffic(timer); });
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800531 }
532 }
533
Davide Pesavento35185332019-01-14 04:00:15 -0500534 void
535 stop()
536 {
537 if (m_nContentInconsistencies > 0 || m_nInterestsSent != m_nInterestsReceived) {
538 m_hasError = true;
539 }
540
541 logStatistics();
542 m_face.shutdown();
Davide Pesavento915ce802023-09-17 18:10:52 -0400543 m_io.stop();
Davide Pesavento35185332019-01-14 04:00:15 -0500544 }
545
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800546private:
Alexander Lanee9fe1872023-07-25 20:00:23 -0400547 Logger m_logger{"NdnTrafficClient"};
Davide Pesavento915ce802023-09-17 18:10:52 -0400548 boost::asio::io_context m_io;
549 boost::asio::signal_set m_signalSet{m_io, SIGINT, SIGTERM};
550 ndn::Face m_face{m_io};
Davide Pesavento35185332019-01-14 04:00:15 -0500551
552 std::string m_configurationFile;
Alexander Lanee9fe1872023-07-25 20:00:23 -0400553 std::string m_timestampFormat;
Davide Pesaventoef064892022-04-05 02:26:03 -0400554 std::optional<uint64_t> m_nMaximumInterests;
Davide Pesavento35185332019-01-14 04:00:15 -0500555 time::milliseconds m_interestInterval = 1_s;
Davide Pesavento35185332019-01-14 04:00:15 -0500556
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700557 std::vector<InterestTrafficConfiguration> m_trafficPatterns;
558 std::vector<uint32_t> m_nonces;
Davide Pesavento35185332019-01-14 04:00:15 -0500559 uint64_t m_nInterestsSent = 0;
560 uint64_t m_nInterestsReceived = 0;
561 uint64_t m_nNacks = 0;
562 uint64_t m_nContentInconsistencies = 0;
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700563
Davide Pesavento912d2e82019-01-10 17:04:31 -0500564 // RTT is stored as milliseconds with fractional sub-milliseconds precision
Davide Pesavento35185332019-01-14 04:00:15 -0500565 double m_minimumInterestRoundTripTime = std::numeric_limits<double>::max();
566 double m_maximumInterestRoundTripTime = 0;
567 double m_totalInterestRoundTripTime = 0;
568
Davide Pesaventoef064892022-04-05 02:26:03 -0400569 bool m_wantQuiet = false;
Alexander Lanee9fe1872023-07-25 20:00:23 -0400570 bool m_wantVerbose = false;
Davide Pesavento35185332019-01-14 04:00:15 -0500571 bool m_hasError = false;
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800572};
573
Davide Pesaventoef064892022-04-05 02:26:03 -0400574} // namespace ndntg
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800575
Davide Pesavento84e77302023-04-17 01:10:06 -0400576namespace po = boost::program_options;
577
Davide Pesavento35185332019-01-14 04:00:15 -0500578static void
Davide Pesaventoef064892022-04-05 02:26:03 -0400579usage(std::ostream& os, std::string_view programName, const po::options_description& desc)
Davide Pesavento35185332019-01-14 04:00:15 -0500580{
581 os << "Usage: " << programName << " [options] <Traffic_Configuration_File>\n"
582 << "\n"
583 << "Generate Interest traffic as per provided Traffic_Configuration_File.\n"
584 << "Interests are continuously generated unless a total number is specified.\n"
585 << "Set the environment variable NDN_TRAFFIC_LOGFOLDER to redirect output to a log file.\n"
586 << "\n"
587 << desc;
588}
589
jeraldabrahamcc3c6c92014-03-28 02:21:45 -0700590int
591main(int argc, char* argv[])
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800592{
Davide Pesavento35185332019-01-14 04:00:15 -0500593 std::string configFile;
Alexander Lanee9fe1872023-07-25 20:00:23 -0400594 std::string timestampFormat;
Davide Pesaventod0b59982015-02-27 19:15:15 +0100595
Davide Pesavento35185332019-01-14 04:00:15 -0500596 po::options_description visibleOptions("Options");
597 visibleOptions.add_options()
598 ("help,h", "print this help message and exit")
Alexander Lanee9fe1872023-07-25 20:00:23 -0400599 ("count,c", po::value<int64_t>(), "total number of Interests to be generated")
Davide Pesavento35185332019-01-14 04:00:15 -0500600 ("interval,i", po::value<ndn::time::milliseconds::rep>()->default_value(1000),
601 "Interest generation interval in milliseconds")
Alexander Lanee9fe1872023-07-25 20:00:23 -0400602 ("timestamp-format,t", po::value<std::string>(&timestampFormat), "format string for timestamp output")
603 ("quiet,q", po::bool_switch(), "turn off logging of Interest generation and Data reception")
604 ("verbose,v", po::bool_switch(), "log additional per-packet information")
Davide Pesavento35185332019-01-14 04:00:15 -0500605 ;
606
607 po::options_description hiddenOptions;
608 hiddenOptions.add_options()
609 ("config-file", po::value<std::string>(&configFile))
610 ;
611
612 po::positional_options_description posOptions;
613 posOptions.add("config-file", -1);
614
615 po::options_description allOptions;
616 allOptions.add(visibleOptions).add(hiddenOptions);
617
618 po::variables_map vm;
619 try {
620 po::store(po::command_line_parser(argc, argv).options(allOptions).positional(posOptions).run(), vm);
621 po::notify(vm);
622 }
623 catch (const po::error& e) {
624 std::cerr << "ERROR: " << e.what() << std::endl;
625 return 2;
626 }
627 catch (const boost::bad_any_cast& e) {
628 std::cerr << "ERROR: " << e.what() << std::endl;
629 return 2;
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800630 }
631
Davide Pesavento35185332019-01-14 04:00:15 -0500632 if (vm.count("help") > 0) {
633 usage(std::cout, argv[0], visibleOptions);
634 return 0;
635 }
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800636
Davide Pesavento35185332019-01-14 04:00:15 -0500637 if (configFile.empty()) {
638 usage(std::cerr, argv[0], visibleOptions);
639 return 2;
640 }
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800641
Alexander Lanee9fe1872023-07-25 20:00:23 -0400642 ndntg::NdnTrafficClient client(std::move(configFile));
Davide Pesavento35185332019-01-14 04:00:15 -0500643
644 if (vm.count("count") > 0) {
Alexander Lanee9fe1872023-07-25 20:00:23 -0400645 auto count = vm["count"].as<int64_t>();
Davide Pesavento35185332019-01-14 04:00:15 -0500646 if (count < 0) {
Alexander Lanee9fe1872023-07-25 20:00:23 -0400647 std::cerr << "ERROR: the argument for option '--count' cannot be negative\n";
Davide Pesavento35185332019-01-14 04:00:15 -0500648 return 2;
649 }
650 client.setMaximumInterests(static_cast<uint64_t>(count));
651 }
652
653 if (vm.count("interval") > 0) {
654 ndn::time::milliseconds interval(vm["interval"].as<ndn::time::milliseconds::rep>());
Davide Pesaventoef064892022-04-05 02:26:03 -0400655 if (interval <= 0_ms) {
Alexander Lanee9fe1872023-07-25 20:00:23 -0400656 std::cerr << "ERROR: the argument for option '--interval' must be positive\n";
Davide Pesavento35185332019-01-14 04:00:15 -0500657 return 2;
658 }
659 client.setInterestInterval(interval);
660 }
661
Alexander Lanee9fe1872023-07-25 20:00:23 -0400662 if (!timestampFormat.empty()) {
663 client.setTimestampFormat(std::move(timestampFormat));
664 }
665
Davide Pesavento35185332019-01-14 04:00:15 -0500666 if (vm["quiet"].as<bool>()) {
Alexander Lanee9fe1872023-07-25 20:00:23 -0400667 if (vm["verbose"].as<bool>()) {
668 std::cerr << "ERROR: cannot set both '--quiet' and '--verbose'\n";
669 return 2;
670 }
Davide Pesavento35185332019-01-14 04:00:15 -0500671 client.setQuietLogging();
672 }
673
Alexander Lanee9fe1872023-07-25 20:00:23 -0400674 if (vm["verbose"].as<bool>()) {
675 client.setVerboseLogging();
676 }
677
Davide Pesavento306e5bc2019-01-26 16:20:34 -0500678 return client.run();
Alexander Afanasyeva8f2a922014-02-26 14:21:56 -0800679}