table+fw: move forwarding semantics out of PIT entry
refs #3546
Change-Id: I1e6f87fd81176c116b6d758156da1cf96ea03608
diff --git a/daemon/fw/access-strategy.cpp b/daemon/fw/access-strategy.cpp
index b1ed449..8d53168 100644
--- a/daemon/fw/access-strategy.cpp
+++ b/daemon/fw/access-strategy.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2015, Regents of the University of California,
+ * Copyright (c) 2014-2016, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,6 +24,7 @@
*/
#include "access-strategy.hpp"
+#include "pit-algorithm.hpp"
#include "core/logger.hpp"
namespace nfd {
@@ -131,7 +132,7 @@
return false;
}
- if (pitEntry->violatesScope(*face)) {
+ if (violatesScope(*pitEntry, *face)) {
NFD_LOG_DEBUG(pitEntry->getInterest() << " last-nexthop-violates-scope");
return false;
}
diff --git a/daemon/fw/best-route-strategy.cpp b/daemon/fw/best-route-strategy.cpp
index 45dd4a5..d6d9bb0 100644
--- a/daemon/fw/best-route-strategy.cpp
+++ b/daemon/fw/best-route-strategy.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2015, Regents of the University of California,
+ * Copyright (c) 2014-2016, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,6 +24,7 @@
*/
#include "best-route-strategy.hpp"
+#include "pit-algorithm.hpp"
namespace nfd {
namespace fw {
@@ -40,27 +41,20 @@
{
}
-static inline bool
-predicate_PitEntry_canForwardTo_NextHop(shared_ptr<pit::Entry> pitEntry,
- const fib::NextHop& nexthop)
-{
- return pitEntry->canForwardTo(*nexthop.getFace());
-}
-
void
BestRouteStrategy::afterReceiveInterest(const Face& inFace,
- const Interest& interest,
- shared_ptr<fib::Entry> fibEntry,
- shared_ptr<pit::Entry> pitEntry)
+ const Interest& interest,
+ shared_ptr<fib::Entry> fibEntry,
+ shared_ptr<pit::Entry> pitEntry)
{
- if (pitEntry->hasUnexpiredOutRecords()) {
+ if (hasPendingOutRecords(*pitEntry)) {
// not a new Interest, don't forward
return;
}
const fib::NextHopList& nexthops = fibEntry->getNextHops();
fib::NextHopList::const_iterator it = std::find_if(nexthops.begin(), nexthops.end(),
- bind(&predicate_PitEntry_canForwardTo_NextHop, pitEntry, _1));
+ [&pitEntry] (const fib::NextHop& nexthop) { return canForwardToLegacy(*pitEntry, *nexthop.getFace()); });
if (it == nexthops.end()) {
this->rejectPendingInterest(pitEntry);
diff --git a/daemon/fw/best-route-strategy2.cpp b/daemon/fw/best-route-strategy2.cpp
index 477b0a3..96791be 100644
--- a/daemon/fw/best-route-strategy2.cpp
+++ b/daemon/fw/best-route-strategy2.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2015, Regents of the University of California,
+ * Copyright (c) 2014-2016, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,6 +24,7 @@
*/
#include "best-route-strategy2.hpp"
+#include "pit-algorithm.hpp"
#include "core/logger.hpp"
namespace nfd {
@@ -65,7 +66,7 @@
return false;
// forwarding would violate scope
- if (pitEntry->violatesScope(*upstream))
+ if (violatesScope(*pitEntry, *upstream))
return false;
if (wantUnused) {
diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp
index e8a0ade..ef792ae 100644
--- a/daemon/fw/forwarder.cpp
+++ b/daemon/fw/forwarder.cpp
@@ -24,6 +24,7 @@
*/
#include "forwarder.hpp"
+#include "pit-algorithm.hpp"
#include "core/logger.hpp"
#include "core/random.hpp"
#include "strategy.hpp"
@@ -129,8 +130,8 @@
shared_ptr<pit::Entry> pitEntry = m_pit.insert(interest).first;
// detect duplicate Nonce in PIT entry
- bool hasDuplicateNonceInPit = pitEntry->findNonce(interest.getNonce(), inFace) !=
- pit::DUPLICATE_NONCE_NONE;
+ bool hasDuplicateNonceInPit = fw::findDuplicateNonce(*pitEntry, interest.getNonce(), inFace) !=
+ fw::DUPLICATE_NONCE_NONE;
if (hasDuplicateNonceInPit) {
// goto Interest loop pipeline
this->onInterestLoop(inFace, interest);
@@ -298,7 +299,7 @@
" interest=" << pitEntry->getName());
// scope control
- if (pitEntry->violatesScope(outFace)) {
+ if (fw::violatesScope(*pitEntry, outFace)) {
NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
" interest=" << pitEntry->getName() << " violates scope");
return;
@@ -329,7 +330,7 @@
void
Forwarder::onInterestReject(shared_ptr<pit::Entry> pitEntry)
{
- if (pitEntry->hasUnexpiredOutRecords()) {
+ if (fw::hasPendingOutRecords(*pitEntry)) {
NFD_LOG_ERROR("onInterestReject interest=" << pitEntry->getName() <<
" cannot reject forwarded Interest");
return;
diff --git a/daemon/fw/multicast-strategy.cpp b/daemon/fw/multicast-strategy.cpp
index a2559e4..3c92130 100644
--- a/daemon/fw/multicast-strategy.cpp
+++ b/daemon/fw/multicast-strategy.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2015, Regents of the University of California,
+ * Copyright (c) 2014-2016, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,6 +24,7 @@
*/
#include "multicast-strategy.hpp"
+#include "pit-algorithm.hpp"
namespace nfd {
namespace fw {
@@ -38,20 +39,20 @@
void
MulticastStrategy::afterReceiveInterest(const Face& inFace,
- const Interest& interest,
- shared_ptr<fib::Entry> fibEntry,
- shared_ptr<pit::Entry> pitEntry)
+ const Interest& interest,
+ shared_ptr<fib::Entry> fibEntry,
+ shared_ptr<pit::Entry> pitEntry)
{
const fib::NextHopList& nexthops = fibEntry->getNextHops();
for (fib::NextHopList::const_iterator it = nexthops.begin(); it != nexthops.end(); ++it) {
shared_ptr<Face> outFace = it->getFace();
- if (pitEntry->canForwardTo(*outFace)) {
+ if (canForwardToLegacy(*pitEntry, *outFace)) {
this->sendInterest(pitEntry, outFace);
}
}
- if (!pitEntry->hasUnexpiredOutRecords()) {
+ if (!hasPendingOutRecords(*pitEntry)) {
this->rejectPendingInterest(pitEntry);
}
}
diff --git a/daemon/fw/ncc-strategy.cpp b/daemon/fw/ncc-strategy.cpp
index 9ecb2cd..5f4c7fa 100644
--- a/daemon/fw/ncc-strategy.cpp
+++ b/daemon/fw/ncc-strategy.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2015, Regents of the University of California,
+ * Copyright (c) 2014-2016, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,6 +24,7 @@
*/
#include "ncc-strategy.hpp"
+#include "pit-algorithm.hpp"
#include "core/random.hpp"
#include <boost/random/uniform_int_distribution.hpp>
@@ -60,7 +61,7 @@
shared_ptr<PitEntryInfo> pitEntryInfo =
pitEntry->getOrCreateStrategyInfo<PitEntryInfo>();
- bool isNewPitEntry = !pitEntry->hasUnexpiredOutRecords();
+ bool isNewPitEntry = !hasPendingOutRecords(*pitEntry);
if (!isNewPitEntry) {
return;
}
@@ -74,7 +75,7 @@
shared_ptr<Face> bestFace = measurementsEntryInfo->getBestFace();
if (static_cast<bool>(bestFace) && fibEntry->hasNextHop(bestFace) &&
- pitEntry->canForwardTo(*bestFace)) {
+ canForwardToLegacy(*pitEntry, *bestFace)) {
// TODO Should we use `randlow = 100 + nrand48(h->seed) % 4096U;` ?
deferFirst = measurementsEntryInfo->prediction;
deferRange = time::microseconds((deferFirst.count() + 1) / 2);
@@ -88,7 +89,7 @@
// use first eligible nexthop
auto firstEligibleNexthop = std::find_if(nexthops.begin(), nexthops.end(),
[&pitEntry] (const fib::NextHop& nexthop) {
- return pitEntry->canForwardTo(*nexthop.getFace());
+ return canForwardToLegacy(*pitEntry, *nexthop.getFace());
});
if (firstEligibleNexthop != nexthops.end()) {
this->sendInterest(pitEntry, firstEligibleNexthop->getFace());
@@ -97,7 +98,7 @@
shared_ptr<Face> previousFace = measurementsEntryInfo->previousFace.lock();
if (static_cast<bool>(previousFace) && fibEntry->hasNextHop(previousFace) &&
- pitEntry->canForwardTo(*previousFace)) {
+ canForwardToLegacy(*pitEntry, *previousFace)) {
--nUpstreams;
}
@@ -138,7 +139,7 @@
shared_ptr<Face> previousFace = measurementsEntryInfo->previousFace.lock();
if (static_cast<bool>(previousFace) && fibEntry->hasNextHop(previousFace) &&
- pitEntry->canForwardTo(*previousFace)) {
+ canForwardToLegacy(*pitEntry, *previousFace)) {
this->sendInterest(pitEntry, previousFace);
}
@@ -146,7 +147,7 @@
bool isForwarded = false;
for (fib::NextHopList::const_iterator it = nexthops.begin(); it != nexthops.end(); ++it) {
shared_ptr<Face> face = it->getFace();
- if (pitEntry->canForwardTo(*face)) {
+ if (canForwardToLegacy(*pitEntry, *face)) {
isForwarded = true;
this->sendInterest(pitEntry, face);
break;
diff --git a/daemon/fw/pit-algorithm.cpp b/daemon/fw/pit-algorithm.cpp
new file mode 100644
index 0000000..ac04e2a
--- /dev/null
+++ b/daemon/fw/pit-algorithm.cpp
@@ -0,0 +1,122 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016, 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 "pit-algorithm.hpp"
+
+namespace nfd {
+namespace fw {
+
+namespace scope_prefix {
+const Name LOCALHOST("ndn:/localhost");
+const Name LOCALHOP("ndn:/localhop");
+} // namespace scope_prefix
+
+bool
+violatesScope(const pit::Entry& pitEntry, const Face& outFace)
+{
+ if (outFace.getScope() == ndn::nfd::FACE_SCOPE_LOCAL) {
+ return false;
+ }
+ BOOST_ASSERT(outFace.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL);
+
+ if (scope_prefix::LOCALHOST.isPrefixOf(pitEntry.getName())) {
+ // face is non-local, violates localhost scope
+ return true;
+ }
+
+ if (scope_prefix::LOCALHOP.isPrefixOf(pitEntry.getName())) {
+ // face is non-local, violates localhop scope unless PIT entry has local in-record
+ return std::none_of(pitEntry.getInRecords().begin(), pitEntry.getInRecords().end(),
+ [] (const pit::InRecord& inRecord) { return inRecord.getFace()->getScope() == ndn::nfd::FACE_SCOPE_LOCAL; });
+ }
+
+ // Name is not subject to scope control
+ return false;
+}
+
+bool
+canForwardToLegacy(const pit::Entry& pitEntry, const Face& face)
+{
+ time::steady_clock::TimePoint now = time::steady_clock::now();
+
+ bool hasUnexpiredOutRecord = std::any_of(pitEntry.getOutRecords().begin(), pitEntry.getOutRecords().end(),
+ [&face, &now] (const pit::OutRecord& outRecord) {
+ return outRecord.getFace().get() == &face && outRecord.getExpiry() >= now;
+ });
+ if (hasUnexpiredOutRecord) {
+ return false;
+ }
+
+ bool hasUnexpiredOtherInRecord = std::any_of(pitEntry.getInRecords().begin(), pitEntry.getInRecords().end(),
+ [&face, &now] (const pit::InRecord& inRecord) {
+ return inRecord.getFace().get() != &face && inRecord.getExpiry() >= now;
+ });
+ if (!hasUnexpiredOtherInRecord) {
+ return false;
+ }
+
+ return !violatesScope(pitEntry, face);
+}
+
+int
+findDuplicateNonce(const pit::Entry& pitEntry, uint32_t nonce, const Face& face)
+{
+ int dnw = DUPLICATE_NONCE_NONE;
+
+ for (const pit::InRecord& inRecord : pitEntry.getInRecords()) {
+ if (inRecord.getLastNonce() == nonce) {
+ if (inRecord.getFace().get() == &face) {
+ dnw |= DUPLICATE_NONCE_IN_SAME;
+ }
+ else {
+ dnw |= DUPLICATE_NONCE_IN_OTHER;
+ }
+ }
+ }
+
+ for (const pit::OutRecord& outRecord : pitEntry.getOutRecords()) {
+ if (outRecord.getLastNonce() == nonce) {
+ if (outRecord.getFace().get() == &face) {
+ dnw |= DUPLICATE_NONCE_OUT_SAME;
+ }
+ else {
+ dnw |= DUPLICATE_NONCE_OUT_OTHER;
+ }
+ }
+ }
+
+ return dnw;
+}
+
+bool
+hasPendingOutRecords(const pit::Entry& pitEntry)
+{
+ time::steady_clock::TimePoint now = time::steady_clock::now();
+ return std::any_of(pitEntry.getOutRecords().begin(), pitEntry.getOutRecords().end(),
+ [&now] (const pit::OutRecord& outRecord) { return outRecord.getExpiry() >= now; });
+}
+
+} // namespace fw
+} // namespace nfd
diff --git a/daemon/fw/pit-algorithm.hpp b/daemon/fw/pit-algorithm.hpp
new file mode 100644
index 0000000..c5732b8
--- /dev/null
+++ b/daemon/fw/pit-algorithm.hpp
@@ -0,0 +1,117 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016, 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/>.
+ */
+
+#ifndef NFD_DAEMON_FW_PIT_ALGORITHM_HPP
+#define NFD_DAEMON_FW_PIT_ALGORITHM_HPP
+
+#include "table/pit-entry.hpp"
+
+/** \file
+ * This file contains algorithms that operate on a PIT entry.
+ */
+
+namespace nfd {
+namespace fw {
+
+/** \brief contain Name prefix that affects namespace-based scope control
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/ScopeControl
+ */
+namespace scope_prefix {
+
+/** \brief ndn:/localhost
+ *
+ * The localhost scope limits propagation to the applications on the originating host.
+ *
+ * Interest and Data packets under prefix ndn:/localhost are restricted by these rules:
+ * \li Interest can come from and go to local faces only.
+ * \li Data can come from and go to local faces only.
+ */
+extern const Name LOCALHOST;
+
+/** \brief ndn:/localhop
+ *
+ * The localhop scope limits propagation to no further than the next node.
+ *
+ * Interest packets under prefix ndn:/localhop are restricted by these rules:
+ * \li Interest can come from a local face or a non-local face.
+ * \li If PIT entry has at least one in-record from a local face,
+ * it can be forwarded to local faces and non-local faces.
+ * \li If PIT entry has all in-records from non-local faces,
+ * it can only be forwarded to local faces.
+ * \li PIT entry can be satisfied by Data from any source.
+ *
+ * Data packets under prefix ndn:/localhop are unrestricted.
+ */
+extern const Name LOCALHOP;
+
+} // namespace scope_prefix
+
+/** \brief determine whether forwarding the Interest in \p pitEntry to \p outFace would violate scope
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/ScopeControl
+ */
+bool
+violatesScope(const pit::Entry& pitEntry, const Face& outFace);
+
+/** \brief decide whether Interest can be forwarded to face
+ *
+ * \return true if out-record of this face does not exist or has expired,
+ * and there is an in-record not of this face,
+ * and scope is not violated
+ *
+ * \note This algorithm has a weakness that it does not permit consumer retransmissions
+ * before out-record expires. Therefore, it's not recommended to use this function
+ * in new strategies.
+ * \todo find a better name for this function
+ */
+bool
+canForwardToLegacy(const pit::Entry& pitEntry, const Face& face);
+
+/** \brief indicates where duplicate Nonces are found
+ */
+enum DuplicateNonceWhere {
+ DUPLICATE_NONCE_NONE = 0, ///< no duplicate Nonce is found
+ DUPLICATE_NONCE_IN_SAME = (1 << 0), ///< in-record of same face
+ DUPLICATE_NONCE_IN_OTHER = (1 << 1), ///< in-record of other face
+ DUPLICATE_NONCE_OUT_SAME = (1 << 2), ///< out-record of same face
+ DUPLICATE_NONCE_OUT_OTHER = (1 << 3) ///< out-record of other face
+};
+
+/** \brief determine whether \p pitEntry has duplicate Nonce \p nonce
+ * \return OR'ed DuplicateNonceWhere
+ */
+int
+findDuplicateNonce(const pit::Entry& pitEntry, uint32_t nonce, const Face& face);
+
+/** \brief determine whether \p pitEntry has any pending out-records
+ * \return true if there is one or more unexpired OutRecords
+ * \todo #3545 take Nack into consideration
+ */
+bool
+hasPendingOutRecords(const pit::Entry& pitEntry);
+
+} // namespace fw
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_PIT_ALGORITHM_HPP
diff --git a/daemon/fw/retx-suppression-exponential.cpp b/daemon/fw/retx-suppression-exponential.cpp
index a462f8a..3cca0ab 100644
--- a/daemon/fw/retx-suppression-exponential.cpp
+++ b/daemon/fw/retx-suppression-exponential.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2015, Regents of the University of California,
+ * Copyright (c) 2014-2016, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,6 +24,7 @@
*/
#include "retx-suppression-exponential.hpp"
+#include "pit-algorithm.hpp"
namespace nfd {
namespace fw {
@@ -72,7 +73,7 @@
RetxSuppressionExponential::decide(const Face& inFace, const Interest& interest,
pit::Entry& pitEntry) const
{
- bool isNewPitEntry = !pitEntry.hasUnexpiredOutRecords();
+ bool isNewPitEntry = !hasPendingOutRecords(pitEntry);
if (isNewPitEntry) {
return NEW;
}
diff --git a/daemon/fw/retx-suppression-fixed.cpp b/daemon/fw/retx-suppression-fixed.cpp
index 053c24f..b8b2eb8 100644
--- a/daemon/fw/retx-suppression-fixed.cpp
+++ b/daemon/fw/retx-suppression-fixed.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2015, Regents of the University of California,
+ * Copyright (c) 2014-2016, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,6 +24,7 @@
*/
#include "retx-suppression-fixed.hpp"
+#include "pit-algorithm.hpp"
namespace nfd {
namespace fw {
@@ -40,7 +41,7 @@
RetxSuppressionFixed::decide(const Face& inFace, const Interest& interest,
pit::Entry& pitEntry) const
{
- bool isNewPitEntry = !pitEntry.hasUnexpiredOutRecords();
+ bool isNewPitEntry = !hasPendingOutRecords(pitEntry);
if (isNewPitEntry) {
return NEW;
}
diff --git a/daemon/table/pit-entry.cpp b/daemon/table/pit-entry.cpp
index 22df4a4..21e4c53 100644
--- a/daemon/table/pit-entry.cpp
+++ b/daemon/table/pit-entry.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2015, Regents of the University of California,
+ * Copyright (c) 2014-2016, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -29,9 +29,6 @@
namespace nfd {
namespace pit {
-const Name Entry::LOCALHOST_NAME("ndn:/localhost");
-const Name Entry::LOCALHOP_NAME("ndn:/localhop");
-
Entry::Entry(const Interest& interest)
: m_interest(interest.shared_from_this())
{
@@ -43,90 +40,6 @@
return m_interest->getName();
}
-bool
-Entry::hasLocalInRecord() const
-{
- return std::any_of(m_inRecords.begin(), m_inRecords.end(),
- [] (const InRecord& inRecord) { return inRecord.getFace()->getScope() == ndn::nfd::FACE_SCOPE_LOCAL; });
-}
-
-bool
-Entry::canForwardTo(const Face& face) const
-{
- time::steady_clock::TimePoint now = time::steady_clock::now();
-
- bool hasUnexpiredOutRecord = std::any_of(m_outRecords.begin(), m_outRecords.end(),
- [&face, &now] (const OutRecord& outRecord) {
- return outRecord.getFace().get() == &face && outRecord.getExpiry() >= now;
- });
- if (hasUnexpiredOutRecord) {
- return false;
- }
-
- bool hasUnexpiredOtherInRecord = std::any_of(m_inRecords.begin(), m_inRecords.end(),
- [&face, &now] (const InRecord& inRecord) {
- return inRecord.getFace().get() != &face && inRecord.getExpiry() >= now;
- });
- if (!hasUnexpiredOtherInRecord) {
- return false;
- }
-
- return !this->violatesScope(face);
-}
-
-bool
-Entry::violatesScope(const Face& face) const
-{
- // /localhost scope
- bool isViolatingLocalhost = face.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
- LOCALHOST_NAME.isPrefixOf(this->getName());
- if (isViolatingLocalhost) {
- return true;
- }
-
- // /localhop scope
- bool isViolatingLocalhop = face.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
- LOCALHOP_NAME.isPrefixOf(this->getName()) &&
- !this->hasLocalInRecord();
- if (isViolatingLocalhop) {
- return true;
- }
-
- return false;
-}
-
-int
-Entry::findNonce(uint32_t nonce, const Face& face) const
-{
- // TODO should we ignore expired in/out records?
-
- int dnw = DUPLICATE_NONCE_NONE;
-
- for (const InRecord& inRecord : m_inRecords) {
- if (inRecord.getLastNonce() == nonce) {
- if (inRecord.getFace().get() == &face) {
- dnw |= DUPLICATE_NONCE_IN_SAME;
- }
- else {
- dnw |= DUPLICATE_NONCE_IN_OTHER;
- }
- }
- }
-
- for (const OutRecord& outRecord : m_outRecords) {
- if (outRecord.getLastNonce() == nonce) {
- if (outRecord.getFace().get() == &face) {
- dnw |= DUPLICATE_NONCE_OUT_SAME;
- }
- else {
- dnw |= DUPLICATE_NONCE_OUT_OTHER;
- }
- }
- }
-
- return dnw;
-}
-
InRecordCollection::iterator
Entry::insertOrUpdateInRecord(shared_ptr<Face> face, const Interest& interest)
{
@@ -195,14 +108,5 @@
}
}
-bool
-Entry::hasUnexpiredOutRecords() const
-{
- time::steady_clock::TimePoint now = time::steady_clock::now();
-
- return std::any_of(m_outRecords.begin(), m_outRecords.end(),
- [&now] (const OutRecord& outRecord) { return outRecord.getExpiry() >= now; });
-}
-
} // namespace pit
} // namespace nfd
diff --git a/daemon/table/pit-entry.hpp b/daemon/table/pit-entry.hpp
index 3f15598..012b5aa 100644
--- a/daemon/table/pit-entry.hpp
+++ b/daemon/table/pit-entry.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2015, Regents of the University of California,
+ * Copyright (c) 2014-2016, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -36,7 +36,7 @@
namespace name_tree {
class Entry;
-}
+} // namespace name_tree
namespace pit {
@@ -48,20 +48,6 @@
*/
typedef std::list<OutRecord> OutRecordCollection;
-/** \brief indicates where duplicate Nonces are found
- */
-enum DuplicateNonceWhere {
- DUPLICATE_NONCE_NONE = 0,
- /// in-record of same face
- DUPLICATE_NONCE_IN_SAME = (1 << 0),
- /// in-record of other face
- DUPLICATE_NONCE_IN_OTHER = (1 << 1),
- /// out-record of same face
- DUPLICATE_NONCE_OUT_SAME = (1 << 2),
- /// out-record of other face
- DUPLICATE_NONCE_OUT_OTHER = (1 << 3)
-};
-
/** \brief represents a PIT entry
*/
class Entry : public StrategyInfoHost, noncopyable
@@ -78,45 +64,10 @@
const Name&
getName() const;
- /** \brief decides whether Interest can be forwarded to face
- *
- * \return true if OutRecord of this face does not exist or has expired,
- * and there is an InRecord not of this face,
- * and scope is not violated
- */
- bool
- canForwardTo(const Face& face) const;
-
- /** \brief decides whether forwarding Interest to face would violate scope
- *
- * \return true if scope control would be violated
- * \note canForwardTo has more comprehensive checks (including scope control)
- * and should be used by most strategies. Outgoing Interest pipeline
- * should only check scope because some strategy (eg. vehicular) needs
- * to retransmit sooner than OutRecord expiry, or forward Interest
- * back to incoming face
- */
- bool
- violatesScope(const Face& face) const;
-
- /** \brief finds where a duplicate Nonce appears
- * \return OR'ed DuplicateNonceWhere
- */
- int
- findNonce(uint32_t nonce, const Face& face) const;
-
public: // InRecord
const InRecordCollection&
getInRecords() const;
- /** \brief determines whether any InRecord is a local Face
- *
- * \return true if any InRecord is a local Face,
- * false if all InRecords are non-local Faces
- */
- bool
- hasLocalInRecord() const;
-
/** \brief inserts a InRecord for face, and updates it with interest
*
* If InRecord for face exists, the existing one is updated.
@@ -162,11 +113,6 @@
void
deleteOutRecord(const Face& face);
- /** \return true if there is one or more unexpired OutRecords
- */
- bool
- hasUnexpiredOutRecords() const;
-
public:
scheduler::EventId m_unsatisfyTimer;
scheduler::EventId m_stragglerTimer;
diff --git a/tests/daemon/fw/pit-algorithm.t.cpp b/tests/daemon/fw/pit-algorithm.t.cpp
new file mode 100644
index 0000000..239823c
--- /dev/null
+++ b/tests/daemon/fw/pit-algorithm.t.cpp
@@ -0,0 +1,203 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016, 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/pit-algorithm.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+namespace nfd {
+namespace fw {
+namespace tests {
+
+using namespace nfd::tests;
+
+BOOST_AUTO_TEST_SUITE(Fw)
+BOOST_FIXTURE_TEST_SUITE(TestPitAlgorithm, BaseFixture)
+
+class ScopeControlFixture : public BaseFixture
+{
+protected:
+ ScopeControlFixture()
+ : nonLocalFace1(make_shared<DummyFace>("dummy://1", "dummy://1", ndn::nfd::FACE_SCOPE_NON_LOCAL))
+ , nonLocalFace2(make_shared<DummyFace>("dummy://2", "dummy://2", ndn::nfd::FACE_SCOPE_NON_LOCAL))
+ , localFace3(make_shared<DummyFace>("dummy://3", "dummy://3", ndn::nfd::FACE_SCOPE_LOCAL))
+ , localFace4(make_shared<DummyFace>("dummy://4", "dummy://4", ndn::nfd::FACE_SCOPE_LOCAL))
+ {
+ }
+
+protected:
+ shared_ptr<Face> nonLocalFace1;
+ shared_ptr<Face> nonLocalFace2;
+ shared_ptr<Face> localFace3;
+ shared_ptr<Face> localFace4;
+};
+
+BOOST_FIXTURE_TEST_SUITE(ViolatesScope, ScopeControlFixture)
+
+BOOST_AUTO_TEST_CASE(Unrestricted)
+{
+ shared_ptr<Interest> interest = makeInterest("ndn:/ieWRzDsCu");
+ pit::Entry entry(*interest);
+
+ entry.insertOrUpdateInRecord(nonLocalFace1, *interest);
+ BOOST_CHECK_EQUAL(violatesScope(entry, *nonLocalFace2), false);
+ BOOST_CHECK_EQUAL(violatesScope(entry, *localFace4), false);
+}
+
+BOOST_AUTO_TEST_CASE(Localhost)
+{
+ shared_ptr<Interest> interest = makeInterest("ndn:/localhost/5n1LzIt3");
+ pit::Entry entry(*interest);
+
+ entry.insertOrUpdateInRecord(localFace3, *interest);
+ BOOST_CHECK_EQUAL(violatesScope(entry, *nonLocalFace2), true);
+ BOOST_CHECK_EQUAL(violatesScope(entry, *localFace4), false);
+}
+
+BOOST_AUTO_TEST_CASE(LocalhopFromLocal)
+{
+ shared_ptr<Interest> interest = makeInterest("ndn:/localhop/YcIKWCRYJ");
+ pit::Entry entry(*interest);
+
+ entry.insertOrUpdateInRecord(localFace3, *interest);
+ BOOST_CHECK_EQUAL(violatesScope(entry, *nonLocalFace2), false);
+ BOOST_CHECK_EQUAL(violatesScope(entry, *localFace4), false);
+}
+
+BOOST_AUTO_TEST_CASE(LocalhopFromNonLocal)
+{
+ shared_ptr<Interest> interest = makeInterest("ndn:/localhop/x5uFr5IpqY");
+ pit::Entry entry(*interest);
+
+ entry.insertOrUpdateInRecord(nonLocalFace1, *interest);
+ BOOST_CHECK_EQUAL(violatesScope(entry, *nonLocalFace2), true);
+ BOOST_CHECK_EQUAL(violatesScope(entry, *localFace4), false);
+}
+
+BOOST_AUTO_TEST_CASE(LocalhopFromLocalAndNonLocal)
+{
+ shared_ptr<Interest> interest = makeInterest("ndn:/localhop/gNn2MJAXt");
+ pit::Entry entry(*interest);
+
+ entry.insertOrUpdateInRecord(nonLocalFace1, *interest);
+ entry.insertOrUpdateInRecord(localFace3, *interest);
+ BOOST_CHECK_EQUAL(violatesScope(entry, *nonLocalFace2), false);
+ BOOST_CHECK_EQUAL(violatesScope(entry, *localFace4), false);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // ViolatesScope
+
+BOOST_AUTO_TEST_CASE(CanForwardToLegacy)
+{
+ shared_ptr<Interest> interest = makeInterest("ndn:/WDsuBLIMG");
+ pit::Entry entry(*interest);
+
+ auto face1 = make_shared<DummyFace>();
+ auto face2 = make_shared<DummyFace>();
+
+ entry.insertOrUpdateInRecord(face1, *interest);
+ BOOST_CHECK_EQUAL(canForwardToLegacy(entry, *face1), false);
+ BOOST_CHECK_EQUAL(canForwardToLegacy(entry, *face2), true);
+
+ entry.insertOrUpdateInRecord(face2, *interest);
+ BOOST_CHECK_EQUAL(canForwardToLegacy(entry, *face1), true);
+ BOOST_CHECK_EQUAL(canForwardToLegacy(entry, *face2), true);
+
+ entry.insertOrUpdateOutRecord(face1, *interest);
+ BOOST_CHECK_EQUAL(canForwardToLegacy(entry, *face1), false);
+ BOOST_CHECK_EQUAL(canForwardToLegacy(entry, *face2), true);
+}
+
+BOOST_AUTO_TEST_CASE(Nonce)
+{
+ auto face1 = make_shared<DummyFace>();
+ auto face2 = make_shared<DummyFace>();
+
+ shared_ptr<Interest> interest = makeInterest("ndn:/qtCQ7I1c");
+ interest->setNonce(25559);
+
+ pit::Entry entry0(*interest);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry0, 25559, *face1), DUPLICATE_NONCE_NONE);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry0, 25559, *face2), DUPLICATE_NONCE_NONE);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry0, 19004, *face1), DUPLICATE_NONCE_NONE);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry0, 19004, *face2), DUPLICATE_NONCE_NONE);
+
+ pit::Entry entry1(*interest);
+ entry1.insertOrUpdateInRecord(face1, *interest);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry1, 25559, *face1), DUPLICATE_NONCE_IN_SAME);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry1, 25559, *face2), DUPLICATE_NONCE_IN_OTHER);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry1, 19004, *face1), DUPLICATE_NONCE_NONE);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry1, 19004, *face2), DUPLICATE_NONCE_NONE);
+
+ pit::Entry entry2(*interest);
+ entry2.insertOrUpdateOutRecord(face1, *interest);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry2, 25559, *face1), DUPLICATE_NONCE_OUT_SAME);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry2, 25559, *face2), DUPLICATE_NONCE_OUT_OTHER);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry2, 19004, *face1), DUPLICATE_NONCE_NONE);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry2, 19004, *face2), DUPLICATE_NONCE_NONE);
+
+ pit::Entry entry3(*interest);
+ entry3.insertOrUpdateInRecord(face1, *interest);
+ entry3.insertOrUpdateOutRecord(face1, *interest);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry3, 25559, *face1),
+ DUPLICATE_NONCE_IN_SAME | DUPLICATE_NONCE_OUT_SAME);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry3, 25559, *face2),
+ DUPLICATE_NONCE_IN_OTHER | DUPLICATE_NONCE_OUT_OTHER);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry3, 19004, *face1), DUPLICATE_NONCE_NONE);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry3, 19004, *face2), DUPLICATE_NONCE_NONE);
+
+ pit::Entry entry4(*interest);
+ entry4.insertOrUpdateInRecord(face1, *interest);
+ entry4.insertOrUpdateInRecord(face2, *interest);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry4, 25559, *face1),
+ DUPLICATE_NONCE_IN_SAME | DUPLICATE_NONCE_IN_OTHER);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry4, 25559, *face2),
+ DUPLICATE_NONCE_IN_SAME | DUPLICATE_NONCE_IN_OTHER);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry4, 19004, *face1), DUPLICATE_NONCE_NONE);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry4, 19004, *face2), DUPLICATE_NONCE_NONE);
+
+ pit::Entry entry5(*interest);
+ entry5.insertOrUpdateOutRecord(face1, *interest);
+ entry5.insertOrUpdateOutRecord(face2, *interest);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry5, 25559, *face1),
+ DUPLICATE_NONCE_OUT_SAME | DUPLICATE_NONCE_OUT_OTHER);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry5, 25559, *face2),
+ DUPLICATE_NONCE_OUT_SAME | DUPLICATE_NONCE_OUT_OTHER);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry5, 19004, *face1), DUPLICATE_NONCE_NONE);
+ BOOST_CHECK_EQUAL(findDuplicateNonce(entry5, 19004, *face2), DUPLICATE_NONCE_NONE);
+}
+
+BOOST_AUTO_TEST_CASE(HasPendingOutRecords)
+{
+ /// \todo #3545
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPitAlgorithm
+BOOST_AUTO_TEST_SUITE_END() // Fw
+
+} // namespace tests
+} // namespace fw
+} // namespace nfd
diff --git a/tests/daemon/table/pit.t.cpp b/tests/daemon/table/pit.t.cpp
index 546c0a5..9ca3ec7 100644
--- a/tests/daemon/table/pit.t.cpp
+++ b/tests/daemon/table/pit.t.cpp
@@ -154,65 +154,6 @@
BOOST_CHECK(entry.getOutRecord(*face2) == entry.getOutRecords().end());
}
-BOOST_AUTO_TEST_CASE(Nonce)
-{
- shared_ptr<Face> face1 = make_shared<DummyFace>();
- shared_ptr<Face> face2 = make_shared<DummyFace>();
-
- shared_ptr<Interest> interest = makeInterest("ndn:/qtCQ7I1c");
- interest->setNonce(25559);
-
- pit::Entry entry0(*interest);
- BOOST_CHECK_EQUAL(entry0.findNonce(25559, *face1), pit::DUPLICATE_NONCE_NONE);
- BOOST_CHECK_EQUAL(entry0.findNonce(25559, *face2), pit::DUPLICATE_NONCE_NONE);
- BOOST_CHECK_EQUAL(entry0.findNonce(19004, *face1), pit::DUPLICATE_NONCE_NONE);
- BOOST_CHECK_EQUAL(entry0.findNonce(19004, *face2), pit::DUPLICATE_NONCE_NONE);
-
- pit::Entry entry1(*interest);
- entry1.insertOrUpdateInRecord(face1, *interest);
- BOOST_CHECK_EQUAL(entry1.findNonce(25559, *face1), pit::DUPLICATE_NONCE_IN_SAME);
- BOOST_CHECK_EQUAL(entry1.findNonce(25559, *face2), pit::DUPLICATE_NONCE_IN_OTHER);
- BOOST_CHECK_EQUAL(entry1.findNonce(19004, *face1), pit::DUPLICATE_NONCE_NONE);
- BOOST_CHECK_EQUAL(entry1.findNonce(19004, *face2), pit::DUPLICATE_NONCE_NONE);
-
- pit::Entry entry2(*interest);
- entry2.insertOrUpdateOutRecord(face1, *interest);
- BOOST_CHECK_EQUAL(entry2.findNonce(25559, *face1), pit::DUPLICATE_NONCE_OUT_SAME);
- BOOST_CHECK_EQUAL(entry2.findNonce(25559, *face2), pit::DUPLICATE_NONCE_OUT_OTHER);
- BOOST_CHECK_EQUAL(entry2.findNonce(19004, *face1), pit::DUPLICATE_NONCE_NONE);
- BOOST_CHECK_EQUAL(entry2.findNonce(19004, *face2), pit::DUPLICATE_NONCE_NONE);
-
- pit::Entry entry3(*interest);
- entry3.insertOrUpdateInRecord(face1, *interest);
- entry3.insertOrUpdateOutRecord(face1, *interest);
- BOOST_CHECK_EQUAL(entry3.findNonce(25559, *face1),
- pit::DUPLICATE_NONCE_IN_SAME | pit::DUPLICATE_NONCE_OUT_SAME);
- BOOST_CHECK_EQUAL(entry3.findNonce(25559, *face2),
- pit::DUPLICATE_NONCE_IN_OTHER | pit::DUPLICATE_NONCE_OUT_OTHER);
- BOOST_CHECK_EQUAL(entry3.findNonce(19004, *face1), pit::DUPLICATE_NONCE_NONE);
- BOOST_CHECK_EQUAL(entry3.findNonce(19004, *face2), pit::DUPLICATE_NONCE_NONE);
-
- pit::Entry entry4(*interest);
- entry4.insertOrUpdateInRecord(face1, *interest);
- entry4.insertOrUpdateInRecord(face2, *interest);
- BOOST_CHECK_EQUAL(entry4.findNonce(25559, *face1),
- pit::DUPLICATE_NONCE_IN_SAME | pit::DUPLICATE_NONCE_IN_OTHER);
- BOOST_CHECK_EQUAL(entry4.findNonce(25559, *face2),
- pit::DUPLICATE_NONCE_IN_SAME | pit::DUPLICATE_NONCE_IN_OTHER);
- BOOST_CHECK_EQUAL(entry4.findNonce(19004, *face1), pit::DUPLICATE_NONCE_NONE);
- BOOST_CHECK_EQUAL(entry4.findNonce(19004, *face2), pit::DUPLICATE_NONCE_NONE);
-
- pit::Entry entry5(*interest);
- entry5.insertOrUpdateOutRecord(face1, *interest);
- entry5.insertOrUpdateOutRecord(face2, *interest);
- BOOST_CHECK_EQUAL(entry5.findNonce(25559, *face1),
- pit::DUPLICATE_NONCE_OUT_SAME | pit::DUPLICATE_NONCE_OUT_OTHER);
- BOOST_CHECK_EQUAL(entry5.findNonce(25559, *face2),
- pit::DUPLICATE_NONCE_OUT_SAME | pit::DUPLICATE_NONCE_OUT_OTHER);
- BOOST_CHECK_EQUAL(entry5.findNonce(19004, *face1), pit::DUPLICATE_NONCE_NONE);
- BOOST_CHECK_EQUAL(entry5.findNonce(19004, *face2), pit::DUPLICATE_NONCE_NONE);
-}
-
BOOST_AUTO_TEST_CASE(Lifetime)
{
shared_ptr<Interest> interest = makeInterest("ndn:/7oIEurbgy6");
@@ -229,27 +170,6 @@
BOOST_CHECK_GT(outIt->getExpiry(), time::steady_clock::now());
}
-BOOST_AUTO_TEST_CASE(CanForwardTo)
-{
- shared_ptr<Interest> interest = makeInterest("ndn:/WDsuBLIMG");
- pit::Entry entry(*interest);
-
- shared_ptr<Face> face1 = make_shared<DummyFace>();
- shared_ptr<Face> face2 = make_shared<DummyFace>();
-
- entry.insertOrUpdateInRecord(face1, *interest);
- BOOST_CHECK_EQUAL(entry.canForwardTo(*face1), false);
- BOOST_CHECK_EQUAL(entry.canForwardTo(*face2), true);
-
- entry.insertOrUpdateInRecord(face2, *interest);
- BOOST_CHECK_EQUAL(entry.canForwardTo(*face1), true);
- BOOST_CHECK_EQUAL(entry.canForwardTo(*face2), true);
-
- entry.insertOrUpdateOutRecord(face1, *interest);
- BOOST_CHECK_EQUAL(entry.canForwardTo(*face1), false);
- BOOST_CHECK_EQUAL(entry.canForwardTo(*face2), true);
-}
-
BOOST_AUTO_TEST_CASE(OutRecordNack)
{
shared_ptr<Face> face1 = make_shared<DummyFace>();