blob: 5759e680ea63ef79b788f2c8026b7d02f39f4c8e [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/*
3 * Copyright (c) 2014-2018, 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
28#include "tests/test-common.hpp"
Junxiao Shi890afe92016-12-15 14:34:34 +000029#include "strategy-tester.hpp"
Vince Lehman8a4c29e2016-07-11 08:49:35 +000030#include "topology-tester.hpp"
31
32namespace nfd {
33namespace fw {
34namespace asf {
35namespace tests {
36
37using namespace nfd::fw::tests;
38
Junxiao Shi890afe92016-12-15 14:34:34 +000039// The tester is unused in this file, but it's used in various templated test suites.
40typedef StrategyTester<AsfStrategy> AsfStrategyTester;
41NFD_REGISTER_STRATEGY(AsfStrategyTester);
42
Vince Lehman8a4c29e2016-07-11 08:49:35 +000043BOOST_AUTO_TEST_SUITE(Fw)
44BOOST_FIXTURE_TEST_SUITE(TestAsfStrategy, UnitTestTimeFixture)
45
46class AsfGridFixture : public UnitTestTimeFixture
47{
48protected:
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -050049 AsfGridFixture(Name parameters = AsfStrategy::getStrategyName())
50 : parameters(parameters)
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
78 linkAB = topo.addLink("AB", time::milliseconds(10), {nodeA, nodeB});
79 linkAD = topo.addLink("AD", time::milliseconds(100), {nodeA, nodeD});
80 linkBC = topo.addLink("BC", time::milliseconds(10), {nodeB, nodeC});
81 linkCD = topo.addLink("CD", time::milliseconds(100), {nodeC, nodeD});
82
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 {
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -050095 topo.addIntervalConsumer(consumer->getClientFace(), PRODUCER_PREFIX, time::seconds(1), numInterests);
96 this->advanceClocks(time::milliseconds(10), 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
Junxiao Shia49a1ab2016-07-15 18:24:36 +0000164 this->advanceClocks(time::milliseconds(10), time::seconds(10));
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
233 shared_ptr<TopologyLink> linkAB = topo.addLink("AB", time::milliseconds(15), {nodeA, nodeB}),
234 linkAC = topo.addLink("AC", time::milliseconds(14), {nodeA, nodeC}),
235 linkBC = topo.addLink("BC", time::milliseconds(16), {nodeB, nodeC}),
236 linkBD = topo.addLink("BD", time::milliseconds(80), {nodeB, nodeD});
237
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
261 int j = 1;
262 while (linkAB->getFace(nodeA).getCounters().nOutInterests != 1) {
263 this->advanceClocks(time::milliseconds(1));
264 ++j;
265 // Probe was not scheduled with this ping interest
266 if (j > 1000) {
267 break;
268 }
269 }
270
271 // Check if probe is sent to B else send another ping
272 if (linkAB->getFace(nodeA).getCounters().nOutInterests == 1) {
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000273 // Get pitEntry of node A
274 shared_ptr<pit::Entry> pitEntry = topo.getForwarder(nodeA).getPit().find(*interest);
275 //get outRecord associated with face towards B
276 pit::OutRecordCollection::const_iterator outRecord = pitEntry->getOutRecord(linkAB->getFace(nodeA));
277
278 BOOST_CHECK(outRecord != pitEntry->out_end());
279
280 //Check that Nonce of interest is not equal to Nonce of Probe
281 BOOST_CHECK_NE(nonce, outRecord->getLastNonce());
282
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600283 // B should not have received the probe interest yet
284 BOOST_CHECK_EQUAL(linkAB->getFace(nodeB).getCounters().nInInterests, 0);
285
286 // i-1 interests through B when no probe
287 BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nOutInterests, i - 1);
288
289 // After 15ms, B should get the probe interest
290 this->advanceClocks(time::milliseconds(1), time::milliseconds(15));
291 BOOST_CHECK_EQUAL(linkAB->getFace(nodeB).getCounters().nInInterests, 1);
292 BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nOutInterests, i);
293
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000294 pitEntry = topo.getForwarder(nodeB).getPit().find(*interest);
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600295
296 // Get outRecord associated with face towards D.
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000297 outRecord = pitEntry->getOutRecord(linkBD->getFace(nodeB));
Ashlesh Gawanded3ac7772016-11-06 00:53:05 -0600298
299 BOOST_CHECK(outRecord != pitEntry->out_end());
300
301 // RTT between B and D
302 this->advanceClocks(time::milliseconds(5), time::milliseconds(160));
303 outRecord = pitEntry->getOutRecord(linkBD->getFace(nodeB));
304
305 BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nInData, i);
306
307 BOOST_CHECK(outRecord == pitEntry->out_end());
308
309 // Data is returned for the ping after 15 ms - will result in false measurement
310 // 14+16-15 = 15ms
311 // Since outRecord == pitEntry->out_end()
312 this->advanceClocks(time::milliseconds(1), time::milliseconds(15));
313 BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nInData, i+1);
314
315 break;
316 }
317 }
318}
319
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -0500320BOOST_FIXTURE_TEST_CASE(IgnoreTimeouts, AsfStrategyParametersGridFixture)
321{
322 // Both nodeB and nodeD have FIB entries to reach the producer
323 topo.registerPrefix(nodeB, linkBC->getFace(nodeB), PRODUCER_PREFIX);
324 topo.registerPrefix(nodeD, linkCD->getFace(nodeD), PRODUCER_PREFIX);
325
326 // Send 15 interests let it change to use the 10 ms link
327 runConsumer(15);
328
329 int outInterestsBeforeFailure = linkAD->getFace(nodeA).getCounters().nOutInterests;
330
331 // Bring down 10 ms link
332 linkAB->fail();
333
334 // Send 6 interests, first 5 will be ignored and on the 6th it will record the timeout
335 // ready to switch for the next interest
336 runConsumer(6);
337
338 // Check that link has not been switched to 100 ms because n-silent-timeouts = 5
339 BOOST_CHECK_EQUAL(linkAD->getFace(nodeA).getCounters().nOutInterests - outInterestsBeforeFailure, 0);
340
341 // Send 5 interests, check that 100 ms link is used
342 runConsumer(5);
343
344 BOOST_CHECK_EQUAL(linkAD->getFace(nodeA).getCounters().nOutInterests - outInterestsBeforeFailure, 5);
345}
346
347BOOST_FIXTURE_TEST_CASE(ProbingInterval, AsfStrategyParametersGridFixture)
348{
349 // Both nodeB and nodeD have FIB entries to reach the producer
350 topo.registerPrefix(nodeB, linkBC->getFace(nodeB), PRODUCER_PREFIX);
351 topo.registerPrefix(nodeD, linkCD->getFace(nodeD), PRODUCER_PREFIX);
352
353 // Send 6 interests let it change to use the 10 ms link
354 runConsumer(6);
355
356 shared_ptr<TopologyLink> linkAC = topo.addLink("AC", time::milliseconds(5), {nodeA, nodeD});
357 topo.registerPrefix(nodeA, linkAC->getFace(nodeA), PRODUCER_PREFIX, 1);
358
359 BOOST_CHECK_EQUAL(linkAC->getFace(nodeA).getCounters().nOutInterests, 0);
360
361 // After 30 seconds a probe would be sent that would switch make ASF switch
362 runConsumer(30);
363
364 BOOST_CHECK_EQUAL(linkAC->getFace(nodeA).getCounters().nOutInterests, 1);
365}
366
367class ParametersFixture
368{
369public:
370 void
371 checkValidity(std::string parameters, bool isCorrect)
372 {
373 Name strategyName(Name(AsfStrategy::getStrategyName()).append(parameters));
374 if (isCorrect) {
375 BOOST_CHECK_NO_THROW(make_unique<AsfStrategy>(forwarder, strategyName));
376 }
377 else {
378 BOOST_CHECK_THROW(make_unique<AsfStrategy>(forwarder, strategyName), std::invalid_argument);
379 }
380 }
381
382protected:
383 Forwarder forwarder;
384};
385
386BOOST_FIXTURE_TEST_CASE(InstantiationTest, ParametersFixture)
387{
388 checkValidity("/probing-interval~30000/n-silent-timeouts~5", true);
389 checkValidity("/n-silent-timeouts~5/probing-interval~30000", true);
390 checkValidity("/probing-interval~30000", true);
391 checkValidity("/n-silent-timeouts~5", true);
392 checkValidity("", true);
393
394 checkValidity("/probing-interval~500", false); // At least 1 seconds
395 checkValidity("/probing-interval~-5000", false);
396 checkValidity("/n-silent-timeouts~-5", false);
397 checkValidity("/n-silent-timeouts~-5/probing-interval~-30000", false);
398 checkValidity("/n-silent-timeouts", false);
399 checkValidity("/probing-interval~", false);
400 checkValidity("/~1000", false);
401 checkValidity("/probing-interval~foo", false);
402 checkValidity("/n-silent-timeouts~1~2", false);
403}
404
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000405BOOST_AUTO_TEST_SUITE_END() // TestAsfStrategy
406BOOST_AUTO_TEST_SUITE_END() // Fw
407
408} // namespace tests
409} // namespace asf
410} // namespace fw
411} // namespace nfd