diff --git a/daemon/fw/asf-strategy.cpp b/daemon/fw/asf-strategy.cpp index 871ff2f7..baf8230a 100644 --- a/daemon/fw/asf-strategy.cpp +++ b/daemon/fw/asf-strategy.cpp @@ -60,7 +60,7 @@ AsfStrategy::AsfStrategy(Forwarder& forwarder, const Name& name) const Name& AsfStrategy::getStrategyName() { - static Name strategyName("/localhost/nfd/strategy/asf/%FD%01"); + static Name strategyName("/localhost/nfd/strategy/asf/%FD%02"); return strategyName; } @@ -246,7 +246,8 @@ AsfStrategy::getBestFaceForForwarding(const fib::Entry& fibEntry, const Interest for (const fib::NextHop& hop : fibEntry.getNextHops()) { Face& hopFace = hop.getFace(); - if (hopFace.getId() == inFace.getId() || wouldViolateScope(inFace, interest, hopFace)) { + if ((hopFace.getId() == inFace.getId() && hopFace.getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) || + wouldViolateScope(inFace, interest, hopFace)) { continue; } diff --git a/daemon/fw/best-route-strategy2.cpp b/daemon/fw/best-route-strategy2.cpp index 1666e81b..11add678 100644 --- a/daemon/fw/best-route-strategy2.cpp +++ b/daemon/fw/best-route-strategy2.cpp @@ -57,7 +57,7 @@ BestRouteStrategy2::BestRouteStrategy2(Forwarder& forwarder, const Name& name) const Name& BestRouteStrategy2::getStrategyName() { - static Name strategyName("/localhost/nfd/strategy/best-route/%FD%04"); + static Name strategyName("/localhost/nfd/strategy/best-route/%FD%05"); return strategyName; } @@ -78,8 +78,8 @@ isNextHopEligible(const Face& inFace, const Interest& interest, { const Face& outFace = nexthop.getFace(); - // do not forward back to the same face - if (&outFace == &inFace) + // do not forward back to the same face, unless it is ad hoc + if (outFace.getId() == inFace.getId() && outFace.getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) return false; // forwarding would violate scope diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp index 052258a2..41ab2e2d 100644 --- a/daemon/fw/forwarder.cpp +++ b/daemon/fw/forwarder.cpp @@ -375,7 +375,8 @@ Forwarder::onIncomingData(Face& inFace, const Data& data) // foreach pending downstream for (Face* pendingDownstream : pendingDownstreams) { - if (pendingDownstream == &inFace) { + if (pendingDownstream->getId() == inFace.getId() && + pendingDownstream->getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) { continue; } // goto outgoing Data pipeline @@ -595,7 +596,7 @@ Forwarder::insertDeadNonceList(pit::Entry& pitEntry, bool isSatisfied, } // Dead Nonce List insert - if (upstream == 0) { + if (upstream == nullptr) { // insert all outgoing Nonces const pit::OutRecordCollection& outRecords = pitEntry.getOutRecords(); std::for_each(outRecords.begin(), outRecords.end(), diff --git a/daemon/fw/multicast-strategy.cpp b/daemon/fw/multicast-strategy.cpp index 9543e6f1..7b38bc1a 100644 --- a/daemon/fw/multicast-strategy.cpp +++ b/daemon/fw/multicast-strategy.cpp @@ -58,7 +58,7 @@ MulticastStrategy::MulticastStrategy(Forwarder& forwarder, const Name& name) const Name& MulticastStrategy::getStrategyName() { - static Name strategyName("/localhost/nfd/strategy/multicast/%FD%02"); + static Name strategyName("/localhost/nfd/strategy/multicast/%FD%03"); return strategyName; } @@ -85,12 +85,15 @@ MulticastStrategy::afterReceiveInterest(const Face& inFace, const Interest& inte for (const auto& nexthop : nexthops) { Face& outFace = nexthop.getFace(); - if (&outFace != &inFace && !wouldViolateScope(inFace, interest, outFace)) { - this->sendInterest(pitEntry, outFace, interest); - NFD_LOG_DEBUG(interest << " from=" << inFace.getId() - << " pitEntry-to=" << outFace.getId()); - ++nEligibleNextHops; + if ((outFace.getId() == inFace.getId() && outFace.getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) || + wouldViolateScope(inFace, interest, outFace)) { + continue; } + + this->sendInterest(pitEntry, outFace, interest); + NFD_LOG_DEBUG(interest << " from=" << inFace.getId() + << " pitEntry-to=" << outFace.getId()); + ++nEligibleNextHops; } if (nEligibleNextHops == 0) { diff --git a/tests/daemon/fw/ad-hoc-forwarding.t.cpp b/tests/daemon/fw/ad-hoc-forwarding.t.cpp new file mode 100644 index 00000000..7e81893a --- /dev/null +++ b/tests/daemon/fw/ad-hoc-forwarding.t.cpp @@ -0,0 +1,155 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/** + * Copyright (c) 2014-2017, Regents of the University of California, + * Arizona Board of Regents, + * Colorado State University, + * University Pierre & Marie Curie, Sorbonne University, + * Washington University in St. Louis, + * Beijing Institute of Technology, + * The University of Memphis. + * + * This file is part of NFD (Named Data Networking Forwarding Daemon). + * See AUTHORS.md for complete list of NFD authors and contributors. + * + * NFD is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * NFD, e.g., in COPYING.md file. If not, see . + */ + +/** \file + * This test suite checks that forwarding can relay Interest and Data via ad hoc face. + */ + +// Strategies that can forward Interest to an ad hoc face even if it's the downstream, +// sorted alphabetically. +#include "fw/asf-strategy.hpp" +#include "fw/best-route-strategy2.hpp" +#include "fw/multicast-strategy.hpp" + +#include "tests/test-common.hpp" +#include "topology-tester.hpp" +#include + +namespace nfd { +namespace fw { +namespace tests { + +using namespace nfd::tests; + +template +class AdHocForwardingFixture : public UnitTestTimeFixture +{ +protected: + AdHocForwardingFixture() + { + nodeA = topo.addForwarder("A"); + nodeB = topo.addForwarder("B"); + nodeC = topo.addForwarder("C"); + + for (TopologyNode node : {nodeA, nodeB, nodeC}) { + topo.setStrategy(node); + } + + auto wireless = topo.addLink("ABC", time::milliseconds(10), {nodeA, nodeB, nodeC}, + ndn::nfd::LINK_TYPE_AD_HOC); + wireless->block(nodeA, nodeC); + wireless->block(nodeC, nodeA); + faceA = &wireless->getFace(nodeA); + faceB = &wireless->getFace(nodeB); + faceC = &wireless->getFace(nodeC); + + appA = topo.addAppFace("consumer", nodeA); + topo.registerPrefix(nodeA, *faceA, "/P"); + appC = topo.addAppFace("producer", nodeC, "/P"); + topo.addEchoProducer(appC->getClientFace(), "/P"); + } + +protected: + TopologyTester topo; + TopologyNode nodeA; + TopologyNode nodeB; + TopologyNode nodeC; + Face* faceA; + Face* faceB; + Face* faceC; + shared_ptr appA; + shared_ptr appC; +}; + +BOOST_AUTO_TEST_SUITE(Fw) +BOOST_FIXTURE_TEST_SUITE(TestAdHocForwarding, BaseFixture) + +using Strategies = boost::mpl::vector< + AsfStrategy, + BestRouteStrategy2, + MulticastStrategy +>; + +BOOST_FIXTURE_TEST_CASE_TEMPLATE(SingleNexthop, S, Strategies, + AdHocForwardingFixture) +{ + // +---+---+ + // A B C + // + // A is the consumer. C is the producer. + // B should relay Interest/Data between A and C. + + this->topo.registerPrefix(this->nodeB, *this->faceB, "/P"); + this->topo.addIntervalConsumer(this->appA->getClientFace(), "/P", time::milliseconds(100), 10); + this->advanceClocks(time::milliseconds(5), time::milliseconds(1200)); + + // Consumer should receive Data, and B should be relaying. + BOOST_CHECK_EQUAL(this->faceB->getCounters().nInInterests, 10); + BOOST_CHECK_EQUAL(this->faceB->getCounters().nOutInterests, 10); + BOOST_CHECK_EQUAL(this->appC->getForwarderFace().getCounters().nOutInterests, 10); + BOOST_CHECK_EQUAL(this->faceB->getCounters().nInData, 10); + BOOST_CHECK_EQUAL(this->faceB->getCounters().nOutData, 10); + BOOST_CHECK_EQUAL(this->appA->getForwarderFace().getCounters().nOutData, 10); +} + +BOOST_FIXTURE_TEST_CASE_TEMPLATE(SecondNexthop, S, Strategies, + AdHocForwardingFixture) +{ + // +---+---+ + // A B C + // | + // D + // + // A is the consumer. C is the producer. + // B's first nexthop is D, but B-D link has failed, so B should relay Interest/Data between A and C. + + TopologyNode nodeD = this->topo.addForwarder("D"); + shared_ptr linkBD = this->topo.addLink("BD", time::milliseconds(5), {this->nodeB, nodeD}); + this->topo.registerPrefix(this->nodeB, linkBD->getFace(this->nodeB), "/P", 5); + linkBD->fail(); + this->topo.registerPrefix(this->nodeB, *this->faceB, "/P", 10); + + // Two interval consumers are expressing Interests with same name 40ms apart, + // so that Interests from the second interval consumer are considered retransmission. + this->topo.addIntervalConsumer(this->appA->getClientFace(), "/P", time::milliseconds(100), 50, 1); + this->advanceClocks(time::milliseconds(5), time::milliseconds(40)); + this->topo.addIntervalConsumer(this->appA->getClientFace(), "/P", time::milliseconds(100), 50, 1); + this->advanceClocks(time::milliseconds(5), time::milliseconds(5400)); + + // Consumer should receive Data, and B should be relaying at least some Interest/Data. + BOOST_CHECK_GE(this->faceB->getCounters().nInInterests, 50); + BOOST_CHECK_GE(this->faceB->getCounters().nOutInterests, 25); + BOOST_CHECK_GE(this->appC->getForwarderFace().getCounters().nOutInterests, 25); + BOOST_CHECK_GE(this->faceB->getCounters().nInData, 25); + BOOST_CHECK_GE(this->faceB->getCounters().nOutData, 25); + BOOST_CHECK_GE(this->appA->getForwarderFace().getCounters().nOutData, 25); +} + +BOOST_AUTO_TEST_SUITE_END() // TestAdHocForwarding +BOOST_AUTO_TEST_SUITE_END() // Fw + +} // namespace tests +} // namespace fw +} // namespace nfd diff --git a/tests/daemon/fw/strategy-instantiation.t.cpp b/tests/daemon/fw/strategy-instantiation.t.cpp index 0e012b45..391c7610 100644 --- a/tests/daemon/fw/strategy-instantiation.t.cpp +++ b/tests/daemon/fw/strategy-instantiation.t.cpp @@ -75,11 +75,11 @@ public: using Tests = boost::mpl::vector< Test, - Test, + Test, Test, - Test, + Test, Test, - Test, + Test, Test >; diff --git a/tests/daemon/fw/topology-tester.cpp b/tests/daemon/fw/topology-tester.cpp index ad684478..fef30806 100644 --- a/tests/daemon/fw/topology-tester.cpp +++ b/tests/daemon/fw/topology-tester.cpp @@ -36,36 +36,43 @@ using face::InternalForwarderTransport; using face::InternalClientTransport; using face::GenericLinkService; -TopologyLink::TopologyLink(const time::nanoseconds& delay) +TopologyLink::TopologyLink(time::nanoseconds delay) : m_isUp(true) { this->setDelay(delay); } void -TopologyLink::setDelay(const time::nanoseconds& delay) +TopologyLink::block(TopologyNode i, TopologyNode j) { - BOOST_ASSERT(delay > time::nanoseconds::zero()); - // zero delay does not work on OSX + m_transports.at(i).blockedDestinations.insert(j); +} +void +TopologyLink::unblock(TopologyNode i, TopologyNode j) +{ + m_transports.at(i).blockedDestinations.erase(j); +} + +void +TopologyLink::setDelay(time::nanoseconds delay) +{ + BOOST_ASSERT(delay > time::nanoseconds::zero()); // zero delay does not work on macOS m_delay = delay; } void TopologyLink::addFace(TopologyNode i, shared_ptr face) { - this->attachTransport(i, dynamic_cast(face->getTransport())); - m_faces[i] = face; -} - -void -TopologyLink::attachTransport(TopologyNode i, InternalTransportBase* transport) -{ - BOOST_ASSERT(transport != nullptr); BOOST_ASSERT(m_transports.count(i) == 0); + auto& nodeTransport = m_transports[i]; - m_transports[i] = transport; - transport->afterSend.connect([this, i] (const Block& packet) { this->transmit(i, packet); }); + nodeTransport.face = face; + + nodeTransport.transport = dynamic_cast(face->getTransport()); + BOOST_ASSERT(nodeTransport.transport != nullptr); + nodeTransport.transport->afterSend.connect( + [this, i] (const Block& packet) { this->transmit(i, packet); }); } void @@ -75,12 +82,14 @@ TopologyLink::transmit(TopologyNode i, const Block& packet) return; } + const auto& blockedDestinations = m_transports.at(i).blockedDestinations; + for (const auto& p : m_transports) { - if (p.first == i) { + if (p.first == i || blockedDestinations.count(p.first) > 0) { continue; } - InternalTransportBase* recipient = p.second; + InternalTransportBase* recipient = p.second.transport; this->scheduleReceive(recipient, packet); } } @@ -156,15 +165,17 @@ TopologyTester::addForwarder(const std::string& label) } shared_ptr -TopologyTester::addLink(const std::string& label, const time::nanoseconds& delay, +TopologyTester::addLink(const std::string& label, time::nanoseconds delay, std::initializer_list forwarders, - bool forceMultiAccessFace) + ndn::nfd::LinkType linkType) { auto link = std::make_shared(delay); FaceUri remoteUri("topology://link/" + label); - ndn::nfd::LinkType linkType = (forceMultiAccessFace || forwarders.size() > 2) ? - ndn::nfd::LINK_TYPE_MULTI_ACCESS : - ndn::nfd::LINK_TYPE_POINT_TO_POINT; + if (linkType == ndn::nfd::LINK_TYPE_NONE) { + linkType = forwarders.size() > 2 ? ndn::nfd::LINK_TYPE_MULTI_ACCESS : + ndn::nfd::LINK_TYPE_POINT_TO_POINT; + } + BOOST_ASSERT(forwarders.size() <= 2 || linkType != ndn::nfd::LINK_TYPE_POINT_TO_POINT); for (TopologyNode i : forwarders) { Forwarder& forwarder = this->getForwarder(i); @@ -244,16 +255,23 @@ TopologyTester::addEchoProducer(ndn::Face& face, const Name& prefix) void TopologyTester::addIntervalConsumer(ndn::Face& face, const Name& prefix, - const time::nanoseconds& interval, size_t n) + time::nanoseconds interval, size_t n, int seq) { Name name(prefix); - name.appendTimestamp(); + if (seq >= 0) { + name.appendSequenceNumber(seq); + ++seq; + } + else { + name.appendTimestamp(); + } + shared_ptr interest = makeInterest(name); face.expressInterest(*interest, nullptr, nullptr, nullptr); if (n > 1) { scheduler::schedule(interval, bind(&TopologyTester::addIntervalConsumer, this, - ref(face), prefix, interval, n - 1)); + ref(face), prefix, interval, n - 1, seq)); } } diff --git a/tests/daemon/fw/topology-tester.hpp b/tests/daemon/fw/topology-tester.hpp index 9f97413e..892dc01c 100644 --- a/tests/daemon/fw/topology-tester.hpp +++ b/tests/daemon/fw/topology-tester.hpp @@ -54,7 +54,7 @@ class TopologyLink : noncopyable { public: explicit - TopologyLink(const time::nanoseconds& delay); + TopologyLink(time::nanoseconds delay); /** \brief fail the link, cause packets to be dropped silently */ @@ -72,11 +72,24 @@ public: m_isUp = true; } + /** \brief block transmission from i to j + * + * Packets transmitted by i would not be delivered to j. Packets from j to i are unaffected. + * This can be used to simulate a wireless channel. + */ + void + block(TopologyNode i, TopologyNode j); + + /** \brief unblock transmission from i to j + */ + void + unblock(TopologyNode i, TopologyNode j); + /** \brief change the link delay * \param delay link delay, must be positive */ void - setDelay(const time::nanoseconds& delay); + setDelay(time::nanoseconds delay); /** \brief attach a face to the link * \param i forwarder index @@ -90,15 +103,9 @@ public: Face& getFace(TopologyNode i) { - return *m_faces.at(i); + return *m_transports.at(i).face; } -protected: - /** \brief attach a Transport onto this link - */ - void - attachTransport(TopologyNode i, face::InternalTransportBase* transport); - private: void transmit(TopologyNode i, const Block& packet); @@ -109,8 +116,14 @@ private: private: bool m_isUp; time::nanoseconds m_delay; - std::unordered_map m_transports; - std::unordered_map> m_faces; + + struct NodeTransport + { + face::InternalTransportBase* transport; + shared_ptr face; + std::set blockedDestinations; + }; + std::unordered_map m_transports; }; /** \brief represents a link to a local application @@ -203,15 +216,18 @@ public: } /** \brief makes a link that interconnects two or more forwarders + * \brief linkType desired link type; LINK_TYPE_NONE to use point-to-point for two forwarders + * and multi-access for more than two forwarders; it's an error to specify + * point-to-point when there are more than two forwarders * * A face is created on each of \p forwarders . * When a packet is sent onto one of the faces on this link, * this packet will be received by all other faces on this link after \p delay . */ shared_ptr - addLink(const std::string& label, const time::nanoseconds& delay, + addLink(const std::string& label, time::nanoseconds delay, std::initializer_list forwarders, - bool forceMultiAccessFace = false); + ndn::nfd::LinkType linkType = ndn::nfd::LINK_TYPE_NONE); /** \brief makes a link to local application */ @@ -246,10 +262,11 @@ public: /** \brief creates a consumer application that sends \p n Interests under \p prefix * at \p interval fixed rate. + * \param seq if non-negative, append sequence number instead of timestamp */ void - addIntervalConsumer(ndn::Face& face, const Name& prefix, - const time::nanoseconds& interval, size_t n); + addIntervalConsumer(ndn::Face& face, const Name& prefix, time::nanoseconds interval, + size_t n, int seq = -1); private: bool m_wantPcap = false;