fw: Nack in pipelines and best-route strategy
* in PIT out-record, add last incoming Nack field
* create incoming Nack pipeline
* create outgoing Nack pipeline
* modify Interest loop pipeline to send Nack upon duplicate Nonce
* in strategy API, add after receive Nack trigger and send Nack action
* in best-route strategy, send Nack-NoRoute before rejecting pending Interest
* in best-route strategy, process incoming Nack
Other changes include:
* Pit::find
* StrategyTester saved arguments structs
* TopologyTester transmit at Transport level
refs #3156
Change-Id: I7868561c0838231083d471261200aeb280cc6e9d
diff --git a/daemon/fw/best-route-strategy2.cpp b/daemon/fw/best-route-strategy2.cpp
index 2ec786c..e0e31dc 100644
--- a/daemon/fw/best-route-strategy2.cpp
+++ b/daemon/fw/best-route-strategy2.cpp
@@ -31,7 +31,7 @@
NFD_LOG_INIT("BestRouteStrategy2");
-const Name BestRouteStrategy2::STRATEGY_NAME("ndn:/localhost/nfd/strategy/best-route/%FD%03");
+const Name BestRouteStrategy2::STRATEGY_NAME("ndn:/localhost/nfd/strategy/best-route/%FD%04");
NFD_REGISTER_STRATEGY(BestRouteStrategy2);
BestRouteStrategy2::BestRouteStrategy2(Forwarder& forwarder, const Name& name)
@@ -104,8 +104,7 @@
const fib::NextHopList& nexthops = fibEntry->getNextHops();
fib::NextHopList::const_iterator it = nexthops.end();
- RetxSuppression::Result suppression =
- m_retxSuppression.decide(inFace, interest, *pitEntry);
+ RetxSuppression::Result suppression = m_retxSuppression.decide(inFace, interest, *pitEntry);
if (suppression == RetxSuppression::NEW) {
// forward to nexthop with lowest cost except downstream
it = std::find_if(nexthops.begin(), nexthops.end(),
@@ -114,6 +113,11 @@
if (it == nexthops.end()) {
NFD_LOG_DEBUG(interest << " from=" << inFace.getId() << " noNextHop");
+
+ lp::NackHeader nackHeader;
+ nackHeader.setReason(lp::NackReason::NO_ROUTE);
+ this->sendNack(pitEntry, inFace, nackHeader);
+
this->rejectPendingInterest(pitEntry);
return;
}
@@ -156,5 +160,72 @@
}
}
+/** \return less severe NackReason between x and y
+ *
+ * lp::NackReason::NONE is treated as most severe
+ */
+inline lp::NackReason
+compareLessSevere(lp::NackReason x, lp::NackReason y)
+{
+ if (x == lp::NackReason::NONE) {
+ return y;
+ }
+ if (y == lp::NackReason::NONE) {
+ return x;
+ }
+ return static_cast<lp::NackReason>(std::min(static_cast<int>(x), static_cast<int>(y)));
+}
+
+void
+BestRouteStrategy2::afterReceiveNack(const Face& inFace, const lp::Nack& nack,
+ shared_ptr<fib::Entry> fibEntry,
+ shared_ptr<pit::Entry> pitEntry)
+{
+ int nOutRecordsNotNacked = 0;
+ Face* lastFaceNotNacked = nullptr;
+ lp::NackReason leastSevereReason = lp::NackReason::NONE;
+ for (const pit::OutRecord& outR : pitEntry->getOutRecords()) {
+ const lp::NackHeader* inNack = outR.getIncomingNack();
+ if (inNack == nullptr) {
+ ++nOutRecordsNotNacked;
+ lastFaceNotNacked = outR.getFace().get();
+ continue;
+ }
+
+ leastSevereReason = compareLessSevere(leastSevereReason, inNack->getReason());
+ }
+
+ lp::NackHeader outNack;
+ outNack.setReason(leastSevereReason);
+
+ if (nOutRecordsNotNacked == 1) {
+ BOOST_ASSERT(lastFaceNotNacked != nullptr);
+ pit::InRecordCollection::const_iterator inR = pitEntry->getInRecord(*lastFaceNotNacked);
+ if (inR != pitEntry->getInRecords().end()) {
+ // one out-record not Nacked, which is also a downstream
+ NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
+ " nack=" << nack.getReason() <<
+ " nack-to(bidirectional)=" << lastFaceNotNacked->getId() <<
+ " out-nack=" << outNack.getReason());
+ this->sendNack(pitEntry, *lastFaceNotNacked, outNack);
+ return;
+ }
+ }
+
+ if (nOutRecordsNotNacked > 0) {
+ NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
+ " nack=" << nack.getReason() <<
+ " waiting=" << nOutRecordsNotNacked);
+ // continue waiting
+ return;
+ }
+
+
+ NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
+ " nack=" << nack.getReason() <<
+ " nack-to=all out-nack=" << outNack.getReason());
+ this->sendNacks(pitEntry, outNack);
+}
+
} // namespace fw
} // namespace nfd