fw: add processing for afterNewNextHop signal
Refs: #4931
Change-Id: I08bddc0ae3ceca0ddb777392ea656876ad6fe701
diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp
index f59963e..5c529a8 100644
--- a/daemon/fw/forwarder.cpp
+++ b/daemon/fw/forwarder.cpp
@@ -75,6 +75,10 @@
cleanupOnFaceRemoval(m_nameTree, m_fib, m_pit, face);
});
+ m_fib.afterNewNextHop.connect([&] (const Name& prefix, const fib::NextHop& nextHop) {
+ this->startProcessNewNextHop(prefix, nextHop);
+ });
+
m_strategyChoice.setDefaultStrategy(getDefaultStrategyName());
}
@@ -504,6 +508,46 @@
}
void
+Forwarder::onNewNextHop(const Name& prefix, const fib::NextHop& nextHop)
+{
+ const auto affectedEntries = this->getNameTree().partialEnumerate(prefix,
+ [&] (const name_tree::Entry& nte) -> std::pair<bool, bool> {
+ const fib::Entry* fibEntry = nte.getFibEntry();
+ const fw::Strategy* strategy = nullptr;
+ if (nte.getStrategyChoiceEntry() != nullptr) {
+ strategy = &nte.getStrategyChoiceEntry()->getStrategy();
+ }
+ // current nte has buffered Interests but no fibEntry (except for the root nte) and the strategy
+ // enables new nexthop behavior, we enumerate the current nte and keep visiting its children.
+ if (nte.getName().size() == 0 ||
+ (strategy != nullptr && strategy->wantNewNextHopTrigger() &&
+ fibEntry == nullptr && nte.hasPitEntries())) {
+ return {true, true};
+ }
+ // we don't need the current nte (no pitEntry or strategy doesn't support new nexthop), but
+ // if the current nte has no fibEntry, it's still possible that its children are affected by
+ // the new nexthop.
+ else if (fibEntry == nullptr) {
+ return {false, true};
+ }
+ // if the current nte has a fibEntry, we ignore the current nte and don't visit its
+ // children because they are already covered by the current nte's fibEntry.
+ else {
+ return {false, false};
+ }
+ });
+
+ for (const auto& nte : affectedEntries) {
+ for (const auto& pitEntry : nte.getPitEntries()) {
+ this->dispatchToStrategy(*pitEntry,
+ [&] (fw::Strategy& strategy) {
+ strategy.afterNewNextHop(nextHop, pitEntry);
+ });
+ }
+ }
+}
+
+void
Forwarder::setExpiryTimer(const shared_ptr<pit::Entry>& pitEntry, time::milliseconds duration)
{
BOOST_ASSERT(pitEntry);
diff --git a/daemon/fw/forwarder.hpp b/daemon/fw/forwarder.hpp
index 3b14a61..2da7c87 100644
--- a/daemon/fw/forwarder.hpp
+++ b/daemon/fw/forwarder.hpp
@@ -107,6 +107,16 @@
this->onIncomingNack(ingress, nack);
}
+ /** \brief start new nexthop processing
+ * \param prefix the prefix of the FibEntry containing the new nexthop
+ * \param nextHop the new NextHop
+ */
+ void
+ startProcessNewNextHop(const Name& prefix, const fib::NextHop& nextHop)
+ {
+ this->onNewNextHop(prefix, nextHop);
+ }
+
NameTree&
getNameTree()
{
@@ -218,6 +228,9 @@
VIRTUAL_WITH_TESTS void
onDroppedInterest(const FaceEndpoint& egress, const Interest& interest);
+ VIRTUAL_WITH_TESTS void
+ onNewNextHop(const Name& prefix, const fib::NextHop& nextHop);
+
PROTECTED_WITH_TESTS_ELSE_PRIVATE:
/** \brief set a new expiry timer (now + \p duration) on a PIT entry
*/
diff --git a/daemon/fw/strategy.cpp b/daemon/fw/strategy.cpp
index f9f4106..d33ad9a 100644
--- a/daemon/fw/strategy.cpp
+++ b/daemon/fw/strategy.cpp
@@ -207,6 +207,13 @@
}
void
+Strategy::afterNewNextHop(const fib::NextHop& nextHop, const shared_ptr<pit::Entry>& pitEntry)
+{
+ NFD_LOG_DEBUG("afterNewNextHop pitEntry=" << pitEntry->getName()
+ << " nexthop=" << nextHop.getFace().getId());
+}
+
+void
Strategy::sendData(const shared_ptr<pit::Entry>& pitEntry, const Data& data,
const FaceEndpoint& egress)
{
diff --git a/daemon/fw/strategy.hpp b/daemon/fw/strategy.hpp
index 99e4826..37d98bb 100644
--- a/daemon/fw/strategy.hpp
+++ b/daemon/fw/strategy.hpp
@@ -82,7 +82,7 @@
static std::set<Name>
listRegistered();
-public: // constructor, destructor, strategy name
+public: // constructor, destructor, strategy info
/** \brief Construct a strategy instance.
* \param forwarder a reference to the forwarder, used to enable actions and accessors.
* \note Strategy subclass constructor must not retain a reference to \p forwarder.
@@ -114,6 +114,14 @@
return m_name;
}
+ /** \return whether the afterNewNextHop trigger should be invoked for this strategy.
+ */
+ bool
+ wantNewNextHopTrigger() const
+ {
+ return m_wantNewNextHopTrigger;
+ }
+
public: // triggers
/** \brief trigger after Interest is received
*
@@ -233,6 +241,14 @@
virtual void
onDroppedInterest(const FaceEndpoint& egress, const Interest& interest);
+ /** \brief trigger after new nexthop is added
+ *
+ * The strategy should decide whether to send the buffered Interests to the new nexthop.
+ * In the base class, this method does nothing.
+ */
+ virtual void
+ afterNewNextHop(const fib::NextHop& nextHop, const shared_ptr<pit::Entry>& pitEntry);
+
protected: // actions
/** \brief send Interest to egress
* \param pitEntry PIT entry
@@ -368,6 +384,15 @@
m_name = name;
}
+PUBLIC_WITH_TESTS_ELSE_PROTECTED: // setter
+ /** \brief set whether the afterNewNextHop trigger should be invoked for this strategy
+ */
+ void
+ enableNewNextHopTrigger(bool enabled)
+ {
+ m_wantNewNextHopTrigger = enabled;
+ }
+
private: // registry
typedef std::function<unique_ptr<Strategy>(Forwarder& forwarder, const Name& strategyName)> CreateFunc;
typedef std::map<Name, CreateFunc> Registry; // indexed by strategy name
@@ -392,6 +417,8 @@
Forwarder& m_forwarder;
MeasurementsAccessor m_measurements;
+
+ bool m_wantNewNextHopTrigger = false;
};
} // namespace fw
diff --git a/tests/daemon/fw/dummy-strategy.cpp b/tests/daemon/fw/dummy-strategy.cpp
index 4497ede..84038c7 100644
--- a/tests/daemon/fw/dummy-strategy.cpp
+++ b/tests/daemon/fw/dummy-strategy.cpp
@@ -72,6 +72,8 @@
const FaceEndpoint& ingress, const Data& data)
{
++beforeSatisfyInterest_count;
+
+ Strategy::beforeSatisfyInterest(pitEntry, ingress, data);
}
void
@@ -97,6 +99,16 @@
const shared_ptr<pit::Entry>& pitEntry)
{
++afterReceiveNack_count;
+
+ Strategy::afterReceiveNack(ingress, nack, pitEntry);
+}
+
+void
+DummyStrategy::afterNewNextHop(const fib::NextHop& nextHop, const shared_ptr<pit::Entry>& pitEntry)
+{
+ afterNewNextHopCalls.push_back(pitEntry->getName());
+
+ Strategy::afterNewNextHop(nextHop, pitEntry);
}
} // namespace tests
diff --git a/tests/daemon/fw/dummy-strategy.hpp b/tests/daemon/fw/dummy-strategy.hpp
index 9a2445b..04a5d9d 100644
--- a/tests/daemon/fw/dummy-strategy.hpp
+++ b/tests/daemon/fw/dummy-strategy.hpp
@@ -82,6 +82,9 @@
afterReceiveNack(const FaceEndpoint& ingress, const lp::Nack& nack,
const shared_ptr<pit::Entry>& pitEntry) override;
+ void
+ afterNewNextHop(const fib::NextHop& nextHop, const shared_ptr<pit::Entry>& pitEntry) override;
+
protected:
/** \brief register an alias
* \tparam S subclass of DummyStrategy
@@ -102,6 +105,9 @@
int afterReceiveData_count;
int afterReceiveNack_count;
+ // a collection of names of PIT entries that afterNewNextHop() was called on
+ std::vector<Name> afterNewNextHopCalls;
+
shared_ptr<Face> interestOutFace;
};
diff --git a/tests/daemon/fw/strategy-newnexthop.t.cpp b/tests/daemon/fw/strategy-newnexthop.t.cpp
new file mode 100644
index 0000000..282e74f
--- /dev/null
+++ b/tests/daemon/fw/strategy-newnexthop.t.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "fw/forwarder.hpp"
+#include "common/global.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/daemon/global-io-fixture.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+#include "choose-strategy.hpp"
+#include "dummy-strategy.hpp"
+
+namespace nfd {
+namespace tests {
+
+class StrategyNewNextHopFixture : public GlobalIoTimeFixture
+{
+protected:
+ template<typename ...Args>
+ shared_ptr<DummyFace>
+ addFace(Args&&... args)
+ {
+ auto face = make_shared<DummyFace>(std::forward<Args>(args)...);
+ faceTable.add(face);
+ return face;
+ }
+
+protected:
+ FaceTable faceTable;
+ Forwarder forwarder{faceTable};
+};
+
+BOOST_AUTO_TEST_SUITE(Fw)
+BOOST_FIXTURE_TEST_SUITE(TestStrategyNewNextHop, StrategyNewNextHopFixture)
+
+BOOST_AUTO_TEST_CASE(AfterNewNextHop1)
+{
+ auto face1 = addFace();
+ auto face2 = addFace();
+ auto face3 = addFace();
+
+ DummyStrategy& strategy = choose<DummyStrategy>(forwarder, "/A", DummyStrategy::getStrategyName());
+ StrategyChoice& sc = forwarder.getStrategyChoice();
+ sc.insert(Name("/A/B"), strategy.getStrategyName());
+ sc.insert(Name("/A/B/C"), strategy.getStrategyName());
+
+ strategy.enableNewNextHopTrigger(true);
+
+ Fib& fib = forwarder.getFib();
+ Pit& pit = forwarder.getPit();
+
+ // fib: "/", "/A/B"
+ fib::Entry* entry = fib.insert("/").first;
+ fib.addOrUpdateNextHop(*entry, *face1, 0);
+ entry = fib.insert("/A/B").first;
+ fib.addOrUpdateNextHop(*entry, *face1, 0);
+
+ // pit: "/A", "/A/B/C"
+ auto interest1 = makeInterest("/A");
+ shared_ptr<pit::Entry> pit1 = pit.insert(*interest1).first;
+ pit1->insertOrUpdateInRecord(*face3, *interest1);
+ auto interest2 = makeInterest("/A/B/C");
+ shared_ptr<pit::Entry> pit2 = pit.insert(*interest2).first;
+ pit2->insertOrUpdateInRecord(*face3, *interest2);
+ // new nexthop for "/"
+ entry = fib.insert("/").first;
+ fib.addOrUpdateNextHop(*entry, *face2, 0);
+
+ // /A --> triggered, /A/B --> not triggered, /A/B/C --> not triggered
+ BOOST_REQUIRE_EQUAL(strategy.afterNewNextHopCalls.size(), 1);
+ BOOST_CHECK_EQUAL(strategy.afterNewNextHopCalls[0], Name("/A"));
+}
+
+BOOST_AUTO_TEST_CASE(AfterNewNextHop2)
+{
+ auto face1 = addFace();
+ auto face2 = addFace();
+ auto face3 = addFace();
+
+ DummyStrategy& strategy = choose<DummyStrategy>(forwarder, "/A", DummyStrategy::getStrategyName());
+ StrategyChoice& sc = forwarder.getStrategyChoice();
+ sc.insert(Name("/A/B"), strategy.getStrategyName());
+ sc.insert(Name("/A/B/C"), strategy.getStrategyName());
+
+ strategy.enableNewNextHopTrigger(true);
+
+ Fib& fib = forwarder.getFib();
+ Pit& pit = forwarder.getPit();
+
+ // fib: "/", "/A/B"
+ fib::Entry* entry = fib.insert("/").first;
+ fib.addOrUpdateNextHop(*entry, *face1, 0);
+ entry = fib.insert("/A/B").first;
+ fib.addOrUpdateNextHop(*entry, *face1, 0);
+
+ // pit: "/A", "/A/B/C"
+ auto interest1 = makeInterest("/A");
+ shared_ptr<pit::Entry> pit1 = pit.insert(*interest1).first;
+ pit1->insertOrUpdateInRecord(*face3, *interest1);
+ auto interest2 = makeInterest("/A/B");
+ shared_ptr<pit::Entry> pit2 = pit.insert(*interest2).first;
+ pit2->insertOrUpdateInRecord(*face3, *interest2);
+ // new nexthop for "/"
+ entry = fib.insert("/").first;
+ fib.addOrUpdateNextHop(*entry, *face2, 0);
+
+ // /A --> triggered, /A/B --> not triggered, /A/B/C --> not triggered
+ BOOST_REQUIRE_EQUAL(strategy.afterNewNextHopCalls.size(), 1);
+ BOOST_CHECK_EQUAL(strategy.afterNewNextHopCalls[0], Name("/A"));
+}
+
+BOOST_AUTO_TEST_CASE(DisableTrigger)
+{
+ auto face1 = addFace();
+ auto face2 = addFace();
+ auto face3 = addFace();
+
+ DummyStrategy& strategy = choose<DummyStrategy>(forwarder, "/A", DummyStrategy::getStrategyName());
+ strategy.enableNewNextHopTrigger(false);
+
+ Fib& fib = forwarder.getFib();
+ Pit& pit = forwarder.getPit();
+
+ fib::Entry* entry = fib.insert("/").first;
+ fib.addOrUpdateNextHop(*entry, *face1, 0);
+
+ auto interest1 = makeInterest("/A");
+ shared_ptr<pit::Entry> pit1 = pit.insert(*interest1).first;
+ pit1->insertOrUpdateInRecord(*face3, *interest1);
+ // new nexthop for "/A"
+ entry = fib.insert("/A").first;
+ fib.addOrUpdateNextHop(*entry, *face2, 0);
+
+ BOOST_CHECK_EQUAL(strategy.afterNewNextHopCalls.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestStrategyNewNextHop
+BOOST_AUTO_TEST_SUITE_END() // Fw
+
+} // namespace tests
+} // namespace nfd