blob: 0ab54efa1ccd7f0e8ae05f6aac88554073849d96 [file] [log] [blame]
Junxiao Shi67ba8d22015-08-21 21:21:28 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesavento87fc0f82018-04-11 23:43:51 -04002/*
Alexander Afanasyev4400e422021-02-17 11:17:33 -05003 * Copyright (c) 2014-2021, Regents of the University of California,
Junxiao Shi67ba8d22015-08-21 21:21:28 -07004 * 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/multicast-strategy.hpp"
Davide Pesavento2cae8ca2019-04-18 20:48:05 -040027#include "common/global.hpp"
Junxiao Shi67ba8d22015-08-21 21:21:28 -070028
29#include "tests/test-common.hpp"
Davide Pesaventocf7db2f2019-03-24 23:17:28 -040030#include "tests/daemon/face/dummy-face.hpp"
Alexander Afanasyev40604e32021-02-17 12:06:26 -050031#include "choose-strategy.hpp"
Davide Pesavento3dade002019-03-19 11:29:56 -060032#include "strategy-tester.hpp"
Ashlesh Gawandec1d48372020-08-02 22:30:11 -070033#include "topology-tester.hpp"
Junxiao Shi67ba8d22015-08-21 21:21:28 -070034
35namespace nfd {
36namespace fw {
37namespace tests {
38
Davide Pesavento14e71f02019-03-28 17:35:25 -040039using MulticastStrategyTester = StrategyTester<MulticastStrategy>;
Junxiao Shi890afe92016-12-15 14:34:34 +000040NFD_REGISTER_STRATEGY(MulticastStrategyTester);
41
Davide Pesaventocf7db2f2019-03-24 23:17:28 -040042class MulticastStrategyFixture : public GlobalIoTimeFixture
Junxiao Shi890afe92016-12-15 14:34:34 +000043{
44protected:
45 MulticastStrategyFixture()
Alexander Afanasyev40604e32021-02-17 12:06:26 -050046 : strategy(choose<MulticastStrategyTester>(forwarder))
47 , face1(make_shared<DummyFace>())
Junxiao Shi890afe92016-12-15 14:34:34 +000048 , face2(make_shared<DummyFace>())
49 , face3(make_shared<DummyFace>())
50 {
Davide Pesaventoa4abfb02019-10-06 16:02:56 -040051 faceTable.add(face1);
52 faceTable.add(face2);
53 faceTable.add(face3);
Junxiao Shi890afe92016-12-15 14:34:34 +000054 }
55
56protected:
Davide Pesaventoa4abfb02019-10-06 16:02:56 -040057 FaceTable faceTable;
58 Forwarder forwarder{faceTable};
Alexander Afanasyev40604e32021-02-17 12:06:26 -050059 MulticastStrategyTester& strategy;
Davide Pesaventoa4abfb02019-10-06 16:02:56 -040060 Fib& fib{forwarder.getFib()};
61 Pit& pit{forwarder.getPit()};
62
Junxiao Shi890afe92016-12-15 14:34:34 +000063 shared_ptr<DummyFace> face1;
64 shared_ptr<DummyFace> face2;
65 shared_ptr<DummyFace> face3;
66};
67
Junxiao Shi5e5e4452015-09-24 16:56:52 -070068BOOST_AUTO_TEST_SUITE(Fw)
Junxiao Shi890afe92016-12-15 14:34:34 +000069BOOST_FIXTURE_TEST_SUITE(TestMulticastStrategy, MulticastStrategyFixture)
Junxiao Shi67ba8d22015-08-21 21:21:28 -070070
Ashlesh Gawandec1d48372020-08-02 22:30:11 -070071BOOST_AUTO_TEST_CASE(Bug5123)
72{
73 fib::Entry& fibEntry = *fib.insert(Name()).first;
74 fib.addOrUpdateNextHop(fibEntry, *face2, 0);
75
76 // Send an Interest from face 1 to face 2
77 shared_ptr<Interest> interest = makeInterest("ndn:/H0D6i5fc");
78 shared_ptr<pit::Entry> pitEntry = pit.insert(*interest).first;
79 pitEntry->insertOrUpdateInRecord(*face1, *interest);
80
81 strategy.afterReceiveInterest(FaceEndpoint(*face1, 0), *interest, pitEntry);
82 BOOST_CHECK_EQUAL(strategy.rejectPendingInterestHistory.size(), 0);
83
84 // Advance more than default suppression
85 this->advanceClocks(15_ms);
86
87 // Get same interest from face 2 which does not have anywhere to go
88 pitEntry = pit.insert(*interest).first;
89 pitEntry->insertOrUpdateInRecord(*face2, *interest);
90
91 strategy.afterReceiveInterest(FaceEndpoint(*face2, 0), *interest, pitEntry);
92 // Since the interest is same as the one sent out by face 1 pit should not be rejected
93 // as any data coming back should be able to satisfy original interest from face 1
94 BOOST_CHECK_EQUAL(strategy.rejectPendingInterestHistory.size(), 0);
95
96 /*
97 * +---------+ +---------+ +---------+
98 * | nodeA |------------| nodeB |----------| nodeC |
99 * +---------+ 10ms +---------+ 100ms +---------+
100 */
101
102 const Name PRODUCER_PREFIX = "/ndn/edu/nodeC/ping";
103
104 TopologyTester topo;
105 TopologyNode nodeA = topo.addForwarder("A"),
106 nodeB = topo.addForwarder("B"),
107 nodeC = topo.addForwarder("C");
108
109 for (TopologyNode node : {nodeA, nodeB, nodeC}) {
110 topo.setStrategy<MulticastStrategy>(node);
111 }
112
113 shared_ptr<TopologyLink> linkAB = topo.addLink("AB", 10_ms, {nodeA, nodeB}),
114 linkBC = topo.addLink("BC", 100_ms, {nodeB, nodeC});
115
116 shared_ptr<TopologyAppLink> appA = topo.addAppFace("cA", nodeA),
117 appB = topo.addAppFace("cB", nodeB),
118 pingServer = topo.addAppFace("p", nodeC, PRODUCER_PREFIX);
119 topo.addEchoProducer(pingServer->getClientFace());
120 topo.registerPrefix(nodeA, linkAB->getFace(nodeA), PRODUCER_PREFIX, 10);
121 topo.registerPrefix(nodeB, linkAB->getFace(nodeB), PRODUCER_PREFIX, 10);
122 topo.registerPrefix(nodeB, linkBC->getFace(nodeB), PRODUCER_PREFIX, 100);
123
124 Name name(PRODUCER_PREFIX);
125 name.appendTimestamp();
126 interest = makeInterest(name);
127 appA->getClientFace().expressInterest(*interest, nullptr, nullptr, nullptr);
128
129 this->advanceClocks(10_ms, 20_ms);
130
131 // AppB expresses the same interest
132 interest->refreshNonce();
133 appB->getClientFace().expressInterest(*interest, nullptr, nullptr, nullptr);
134 this->advanceClocks(10_ms, 200_ms);
135
136 // Data should have made to appB
137 BOOST_CHECK_EQUAL(linkBC->getFace(nodeB).getCounters().nInData, 1);
138 BOOST_CHECK_EQUAL(linkAB->getFace(nodeA).getCounters().nInData, 0);
139
140 this->advanceClocks(10_ms, 10_ms);
141 // nodeA should have gotten the data successfully
142 BOOST_CHECK_EQUAL(linkAB->getFace(nodeA).getCounters().nInData, 1);
143 BOOST_CHECK_EQUAL(topo.getForwarder(nodeA).getCounters().nUnsolicitedData, 0);
144}
145
Junxiao Shi67ba8d22015-08-21 21:21:28 -0700146BOOST_AUTO_TEST_CASE(Forward2)
147{
Junxiao Shia6de4292016-07-12 02:08:10 +0000148 fib::Entry& fibEntry = *fib.insert(Name()).first;
Ju Pand8315bf2019-07-31 06:59:07 +0000149 fib.addOrUpdateNextHop(fibEntry, *face1, 0);
150 fib.addOrUpdateNextHop(fibEntry, *face2, 0);
151 fib.addOrUpdateNextHop(fibEntry, *face3, 0);
Junxiao Shi67ba8d22015-08-21 21:21:28 -0700152
153 shared_ptr<Interest> interest = makeInterest("ndn:/H0D6i5fc");
Junxiao Shi67ba8d22015-08-21 21:21:28 -0700154 shared_ptr<pit::Entry> pitEntry = pit.insert(*interest).first;
Md Ashiqur Rahmanc88d2d42019-08-28 20:19:47 +0000155 pitEntry->insertOrUpdateInRecord(*face3, *interest);
Junxiao Shi67ba8d22015-08-21 21:21:28 -0700156
ashiqopuc7079482019-02-20 05:34:37 +0000157 strategy.afterReceiveInterest(FaceEndpoint(*face3, 0), *interest, pitEntry);
Junxiao Shi5e5e4452015-09-24 16:56:52 -0700158 BOOST_CHECK_EQUAL(strategy.rejectPendingInterestHistory.size(), 0);
159 BOOST_CHECK_EQUAL(strategy.sendInterestHistory.size(), 2);
160 std::set<FaceId> sentInterestFaceIds;
161 std::transform(strategy.sendInterestHistory.begin(), strategy.sendInterestHistory.end(),
162 std::inserter(sentInterestFaceIds, sentInterestFaceIds.end()),
163 [] (const MulticastStrategyTester::SendInterestArgs& args) {
164 return args.outFaceId;
165 });
166 std::set<FaceId> expectedInterestFaceIds{face1->getId(), face2->getId()};
167 BOOST_CHECK_EQUAL_COLLECTIONS(sentInterestFaceIds.begin(), sentInterestFaceIds.end(),
168 expectedInterestFaceIds.begin(), expectedInterestFaceIds.end());
Ashlesh Gawandee38e2612017-02-25 07:23:41 +0000169
Ashlesh Gawandeecdbe5f2017-03-04 03:08:24 +0000170 const time::nanoseconds TICK = time::duration_cast<time::nanoseconds>(
171 MulticastStrategy::RETX_SUPPRESSION_INITIAL * 0.1);
Junxiao Shi67ba8d22015-08-21 21:21:28 -0700172
Ashlesh Gawandeecdbe5f2017-03-04 03:08:24 +0000173 // downstream retransmits frequently, but the strategy should not send Interests
174 // more often than DEFAULT_MIN_RETX_INTERVAL
175 scheduler::EventId retxFrom4Evt;
176 size_t nSentLast = strategy.sendInterestHistory.size();
177 time::steady_clock::TimePoint timeSentLast = time::steady_clock::now();
Davide Pesavento87fc0f82018-04-11 23:43:51 -0400178 std::function<void()> periodicalRetxFrom4; // let periodicalRetxFrom4 lambda capture itself
Ashlesh Gawandeecdbe5f2017-03-04 03:08:24 +0000179 periodicalRetxFrom4 = [&] {
Md Ashiqur Rahmanc88d2d42019-08-28 20:19:47 +0000180 pitEntry->insertOrUpdateInRecord(*face3, *interest);
ashiqopuc7079482019-02-20 05:34:37 +0000181 strategy.afterReceiveInterest(FaceEndpoint(*face3, 0), *interest, pitEntry);
Junxiao Shi67ba8d22015-08-21 21:21:28 -0700182
Ashlesh Gawandeecdbe5f2017-03-04 03:08:24 +0000183 size_t nSent = strategy.sendInterestHistory.size();
184 if (nSent > nSentLast) {
185 // Multicast strategy should multicast the interest to other two faces
186 BOOST_CHECK_EQUAL(nSent - nSentLast, 2);
Davide Pesavento3dade002019-03-19 11:29:56 -0600187 auto timeSent = time::steady_clock::now();
Ashlesh Gawandeecdbe5f2017-03-04 03:08:24 +0000188 BOOST_CHECK_GE(timeSent - timeSentLast, TICK * 8);
189 nSentLast = nSent;
190 timeSentLast = timeSent;
191 }
Junxiao Shi67ba8d22015-08-21 21:21:28 -0700192
Davide Pesavento3dade002019-03-19 11:29:56 -0600193 retxFrom4Evt = getScheduler().schedule(TICK * 5, periodicalRetxFrom4);
Ashlesh Gawandeecdbe5f2017-03-04 03:08:24 +0000194 };
195 periodicalRetxFrom4();
196 this->advanceClocks(TICK, MulticastStrategy::RETX_SUPPRESSION_MAX * 16);
Davide Pesavento3dade002019-03-19 11:29:56 -0600197 retxFrom4Evt.cancel();
Junxiao Shi67ba8d22015-08-21 21:21:28 -0700198}
199
Alexander Afanasyev4400e422021-02-17 11:17:33 -0500200BOOST_AUTO_TEST_CASE(LoopingInterest)
Junxiao Shi67ba8d22015-08-21 21:21:28 -0700201{
Junxiao Shia6de4292016-07-12 02:08:10 +0000202 fib::Entry& fibEntry = *fib.insert(Name()).first;
Ju Pand8315bf2019-07-31 06:59:07 +0000203 fib.addOrUpdateNextHop(fibEntry, *face1, 0);
Junxiao Shi67ba8d22015-08-21 21:21:28 -0700204
205 shared_ptr<Interest> interest = makeInterest("ndn:/H0D6i5fc");
Junxiao Shi67ba8d22015-08-21 21:21:28 -0700206 shared_ptr<pit::Entry> pitEntry = pit.insert(*interest).first;
Md Ashiqur Rahmanc88d2d42019-08-28 20:19:47 +0000207 pitEntry->insertOrUpdateInRecord(*face1, *interest);
Junxiao Shi67ba8d22015-08-21 21:21:28 -0700208
ashiqopuc7079482019-02-20 05:34:37 +0000209 strategy.afterReceiveInterest(FaceEndpoint(*face1, 0), *interest, pitEntry);
Alexander Afanasyev4400e422021-02-17 11:17:33 -0500210 BOOST_CHECK_EQUAL(strategy.rejectPendingInterestHistory.size(), 0);
Junxiao Shi5e5e4452015-09-24 16:56:52 -0700211 BOOST_CHECK_EQUAL(strategy.sendInterestHistory.size(), 0);
Junxiao Shi67ba8d22015-08-21 21:21:28 -0700212}
213
Alexander Afanasyev40604e32021-02-17 12:06:26 -0500214BOOST_AUTO_TEST_CASE(ForwardAsync)
215{
216 fib::Entry& fibEntry = *fib.insert(Name()).first;
217 fib.addOrUpdateNextHop(fibEntry, *face1, 0);
218 fib.addOrUpdateNextHop(fibEntry, *face2, 0);
219
220 shared_ptr<Interest> interest = makeInterest("ndn:/H0D6i5fc");
221 shared_ptr<pit::Entry> pitEntry = pit.insert(*interest).first;
222 pitEntry->insertOrUpdateInRecord(*face1, *interest);
223
224 strategy.afterReceiveInterest(FaceEndpoint(*face1, 0), *interest, pitEntry);
225 BOOST_CHECK_EQUAL(strategy.rejectPendingInterestHistory.size(), 0);
226 BOOST_CHECK_EQUAL(strategy.sendInterestHistory.size(), 1);
227
228 fib.addOrUpdateNextHop(fibEntry, *face3, 0);
229 BOOST_CHECK_EQUAL(strategy.rejectPendingInterestHistory.size(), 0);
230 BOOST_CHECK_EQUAL(strategy.sendInterestHistory.size(), 2);
231}
232
233BOOST_AUTO_TEST_SUITE(LocalhopScope)
234
235class ForwardAsyncFixture : public MulticastStrategyFixture
236{
237protected:
238 shared_ptr<Face> inFace1;
239 shared_ptr<Face> inFace2;
240 shared_ptr<Face> fibFace1;
241 shared_ptr<Face> fibFace2;
242 shared_ptr<Face> newFibFace;
243
244 size_t expectedInterests = 0;
245};
246
247class BasicNonLocal : public ForwardAsyncFixture
248{
249protected:
250 BasicNonLocal()
251 {
252 inFace1 = face1;
253 // inFace2 = nullptr;
254 fibFace1 = face1;
255 fibFace2 = face2;
256 newFibFace = face3;
257 expectedInterests = 0; // anything received on non-local face can only be sent to local face
258 }
259};
260
261class NewFibLocal : public ForwardAsyncFixture
262{
263protected:
264 NewFibLocal()
265 {
266 inFace1 = face1;
267 // inFace2 = nullptr;
268 fibFace1 = face1;
269 fibFace2 = face2;
270 newFibFace = make_shared<DummyFace>("dummy://", "dummy://", ndn::nfd::FACE_SCOPE_LOCAL);
271 expectedInterests = 1;
272
273 faceTable.add(newFibFace);
274 }
275};
276
277class InFaceLocal : public ForwardAsyncFixture
278{
279protected:
280 InFaceLocal()
281 {
282 inFace1 = make_shared<DummyFace>("dummy://", "dummy://", ndn::nfd::FACE_SCOPE_LOCAL);
283 // inFace2 = nullptr;
284 fibFace1 = face1;
285 fibFace2 = face2;
286 newFibFace = face3;
287 expectedInterests = 1;
288
289 faceTable.add(inFace1);
290 }
291};
292
293class InFaceLocalSameNewFace : public ForwardAsyncFixture
294{
295protected:
296 InFaceLocalSameNewFace()
297 {
298 inFace1 = make_shared<DummyFace>("dummy://", "dummy://", ndn::nfd::FACE_SCOPE_LOCAL);
299 // inFace2 = nullptr;
300 fibFace1 = face1;
301 fibFace2 = face2;
302 newFibFace = inFace1;
303 expectedInterests = 0;
304
305 faceTable.add(inFace1);
306 }
307};
308
309class InFaceLocalAdHocSameNewFace : public ForwardAsyncFixture
310{
311protected:
312 InFaceLocalAdHocSameNewFace()
313 {
314 inFace1 = make_shared<DummyFace>("dummy://", "dummy://", ndn::nfd::FACE_SCOPE_LOCAL,
315 ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
316 ndn::nfd::LINK_TYPE_AD_HOC);
317 // inFace2 = nullptr;
318 fibFace1 = face1;
319 fibFace2 = face2;
320 newFibFace = inFace1;
321 expectedInterests = 1;
322
323 faceTable.add(inFace1);
324 }
325};
326
327class InFaceLocalAndNonLocal1 : public ForwardAsyncFixture
328{
329protected:
330 InFaceLocalAndNonLocal1()
331 {
332 inFace1 = make_shared<DummyFace>("dummy://", "dummy://", ndn::nfd::FACE_SCOPE_LOCAL);
333 inFace2 = face1;
334 fibFace1 = face1;
335 fibFace2 = face2;
336 newFibFace = face3;
337 expectedInterests = 1;
338
339 faceTable.add(inFace1);
340 }
341};
342
343class InFaceLocalAndNonLocal2 : public ForwardAsyncFixture
344{
345protected:
346 InFaceLocalAndNonLocal2()
347 {
348 inFace1 = face1;
349 inFace2 = make_shared<DummyFace>("dummy://", "dummy://", ndn::nfd::FACE_SCOPE_LOCAL);
350 fibFace1 = face1;
351 fibFace2 = face2;
352 newFibFace = face3;
353 expectedInterests = 1;
354
355 faceTable.add(inFace2);
356 }
357};
358
359class InFaceSelection1 : public ForwardAsyncFixture
360{
361protected:
362 InFaceSelection1()
363 {
364 inFace1 = face1;
365 // inFace2 = nullptr;
366 fibFace1 = face3;
367 fibFace2 = face2;
368 newFibFace = face1;
369
370 expectedInterests = 0;
371 }
372};
373
374class InFaceSelection2 : public ForwardAsyncFixture
375{
376protected:
377 InFaceSelection2()
378 {
379 inFace1 = face2;
380 inFace2 = face1;
381 fibFace1 = face2;
382 fibFace2 = face3;
383 newFibFace = face1;
384
385 // this test will trigger the check for additional branch, but it
386 // still is not going to pass the localhop check
387 expectedInterests = 0;
388 }
389};
390
391using Tests = boost::mpl::vector<
392 BasicNonLocal,
393 NewFibLocal,
394 InFaceLocal,
395 InFaceLocalSameNewFace,
396 InFaceLocalAdHocSameNewFace,
397 InFaceLocalAndNonLocal1,
398 InFaceLocalAndNonLocal2,
399 InFaceSelection1,
400 InFaceSelection2
401>;
402
403BOOST_FIXTURE_TEST_CASE_TEMPLATE(ForwardAsync, T, Tests, T)
404{
405 fib::Entry& fibEntry = *this->fib.insert(Name("/localhop")).first;
406 this->fib.addOrUpdateNextHop(fibEntry, *this->fibFace1, 0);
407 this->fib.addOrUpdateNextHop(fibEntry, *this->fibFace2, 0);
408
409 shared_ptr<Interest> interest = makeInterest("ndn:/localhop/H0D6i5fc");
410 shared_ptr<pit::Entry> pitEntry = this->pit.insert(*interest).first;
411 pitEntry->insertOrUpdateInRecord(*this->inFace1, *interest);
412 this->strategy.afterReceiveInterest(FaceEndpoint(*this->inFace1, 0), *interest, pitEntry);
413
414 if (this->inFace2 != nullptr) {
415 shared_ptr<Interest> interest2 = makeInterest("ndn:/localhop/H0D6i5fc");
416 pitEntry->insertOrUpdateInRecord(*this->inFace2, *interest2);
417 this->strategy.afterReceiveInterest(FaceEndpoint(*this->inFace2, 0), *interest2, pitEntry);
418 }
419
420 this->strategy.sendInterestHistory.clear();
421 this->fib.addOrUpdateNextHop(fibEntry, *this->newFibFace, 0);
422 BOOST_CHECK_EQUAL(this->strategy.sendInterestHistory.size(), this->expectedInterests);
423}
424
425BOOST_AUTO_TEST_SUITE_END() // LocalhopScope
Junxiao Shi5e5e4452015-09-24 16:56:52 -0700426BOOST_AUTO_TEST_SUITE_END() // TestMulticastStrategy
427BOOST_AUTO_TEST_SUITE_END() // Fw
Junxiao Shi67ba8d22015-08-21 21:21:28 -0700428
429} // namespace tests
430} // namespace fw
431} // namespace nfd