blob: 27ed2aac820b14b063ebaaa79e897818002bfd90 [file] [log] [blame]
Vince Lehman8a4c29e2016-07-11 08:49:35 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -05002/*
ashiqopud3ae85d2019-02-17 02:29:55 +00003 * Copyright (c) 2014-2019, Regents of the University of California,
Vince Lehman8a4c29e2016-07-11 08:49:35 +00004 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
10 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#include "fw/asf-strategy.hpp"
27
Junxiao Shi890afe92016-12-15 14:34:34 +000028#include "strategy-tester.hpp"
Vince Lehman8a4c29e2016-07-11 08:49:35 +000029#include "topology-tester.hpp"
30
31namespace nfd {
32namespace fw {
33namespace asf {
34namespace tests {
35
36using namespace nfd::fw::tests;
37
Junxiao Shi890afe92016-12-15 14:34:34 +000038// The tester is unused in this file, but it's used in various templated test suites.
Davide Pesavento14e71f02019-03-28 17:35:25 -040039using AsfStrategyTester = StrategyTester<AsfStrategy>;
Junxiao Shi890afe92016-12-15 14:34:34 +000040NFD_REGISTER_STRATEGY(AsfStrategyTester);
41
Vince Lehman8a4c29e2016-07-11 08:49:35 +000042BOOST_AUTO_TEST_SUITE(Fw)
Davide Pesaventocf7db2f2019-03-24 23:17:28 -040043BOOST_FIXTURE_TEST_SUITE(TestAsfStrategy, GlobalIoTimeFixture)
Vince Lehman8a4c29e2016-07-11 08:49:35 +000044
Davide Pesaventocf7db2f2019-03-24 23:17:28 -040045class AsfGridFixture : public GlobalIoTimeFixture
Vince Lehman8a4c29e2016-07-11 08:49:35 +000046{
47protected:
Davide Pesavento14e71f02019-03-28 17:35:25 -040048 explicit
49 AsfGridFixture(const Name& params = AsfStrategy::getStrategyName())
50 : parameters(params)
Vince Lehman8a4c29e2016-07-11 08:49:35 +000051 {
52 /*
53 * +---------+
54 * +----->| nodeB |<------+
55 * | +---------+ |
56 * 10ms | | 10ms
57 * v v
58 * +---------+ +---------+
59 * | nodeA | | nodeC |
60 * +---------+ +---------+
61 * ^ ^
62 * 100ms | | 100ms
63 * | +---------+ |
64 * +----->| nodeD |<------+
65 * +---------+
66 */
67
68 nodeA = topo.addForwarder("A");
69 nodeB = topo.addForwarder("B");
70 nodeC = topo.addForwarder("C");
71 nodeD = topo.addForwarder("D");
72
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -050073 topo.setStrategy<AsfStrategy>(nodeA, Name("ndn:/"), parameters);
74 topo.setStrategy<AsfStrategy>(nodeB, Name("ndn:/"), parameters);
75 topo.setStrategy<AsfStrategy>(nodeC, Name("ndn:/"), parameters);
76 topo.setStrategy<AsfStrategy>(nodeD, Name("ndn:/"), parameters);
Vince Lehman8a4c29e2016-07-11 08:49:35 +000077
Davide Pesavento14e71f02019-03-28 17:35:25 -040078 linkAB = topo.addLink("AB", 10_ms, {nodeA, nodeB});
79 linkAD = topo.addLink("AD", 100_ms, {nodeA, nodeD});
80 linkBC = topo.addLink("BC", 10_ms, {nodeB, nodeC});
81 linkCD = topo.addLink("CD", 100_ms, {nodeC, nodeD});
Vince Lehman8a4c29e2016-07-11 08:49:35 +000082
83 consumer = topo.addAppFace("c", nodeA);
84 producer = topo.addAppFace("p", nodeC, PRODUCER_PREFIX);
85 topo.addEchoProducer(producer->getClientFace());
86
87 // Register producer prefix on consumer node
88 topo.registerPrefix(nodeA, linkAB->getFace(nodeA), PRODUCER_PREFIX, 10);
89 topo.registerPrefix(nodeA, linkAD->getFace(nodeA), PRODUCER_PREFIX, 5);
90 }
91
92 void
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -050093 runConsumer(int numInterests = 30)
Vince Lehman8a4c29e2016-07-11 08:49:35 +000094 {
Davide Pesavento14e71f02019-03-28 17:35:25 -040095 topo.addIntervalConsumer(consumer->getClientFace(), PRODUCER_PREFIX, 1_s, numInterests);
96 this->advanceClocks(10_ms, time::seconds(numInterests));
Vince Lehman8a4c29e2016-07-11 08:49:35 +000097 }
98
99protected:
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -0500100 Name parameters;
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000101 TopologyTester topo;
102
103 TopologyNode nodeA;
104 TopologyNode nodeB;
105 TopologyNode nodeC;
106 TopologyNode nodeD;
107
108 shared_ptr<TopologyLink> linkAB;
109 shared_ptr<TopologyLink> linkAD;
110 shared_ptr<TopologyLink> linkBC;
111 shared_ptr<TopologyLink> linkCD;
112
113 shared_ptr<TopologyAppLink> consumer;
114 shared_ptr<TopologyAppLink> producer;
115
116 static const Name PRODUCER_PREFIX;
117};
118
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -0500119class AsfStrategyParametersGridFixture : public AsfGridFixture
120{
121protected:
122 AsfStrategyParametersGridFixture()
123 : AsfGridFixture(Name(AsfStrategy::getStrategyName())
124 .append("probing-interval~30000")
125 .append("n-silent-timeouts~5"))
126 {
127 }
128};
129
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000130const Name AsfGridFixture::PRODUCER_PREFIX = Name("ndn:/hr/C");
131
132BOOST_FIXTURE_TEST_CASE(Basic, AsfGridFixture)
133{
134 // Both nodeB and nodeD have FIB entries to reach the producer
135 topo.registerPrefix(nodeB, linkBC->getFace(nodeB), PRODUCER_PREFIX);
136 topo.registerPrefix(nodeD, linkCD->getFace(nodeD), PRODUCER_PREFIX);
137
138 runConsumer();
139
140 // ASF should use the Face to nodeD because it has lower routing cost.
141 // After 5 seconds, a probe Interest should be sent to the Face to nodeB,
142 // and the probe should return Data quicker. ASF should then use the Face
143 // to nodeB to forward the remaining Interests.
144 BOOST_CHECK_EQUAL(consumer->getForwarderFace().getCounters().nOutData, 30);
145 BOOST_CHECK_GE(linkAB->getFace(nodeA).getCounters().nOutInterests, 24);
146 BOOST_CHECK_LE(linkAD->getFace(nodeA).getCounters().nOutInterests, 6);
147
148 // If the link from nodeA to nodeB fails, ASF should start using the Face
149 // to nodeD again.
150 linkAB->fail();
151
152 runConsumer();
153
154 // Only 59 Data because the first Interest to nodeB after the failure should timeout
155 BOOST_CHECK_EQUAL(consumer->getForwarderFace().getCounters().nOutData, 59);
156 BOOST_CHECK_LE(linkAB->getFace(nodeA).getCounters().nOutInterests, 30);
157 BOOST_CHECK_GE(linkAD->getFace(nodeA).getCounters().nOutInterests, 30);
158
159 // If the link from nodeA to nodeB recovers, ASF should probe the Face
160 // to nodeB and start using it again.
161 linkAB->recover();
162
163 // Advance time to ensure probing is due
Davide Pesavento14e71f02019-03-28 17:35:25 -0400164 this->advanceClocks(10_ms, 10_s);
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000165
166 runConsumer();
167
168 BOOST_CHECK_EQUAL(consumer->getForwarderFace().getCounters().nOutData, 89);
169 BOOST_CHECK_GE(linkAB->getFace(nodeA).getCounters().nOutInterests, 50);
170 BOOST_CHECK_LE(linkAD->getFace(nodeA).getCounters().nOutInterests, 40);
171
172 // If both links fail, nodeA should forward to the next hop with the lowest cost
173 linkAB->fail();
174 linkAD->fail();
175
176 runConsumer();
177
178 BOOST_CHECK_EQUAL(consumer->getForwarderFace().getCounters().nOutData, 89);
Davide Pesavento5f47aa62016-10-07 22:09:09 +0200179 BOOST_CHECK_LE(linkAB->getFace(nodeA).getCounters().nOutInterests, 61); // FIXME #3830
180 BOOST_CHECK_GE(linkAD->getFace(nodeA).getCounters().nOutInterests, 59); // FIXME #3830
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000181}
182
183BOOST_FIXTURE_TEST_CASE(Nack, AsfGridFixture)
184{
185 // nodeB has a FIB entry to reach the producer, but nodeD does not
186 topo.registerPrefix(nodeB, linkBC->getFace(nodeB), PRODUCER_PREFIX);
187
188 // The strategy should first try to send to nodeD. But since nodeD does not have a route for
189 // the producer's prefix, it should return a NO_ROUTE Nack. The strategy should then start using the Face to
190 // nodeB.
191 runConsumer();
192
193 BOOST_CHECK_GE(linkAD->getFace(nodeA).getCounters().nInNacks, 1);
194 BOOST_CHECK_EQUAL(consumer->getForwarderFace().getCounters().nOutData, 29);
195 BOOST_CHECK_EQUAL(linkAB->getFace(nodeA).getCounters().nOutInterests, 29);
196
197 // nodeD should receive 2 Interests: one for the very first Interest and
198 // another from a probe
199 BOOST_CHECK_GE(linkAD->getFace(nodeA).getCounters().nOutInterests, 2);
200}
201
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000202BOOST_AUTO_TEST_CASE(NoPitOutRecordAndProbeInterestNewNonce)
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600203{
204 /* +---------+
205 * | nodeD |
206 * +---------+
207 * |
208 * | 80ms
209 * |
210 * |
211 * +---------+
212 * +----->| nodeB |<------+
213 * | +---------+ |
214 * 15ms | | 16ms
215 * v v
216 * +---------+ +---------+
217 * | nodeA |--------------| nodeC |
218 * +---------+ 14ms +---------+
219 */
220
221 const Name PRODUCER_PREFIX = "/ndn/edu/nodeD/ping";
222
223 TopologyTester topo;
224 TopologyNode nodeA = topo.addForwarder("A"),
225 nodeB = topo.addForwarder("B"),
226 nodeC = topo.addForwarder("C"),
227 nodeD = topo.addForwarder("D");
228
229 for (TopologyNode node : {nodeA, nodeB, nodeC, nodeD}) {
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -0500230 topo.setStrategy<AsfStrategy>(node);
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600231 }
232
Davide Pesavento14e71f02019-03-28 17:35:25 -0400233 shared_ptr<TopologyLink> linkAB = topo.addLink("AB", 15_ms, {nodeA, nodeB}),
234 linkAC = topo.addLink("AC", 14_ms, {nodeA, nodeC}),
235 linkBC = topo.addLink("BC", 16_ms, {nodeB, nodeC}),
236 linkBD = topo.addLink("BD", 80_ms, {nodeB, nodeD});
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600237
238 shared_ptr<TopologyAppLink> ping = topo.addAppFace("c", nodeA),
239 pingServer = topo.addAppFace("p", nodeD, PRODUCER_PREFIX);
240 topo.addEchoProducer(pingServer->getClientFace());
241
242 // Register prefixes
243 topo.registerPrefix(nodeA, linkAB->getFace(nodeA), PRODUCER_PREFIX, 15);
244 topo.registerPrefix(nodeA, linkAC->getFace(nodeA), PRODUCER_PREFIX, 14);
245 topo.registerPrefix(nodeC, linkBC->getFace(nodeC), PRODUCER_PREFIX, 16);
246 topo.registerPrefix(nodeB, linkBD->getFace(nodeB), PRODUCER_PREFIX, 80);
247
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000248 uint32_t nonce;
249
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600250 // Send 6 interest since probes can be scheduled b/w 0-5 seconds
251 for (int i = 1; i < 7; i++) {
252 // Send ping number i
253 Name name(PRODUCER_PREFIX);
254 name.appendTimestamp();
255 shared_ptr<Interest> interest = makeInterest(name);
256 ping->getClientFace().expressInterest(*interest, nullptr, nullptr, nullptr);
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000257 nonce = interest->getNonce();
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600258
259 // Don't know when the probe will be triggered since it is random between 0-5 seconds
260 // or whether it will be triggered for this interest
Davide Pesavento14e71f02019-03-28 17:35:25 -0400261 for (int j = 1; j <= 1000 && linkAB->getFace(nodeA).getCounters().nOutInterests != 1; ++j) {
262 this->advanceClocks(1_ms);
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600263 }
264
265 // Check if probe is sent to B else send another ping
266 if (linkAB->getFace(nodeA).getCounters().nOutInterests == 1) {
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000267 // Get pitEntry of node A
268 shared_ptr<pit::Entry> pitEntry = topo.getForwarder(nodeA).getPit().find(*interest);
269 //get outRecord associated with face towards B
ashiqopud3ae85d2019-02-17 02:29:55 +0000270 pit::OutRecordCollection::const_iterator outRecord = pitEntry->getOutRecord(linkAB->getFace(nodeA), 0);
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000271
272 BOOST_CHECK(outRecord != pitEntry->out_end());
273
274 //Check that Nonce of interest is not equal to Nonce of Probe
275 BOOST_CHECK_NE(nonce, outRecord->getLastNonce());
276
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600277 // B should not have received the probe interest yet
278 BOOST_CHECK_EQUAL(linkAB->getFace(nodeB).getCounters().nInInterests, 0);
279
280 // i-1 interests through B when no probe
281 BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nOutInterests, i - 1);
282
283 // After 15ms, B should get the probe interest
Davide Pesavento14e71f02019-03-28 17:35:25 -0400284 this->advanceClocks(1_ms, 15_ms);
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600285 BOOST_CHECK_EQUAL(linkAB->getFace(nodeB).getCounters().nInInterests, 1);
286 BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nOutInterests, i);
287
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000288 pitEntry = topo.getForwarder(nodeB).getPit().find(*interest);
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600289
290 // Get outRecord associated with face towards D.
ashiqopud3ae85d2019-02-17 02:29:55 +0000291 outRecord = pitEntry->getOutRecord(linkBD->getFace(nodeB), 0);
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600292
293 BOOST_CHECK(outRecord != pitEntry->out_end());
294
295 // RTT between B and D
Davide Pesavento14e71f02019-03-28 17:35:25 -0400296 this->advanceClocks(5_ms, 160_ms);
ashiqopud3ae85d2019-02-17 02:29:55 +0000297 outRecord = pitEntry->getOutRecord(linkBD->getFace(nodeB), 0);
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600298
299 BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nInData, i);
300
301 BOOST_CHECK(outRecord == pitEntry->out_end());
302
303 // Data is returned for the ping after 15 ms - will result in false measurement
304 // 14+16-15 = 15ms
305 // Since outRecord == pitEntry->out_end()
Davide Pesavento14e71f02019-03-28 17:35:25 -0400306 this->advanceClocks(1_ms, 15_ms);
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600307 BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nInData, i+1);
308
309 break;
310 }
311 }
312}
313
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -0500314BOOST_FIXTURE_TEST_CASE(IgnoreTimeouts, AsfStrategyParametersGridFixture)
315{
316 // Both nodeB and nodeD have FIB entries to reach the producer
317 topo.registerPrefix(nodeB, linkBC->getFace(nodeB), PRODUCER_PREFIX);
318 topo.registerPrefix(nodeD, linkCD->getFace(nodeD), PRODUCER_PREFIX);
319
320 // Send 15 interests let it change to use the 10 ms link
321 runConsumer(15);
322
323 int outInterestsBeforeFailure = linkAD->getFace(nodeA).getCounters().nOutInterests;
324
325 // Bring down 10 ms link
326 linkAB->fail();
327
328 // Send 6 interests, first 5 will be ignored and on the 6th it will record the timeout
329 // ready to switch for the next interest
330 runConsumer(6);
331
332 // Check that link has not been switched to 100 ms because n-silent-timeouts = 5
333 BOOST_CHECK_EQUAL(linkAD->getFace(nodeA).getCounters().nOutInterests - outInterestsBeforeFailure, 0);
334
335 // Send 5 interests, check that 100 ms link is used
336 runConsumer(5);
337
338 BOOST_CHECK_EQUAL(linkAD->getFace(nodeA).getCounters().nOutInterests - outInterestsBeforeFailure, 5);
339}
340
341BOOST_FIXTURE_TEST_CASE(ProbingInterval, AsfStrategyParametersGridFixture)
342{
343 // Both nodeB and nodeD have FIB entries to reach the producer
344 topo.registerPrefix(nodeB, linkBC->getFace(nodeB), PRODUCER_PREFIX);
345 topo.registerPrefix(nodeD, linkCD->getFace(nodeD), PRODUCER_PREFIX);
346
347 // Send 6 interests let it change to use the 10 ms link
348 runConsumer(6);
349
Davide Pesavento14e71f02019-03-28 17:35:25 -0400350 shared_ptr<TopologyLink> linkAC = topo.addLink("AC", 5_ms, {nodeA, nodeD});
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -0500351 topo.registerPrefix(nodeA, linkAC->getFace(nodeA), PRODUCER_PREFIX, 1);
352
353 BOOST_CHECK_EQUAL(linkAC->getFace(nodeA).getCounters().nOutInterests, 0);
354
355 // After 30 seconds a probe would be sent that would switch make ASF switch
356 runConsumer(30);
357
358 BOOST_CHECK_EQUAL(linkAC->getFace(nodeA).getCounters().nOutInterests, 1);
359}
360
361class ParametersFixture
362{
363public:
364 void
365 checkValidity(std::string parameters, bool isCorrect)
366 {
367 Name strategyName(Name(AsfStrategy::getStrategyName()).append(parameters));
368 if (isCorrect) {
369 BOOST_CHECK_NO_THROW(make_unique<AsfStrategy>(forwarder, strategyName));
370 }
371 else {
372 BOOST_CHECK_THROW(make_unique<AsfStrategy>(forwarder, strategyName), std::invalid_argument);
373 }
374 }
375
376protected:
377 Forwarder forwarder;
378};
379
380BOOST_FIXTURE_TEST_CASE(InstantiationTest, ParametersFixture)
381{
382 checkValidity("/probing-interval~30000/n-silent-timeouts~5", true);
383 checkValidity("/n-silent-timeouts~5/probing-interval~30000", true);
384 checkValidity("/probing-interval~30000", true);
385 checkValidity("/n-silent-timeouts~5", true);
386 checkValidity("", true);
387
388 checkValidity("/probing-interval~500", false); // At least 1 seconds
389 checkValidity("/probing-interval~-5000", false);
390 checkValidity("/n-silent-timeouts~-5", false);
391 checkValidity("/n-silent-timeouts~-5/probing-interval~-30000", false);
392 checkValidity("/n-silent-timeouts", false);
393 checkValidity("/probing-interval~", false);
394 checkValidity("/~1000", false);
395 checkValidity("/probing-interval~foo", false);
396 checkValidity("/n-silent-timeouts~1~2", false);
397}
398
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000399BOOST_AUTO_TEST_SUITE_END() // TestAsfStrategy
400BOOST_AUTO_TEST_SUITE_END() // Fw
401
402} // namespace tests
403} // namespace asf
404} // namespace fw
405} // namespace nfd