blob: d363880f3cf4023f4b7d2c994e1868a5b31e0a61 [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.
39typedef StrategyTester<AsfStrategy> AsfStrategyTester;
40NFD_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:
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -050048 AsfGridFixture(Name parameters = AsfStrategy::getStrategyName())
49 : parameters(parameters)
Vince Lehman8a4c29e2016-07-11 08:49:35 +000050 {
51 /*
52 * +---------+
53 * +----->| nodeB |<------+
54 * | +---------+ |
55 * 10ms | | 10ms
56 * v v
57 * +---------+ +---------+
58 * | nodeA | | nodeC |
59 * +---------+ +---------+
60 * ^ ^
61 * 100ms | | 100ms
62 * | +---------+ |
63 * +----->| nodeD |<------+
64 * +---------+
65 */
66
67 nodeA = topo.addForwarder("A");
68 nodeB = topo.addForwarder("B");
69 nodeC = topo.addForwarder("C");
70 nodeD = topo.addForwarder("D");
71
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -050072 topo.setStrategy<AsfStrategy>(nodeA, Name("ndn:/"), parameters);
73 topo.setStrategy<AsfStrategy>(nodeB, Name("ndn:/"), parameters);
74 topo.setStrategy<AsfStrategy>(nodeC, Name("ndn:/"), parameters);
75 topo.setStrategy<AsfStrategy>(nodeD, Name("ndn:/"), parameters);
Vince Lehman8a4c29e2016-07-11 08:49:35 +000076
77 linkAB = topo.addLink("AB", time::milliseconds(10), {nodeA, nodeB});
78 linkAD = topo.addLink("AD", time::milliseconds(100), {nodeA, nodeD});
79 linkBC = topo.addLink("BC", time::milliseconds(10), {nodeB, nodeC});
80 linkCD = topo.addLink("CD", time::milliseconds(100), {nodeC, nodeD});
81
82 consumer = topo.addAppFace("c", nodeA);
83 producer = topo.addAppFace("p", nodeC, PRODUCER_PREFIX);
84 topo.addEchoProducer(producer->getClientFace());
85
86 // Register producer prefix on consumer node
87 topo.registerPrefix(nodeA, linkAB->getFace(nodeA), PRODUCER_PREFIX, 10);
88 topo.registerPrefix(nodeA, linkAD->getFace(nodeA), PRODUCER_PREFIX, 5);
89 }
90
91 void
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -050092 runConsumer(int numInterests = 30)
Vince Lehman8a4c29e2016-07-11 08:49:35 +000093 {
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -050094 topo.addIntervalConsumer(consumer->getClientFace(), PRODUCER_PREFIX, time::seconds(1), numInterests);
95 this->advanceClocks(time::milliseconds(10), time::seconds(numInterests));
Vince Lehman8a4c29e2016-07-11 08:49:35 +000096 }
97
98protected:
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -050099 Name parameters;
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000100 TopologyTester topo;
101
102 TopologyNode nodeA;
103 TopologyNode nodeB;
104 TopologyNode nodeC;
105 TopologyNode nodeD;
106
107 shared_ptr<TopologyLink> linkAB;
108 shared_ptr<TopologyLink> linkAD;
109 shared_ptr<TopologyLink> linkBC;
110 shared_ptr<TopologyLink> linkCD;
111
112 shared_ptr<TopologyAppLink> consumer;
113 shared_ptr<TopologyAppLink> producer;
114
115 static const Name PRODUCER_PREFIX;
116};
117
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -0500118class AsfStrategyParametersGridFixture : public AsfGridFixture
119{
120protected:
121 AsfStrategyParametersGridFixture()
122 : AsfGridFixture(Name(AsfStrategy::getStrategyName())
123 .append("probing-interval~30000")
124 .append("n-silent-timeouts~5"))
125 {
126 }
127};
128
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000129const Name AsfGridFixture::PRODUCER_PREFIX = Name("ndn:/hr/C");
130
131BOOST_FIXTURE_TEST_CASE(Basic, AsfGridFixture)
132{
133 // Both nodeB and nodeD have FIB entries to reach the producer
134 topo.registerPrefix(nodeB, linkBC->getFace(nodeB), PRODUCER_PREFIX);
135 topo.registerPrefix(nodeD, linkCD->getFace(nodeD), PRODUCER_PREFIX);
136
137 runConsumer();
138
139 // ASF should use the Face to nodeD because it has lower routing cost.
140 // After 5 seconds, a probe Interest should be sent to the Face to nodeB,
141 // and the probe should return Data quicker. ASF should then use the Face
142 // to nodeB to forward the remaining Interests.
143 BOOST_CHECK_EQUAL(consumer->getForwarderFace().getCounters().nOutData, 30);
144 BOOST_CHECK_GE(linkAB->getFace(nodeA).getCounters().nOutInterests, 24);
145 BOOST_CHECK_LE(linkAD->getFace(nodeA).getCounters().nOutInterests, 6);
146
147 // If the link from nodeA to nodeB fails, ASF should start using the Face
148 // to nodeD again.
149 linkAB->fail();
150
151 runConsumer();
152
153 // Only 59 Data because the first Interest to nodeB after the failure should timeout
154 BOOST_CHECK_EQUAL(consumer->getForwarderFace().getCounters().nOutData, 59);
155 BOOST_CHECK_LE(linkAB->getFace(nodeA).getCounters().nOutInterests, 30);
156 BOOST_CHECK_GE(linkAD->getFace(nodeA).getCounters().nOutInterests, 30);
157
158 // If the link from nodeA to nodeB recovers, ASF should probe the Face
159 // to nodeB and start using it again.
160 linkAB->recover();
161
162 // Advance time to ensure probing is due
Junxiao Shia49a1ab2016-07-15 18:24:36 +0000163 this->advanceClocks(time::milliseconds(10), time::seconds(10));
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000164
165 runConsumer();
166
167 BOOST_CHECK_EQUAL(consumer->getForwarderFace().getCounters().nOutData, 89);
168 BOOST_CHECK_GE(linkAB->getFace(nodeA).getCounters().nOutInterests, 50);
169 BOOST_CHECK_LE(linkAD->getFace(nodeA).getCounters().nOutInterests, 40);
170
171 // If both links fail, nodeA should forward to the next hop with the lowest cost
172 linkAB->fail();
173 linkAD->fail();
174
175 runConsumer();
176
177 BOOST_CHECK_EQUAL(consumer->getForwarderFace().getCounters().nOutData, 89);
Davide Pesavento5f47aa62016-10-07 22:09:09 +0200178 BOOST_CHECK_LE(linkAB->getFace(nodeA).getCounters().nOutInterests, 61); // FIXME #3830
179 BOOST_CHECK_GE(linkAD->getFace(nodeA).getCounters().nOutInterests, 59); // FIXME #3830
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000180}
181
182BOOST_FIXTURE_TEST_CASE(Nack, AsfGridFixture)
183{
184 // nodeB has a FIB entry to reach the producer, but nodeD does not
185 topo.registerPrefix(nodeB, linkBC->getFace(nodeB), PRODUCER_PREFIX);
186
187 // The strategy should first try to send to nodeD. But since nodeD does not have a route for
188 // the producer's prefix, it should return a NO_ROUTE Nack. The strategy should then start using the Face to
189 // nodeB.
190 runConsumer();
191
192 BOOST_CHECK_GE(linkAD->getFace(nodeA).getCounters().nInNacks, 1);
193 BOOST_CHECK_EQUAL(consumer->getForwarderFace().getCounters().nOutData, 29);
194 BOOST_CHECK_EQUAL(linkAB->getFace(nodeA).getCounters().nOutInterests, 29);
195
196 // nodeD should receive 2 Interests: one for the very first Interest and
197 // another from a probe
198 BOOST_CHECK_GE(linkAD->getFace(nodeA).getCounters().nOutInterests, 2);
199}
200
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000201BOOST_AUTO_TEST_CASE(NoPitOutRecordAndProbeInterestNewNonce)
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600202{
203 /* +---------+
204 * | nodeD |
205 * +---------+
206 * |
207 * | 80ms
208 * |
209 * |
210 * +---------+
211 * +----->| nodeB |<------+
212 * | +---------+ |
213 * 15ms | | 16ms
214 * v v
215 * +---------+ +---------+
216 * | nodeA |--------------| nodeC |
217 * +---------+ 14ms +---------+
218 */
219
220 const Name PRODUCER_PREFIX = "/ndn/edu/nodeD/ping";
221
222 TopologyTester topo;
223 TopologyNode nodeA = topo.addForwarder("A"),
224 nodeB = topo.addForwarder("B"),
225 nodeC = topo.addForwarder("C"),
226 nodeD = topo.addForwarder("D");
227
228 for (TopologyNode node : {nodeA, nodeB, nodeC, nodeD}) {
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -0500229 topo.setStrategy<AsfStrategy>(node);
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600230 }
231
232 shared_ptr<TopologyLink> linkAB = topo.addLink("AB", time::milliseconds(15), {nodeA, nodeB}),
233 linkAC = topo.addLink("AC", time::milliseconds(14), {nodeA, nodeC}),
234 linkBC = topo.addLink("BC", time::milliseconds(16), {nodeB, nodeC}),
235 linkBD = topo.addLink("BD", time::milliseconds(80), {nodeB, nodeD});
236
237 shared_ptr<TopologyAppLink> ping = topo.addAppFace("c", nodeA),
238 pingServer = topo.addAppFace("p", nodeD, PRODUCER_PREFIX);
239 topo.addEchoProducer(pingServer->getClientFace());
240
241 // Register prefixes
242 topo.registerPrefix(nodeA, linkAB->getFace(nodeA), PRODUCER_PREFIX, 15);
243 topo.registerPrefix(nodeA, linkAC->getFace(nodeA), PRODUCER_PREFIX, 14);
244 topo.registerPrefix(nodeC, linkBC->getFace(nodeC), PRODUCER_PREFIX, 16);
245 topo.registerPrefix(nodeB, linkBD->getFace(nodeB), PRODUCER_PREFIX, 80);
246
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000247 uint32_t nonce;
248
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600249 // Send 6 interest since probes can be scheduled b/w 0-5 seconds
250 for (int i = 1; i < 7; i++) {
251 // Send ping number i
252 Name name(PRODUCER_PREFIX);
253 name.appendTimestamp();
254 shared_ptr<Interest> interest = makeInterest(name);
255 ping->getClientFace().expressInterest(*interest, nullptr, nullptr, nullptr);
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000256 nonce = interest->getNonce();
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600257
258 // Don't know when the probe will be triggered since it is random between 0-5 seconds
259 // or whether it will be triggered for this interest
260 int j = 1;
261 while (linkAB->getFace(nodeA).getCounters().nOutInterests != 1) {
262 this->advanceClocks(time::milliseconds(1));
263 ++j;
264 // Probe was not scheduled with this ping interest
265 if (j > 1000) {
266 break;
267 }
268 }
269
270 // Check if probe is sent to B else send another ping
271 if (linkAB->getFace(nodeA).getCounters().nOutInterests == 1) {
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000272 // Get pitEntry of node A
273 shared_ptr<pit::Entry> pitEntry = topo.getForwarder(nodeA).getPit().find(*interest);
274 //get outRecord associated with face towards B
ashiqopud3ae85d2019-02-17 02:29:55 +0000275 pit::OutRecordCollection::const_iterator outRecord = pitEntry->getOutRecord(linkAB->getFace(nodeA), 0);
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000276
277 BOOST_CHECK(outRecord != pitEntry->out_end());
278
279 //Check that Nonce of interest is not equal to Nonce of Probe
280 BOOST_CHECK_NE(nonce, outRecord->getLastNonce());
281
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600282 // B should not have received the probe interest yet
283 BOOST_CHECK_EQUAL(linkAB->getFace(nodeB).getCounters().nInInterests, 0);
284
285 // i-1 interests through B when no probe
286 BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nOutInterests, i - 1);
287
288 // After 15ms, B should get the probe interest
289 this->advanceClocks(time::milliseconds(1), time::milliseconds(15));
290 BOOST_CHECK_EQUAL(linkAB->getFace(nodeB).getCounters().nInInterests, 1);
291 BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nOutInterests, i);
292
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000293 pitEntry = topo.getForwarder(nodeB).getPit().find(*interest);
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600294
295 // Get outRecord associated with face towards D.
ashiqopud3ae85d2019-02-17 02:29:55 +0000296 outRecord = pitEntry->getOutRecord(linkBD->getFace(nodeB), 0);
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600297
298 BOOST_CHECK(outRecord != pitEntry->out_end());
299
300 // RTT between B and D
301 this->advanceClocks(time::milliseconds(5), time::milliseconds(160));
ashiqopud3ae85d2019-02-17 02:29:55 +0000302 outRecord = pitEntry->getOutRecord(linkBD->getFace(nodeB), 0);
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600303
304 BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nInData, i);
305
306 BOOST_CHECK(outRecord == pitEntry->out_end());
307
308 // Data is returned for the ping after 15 ms - will result in false measurement
309 // 14+16-15 = 15ms
310 // Since outRecord == pitEntry->out_end()
311 this->advanceClocks(time::milliseconds(1), time::milliseconds(15));
312 BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nInData, i+1);
313
314 break;
315 }
316 }
317}
318
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -0500319BOOST_FIXTURE_TEST_CASE(IgnoreTimeouts, AsfStrategyParametersGridFixture)
320{
321 // Both nodeB and nodeD have FIB entries to reach the producer
322 topo.registerPrefix(nodeB, linkBC->getFace(nodeB), PRODUCER_PREFIX);
323 topo.registerPrefix(nodeD, linkCD->getFace(nodeD), PRODUCER_PREFIX);
324
325 // Send 15 interests let it change to use the 10 ms link
326 runConsumer(15);
327
328 int outInterestsBeforeFailure = linkAD->getFace(nodeA).getCounters().nOutInterests;
329
330 // Bring down 10 ms link
331 linkAB->fail();
332
333 // Send 6 interests, first 5 will be ignored and on the 6th it will record the timeout
334 // ready to switch for the next interest
335 runConsumer(6);
336
337 // Check that link has not been switched to 100 ms because n-silent-timeouts = 5
338 BOOST_CHECK_EQUAL(linkAD->getFace(nodeA).getCounters().nOutInterests - outInterestsBeforeFailure, 0);
339
340 // Send 5 interests, check that 100 ms link is used
341 runConsumer(5);
342
343 BOOST_CHECK_EQUAL(linkAD->getFace(nodeA).getCounters().nOutInterests - outInterestsBeforeFailure, 5);
344}
345
346BOOST_FIXTURE_TEST_CASE(ProbingInterval, AsfStrategyParametersGridFixture)
347{
348 // Both nodeB and nodeD have FIB entries to reach the producer
349 topo.registerPrefix(nodeB, linkBC->getFace(nodeB), PRODUCER_PREFIX);
350 topo.registerPrefix(nodeD, linkCD->getFace(nodeD), PRODUCER_PREFIX);
351
352 // Send 6 interests let it change to use the 10 ms link
353 runConsumer(6);
354
355 shared_ptr<TopologyLink> linkAC = topo.addLink("AC", time::milliseconds(5), {nodeA, nodeD});
356 topo.registerPrefix(nodeA, linkAC->getFace(nodeA), PRODUCER_PREFIX, 1);
357
358 BOOST_CHECK_EQUAL(linkAC->getFace(nodeA).getCounters().nOutInterests, 0);
359
360 // After 30 seconds a probe would be sent that would switch make ASF switch
361 runConsumer(30);
362
363 BOOST_CHECK_EQUAL(linkAC->getFace(nodeA).getCounters().nOutInterests, 1);
364}
365
366class ParametersFixture
367{
368public:
369 void
370 checkValidity(std::string parameters, bool isCorrect)
371 {
372 Name strategyName(Name(AsfStrategy::getStrategyName()).append(parameters));
373 if (isCorrect) {
374 BOOST_CHECK_NO_THROW(make_unique<AsfStrategy>(forwarder, strategyName));
375 }
376 else {
377 BOOST_CHECK_THROW(make_unique<AsfStrategy>(forwarder, strategyName), std::invalid_argument);
378 }
379 }
380
381protected:
382 Forwarder forwarder;
383};
384
385BOOST_FIXTURE_TEST_CASE(InstantiationTest, ParametersFixture)
386{
387 checkValidity("/probing-interval~30000/n-silent-timeouts~5", true);
388 checkValidity("/n-silent-timeouts~5/probing-interval~30000", true);
389 checkValidity("/probing-interval~30000", true);
390 checkValidity("/n-silent-timeouts~5", true);
391 checkValidity("", true);
392
393 checkValidity("/probing-interval~500", false); // At least 1 seconds
394 checkValidity("/probing-interval~-5000", false);
395 checkValidity("/n-silent-timeouts~-5", false);
396 checkValidity("/n-silent-timeouts~-5/probing-interval~-30000", false);
397 checkValidity("/n-silent-timeouts", false);
398 checkValidity("/probing-interval~", false);
399 checkValidity("/~1000", false);
400 checkValidity("/probing-interval~foo", false);
401 checkValidity("/n-silent-timeouts~1~2", false);
402}
403
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000404BOOST_AUTO_TEST_SUITE_END() // TestAsfStrategy
405BOOST_AUTO_TEST_SUITE_END() // Fw
406
407} // namespace tests
408} // namespace asf
409} // namespace fw
410} // namespace nfd