fw: remove EndpointId from egress APIs

This commit partially reverts c70794810592a90847656a97caf27f0326668240

refs: #4849, #4973
Change-Id: I1063e5de2c0e3c90971c4ae006ce46de386c2ed0
diff --git a/daemon/fw/access-strategy.cpp b/daemon/fw/access-strategy.cpp
index 51f7c8d..ee2a7a2 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-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -144,14 +144,13 @@
   NFD_LOG_DEBUG(pitEntry->getInterest() << " interestTo " << mi.lastNexthop
                 << " last-nexthop rto=" << time::duration_cast<time::microseconds>(rto).count());
 
-  this->sendInterest(pitEntry, FaceEndpoint(*outFace, 0), interest);
+  this->sendInterest(pitEntry, *outFace, interest);
 
   // schedule RTO timeout
   PitInfo* pi = pitEntry->insertStrategyInfo<PitInfo>().first;
   pi->rtoTimer = getScheduler().schedule(rto,
-    [this, pitWeak = weak_ptr<pit::Entry>(pitEntry), face = ingress.face.getId(),
-     endpoint = ingress.endpoint, lastNexthop = mi.lastNexthop] {
-      afterRtoTimeout(pitWeak, face, endpoint, lastNexthop);
+    [this, pitWeak = weak_ptr<pit::Entry>(pitEntry), face = ingress.face.getId(), nh = mi.lastNexthop] {
+      afterRtoTimeout(pitWeak, face, nh);
     });
 
   return true;
@@ -159,7 +158,7 @@
 
 void
 AccessStrategy::afterRtoTimeout(const weak_ptr<pit::Entry>& pitWeak,
-                                FaceId inFaceId, EndpointId inEndpointId, FaceId firstOutFaceId)
+                                FaceId inFaceId, FaceId firstOutFaceId)
 {
   shared_ptr<pit::Entry> pitEntry = pitWeak.lock();
   // if PIT entry is gone, RTO timer should have been cancelled
@@ -199,7 +198,7 @@
       continue;
     }
     NFD_LOG_DEBUG(pitEntry->getInterest() << " interestTo " << outFace.getId() << " multicast");
-    this->sendInterest(pitEntry, FaceEndpoint(outFace, 0), interest);
+    this->sendInterest(pitEntry, outFace, interest);
     ++nSent;
   }
   return nSent;
diff --git a/daemon/fw/access-strategy.hpp b/daemon/fw/access-strategy.hpp
index 0ce8917..a766239 100644
--- a/daemon/fw/access-strategy.hpp
+++ b/daemon/fw/access-strategy.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -44,8 +44,6 @@
  *     the granularity of this knowledge is the parent of Data Name.
  *  3. Forward subsequent Interests to the last working nexthop.
  *     If it doesn't respond, multicast again.
- *
- *  \note This strategy is not EndpointId-aware.
  */
 class AccessStrategy : public Strategy
 {
@@ -152,7 +150,7 @@
 
   void
   afterRtoTimeout(const weak_ptr<pit::Entry>& pitWeak,
-                  FaceId inFaceId, EndpointId inEndpointId, FaceId firstOutFaceId);
+                  FaceId inFaceId, FaceId firstOutFaceId);
 
   /** \brief multicast to all nexthops
    *  \param exceptFace don't forward to this face; also, \p inFace is always excluded
diff --git a/daemon/fw/asf-strategy.cpp b/daemon/fw/asf-strategy.cpp
index b9a253d..f967c86 100644
--- a/daemon/fw/asf-strategy.cpp
+++ b/daemon/fw/asf-strategy.cpp
@@ -123,7 +123,7 @@
   if (suppressResult == RetxSuppressionResult::NEW) {
     if (nexthops.size() == 0) {
       NFD_LOG_DEBUG(interest << " new-interest from=" << ingress << " no-nexthop");
-      sendNoRouteNack(ingress, pitEntry);
+      sendNoRouteNack(ingress.face, pitEntry);
       return;
     }
 
@@ -137,7 +137,7 @@
     }
     else {
       NFD_LOG_DEBUG(interest << " new-interest from=" << ingress << " no-nexthop");
-      sendNoRouteNack(ingress, pitEntry);
+      sendNoRouteNack(ingress.face, pitEntry);
     }
     return;
   }
@@ -158,9 +158,8 @@
     NFD_LOG_DEBUG(interest << " retx-interest from=" << ingress << " no-nexthop");
   }
   else {
-    auto egress = FaceEndpoint(it->getFace(), 0);
-    NFD_LOG_DEBUG(interest << " retx-interest from=" << ingress << " retry-to=" << egress);
-    this->sendInterest(pitEntry, egress, interest);
+    NFD_LOG_DEBUG(interest << " retx-interest from=" << ingress << " retry-to=" << it->getFace().getId());
+    this->sendInterest(pitEntry, it->getFace(), interest);
   }
 }
 
@@ -210,30 +209,31 @@
 AsfStrategy::forwardInterest(const Interest& interest, Face& outFace, const fib::Entry& fibEntry,
                              const shared_ptr<pit::Entry>& pitEntry, bool wantNewNonce)
 {
-  auto egress = FaceEndpoint(outFace, 0);
+  auto faceId = outFace.getId();
+
   if (wantNewNonce) {
     // Send probe: interest with new Nonce
     Interest probeInterest(interest);
     probeInterest.refreshNonce();
-    NFD_LOG_TRACE("Sending probe for " << probeInterest << " to=" << egress);
-    this->sendInterest(pitEntry, egress, probeInterest);
+    NFD_LOG_TRACE("Sending probe for " << probeInterest << " to=" << faceId);
+    this->sendInterest(pitEntry, outFace, probeInterest);
   }
   else {
-    this->sendInterest(pitEntry, egress, interest);
+    this->sendInterest(pitEntry, outFace, interest);
   }
 
-  FaceInfo& faceInfo = m_measurements.getOrCreateFaceInfo(fibEntry, interest, egress.face.getId());
+  FaceInfo& faceInfo = m_measurements.getOrCreateFaceInfo(fibEntry, interest, faceId);
 
   // Refresh measurements since Face is being used for forwarding
   NamespaceInfo& namespaceInfo = m_measurements.getOrCreateNamespaceInfo(fibEntry, interest);
-  namespaceInfo.extendFaceInfoLifetime(faceInfo, egress.face.getId());
+  namespaceInfo.extendFaceInfoLifetime(faceInfo, faceId);
 
   if (!faceInfo.isTimeoutScheduled()) {
     auto timeout = faceInfo.scheduleTimeout(interest.getName(),
-      [this, name = interest.getName(), faceId = egress.face.getId()] {
+      [this, name = interest.getName(), faceId] {
         onTimeoutOrNack(name, faceId, false);
       });
-    NFD_LOG_TRACE("Scheduled timeout for " << fibEntry.getPrefix() << " to=" << egress
+    NFD_LOG_TRACE("Scheduled timeout for " << fibEntry.getPrefix() << " to=" << faceId
                   << " in " << time::duration_cast<time::milliseconds>(timeout) << " ms");
   }
 }
@@ -350,11 +350,11 @@
 }
 
 void
-AsfStrategy::sendNoRouteNack(const FaceEndpoint& ingress, const shared_ptr<pit::Entry>& pitEntry)
+AsfStrategy::sendNoRouteNack(Face& face, const shared_ptr<pit::Entry>& pitEntry)
 {
   lp::NackHeader nackHeader;
   nackHeader.setReason(lp::NackReason::NO_ROUTE);
-  this->sendNack(pitEntry, ingress, nackHeader);
+  this->sendNack(pitEntry, face, nackHeader);
   this->rejectPendingInterest(pitEntry);
 }
 
diff --git a/daemon/fw/asf-strategy.hpp b/daemon/fw/asf-strategy.hpp
index e0d40c7..4e5622f 100644
--- a/daemon/fw/asf-strategy.hpp
+++ b/daemon/fw/asf-strategy.hpp
@@ -40,8 +40,6 @@
  *  \see Vince Lehman, Ashlesh Gawande, Rodrigo Aldecoa, Dmitri Krioukov, Beichuan Zhang, Lixia Zhang, and Lan Wang,
  *       "An Experimental Investigation of Hyperbolic Routing with a Smart Forwarding Plane in NDN,"
  *       NDN Technical Report NDN-0042, 2016. http://named-data.net/techreports.html
- *
- *  \note This strategy is not EndpointId-aware.
  */
 class AsfStrategy : public Strategy
 {
@@ -86,7 +84,7 @@
   onTimeoutOrNack(const Name& interestName, FaceId faceId, bool isNack);
 
   void
-  sendNoRouteNack(const FaceEndpoint& ingress, const shared_ptr<pit::Entry>& pitEntry);
+  sendNoRouteNack(Face& face, const shared_ptr<pit::Entry>& pitEntry);
 
 private:
   AsfMeasurements m_measurements;
diff --git a/daemon/fw/best-route-strategy.cpp b/daemon/fw/best-route-strategy.cpp
index 658a17a..82a3f56 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-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -48,7 +48,7 @@
     Face& outFace = nexthop.getFace();
     if (!wouldViolateScope(ingress.face, interest, outFace) &&
         canForwardToLegacy(*pitEntry, outFace)) {
-      this->sendInterest(pitEntry, FaceEndpoint(outFace, 0), interest);
+      this->sendInterest(pitEntry, outFace, interest);
       return;
     }
   }
diff --git a/daemon/fw/best-route-strategy.hpp b/daemon/fw/best-route-strategy.hpp
index e3cc60c..2c5be36 100644
--- a/daemon/fw/best-route-strategy.hpp
+++ b/daemon/fw/best-route-strategy.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -52,8 +52,6 @@
  *  \note This strategy is superceded by Best Route strategy version 2,
  *        which allows consumer retransmissions. This version is kept for
  *        comparison purposes and is not recommended for general usage.
- *
- *  \note This strategy is not EndpointId-aware.
  */
 class BestRouteStrategy : public BestRouteStrategyBase
 {
diff --git a/daemon/fw/best-route-strategy2.cpp b/daemon/fw/best-route-strategy2.cpp
index f2acfed..7a560d8 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-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -86,27 +86,28 @@
 
       lp::NackHeader nackHeader;
       nackHeader.setReason(lp::NackReason::NO_ROUTE);
-      this->sendNack(pitEntry, ingress, nackHeader);
+      this->sendNack(pitEntry, ingress.face, nackHeader);
 
       this->rejectPendingInterest(pitEntry);
       return;
     }
 
-    auto egress = FaceEndpoint(it->getFace(), 0);
-    NFD_LOG_DEBUG(interest << " from=" << ingress << " newPitEntry-to=" << egress);
-    this->sendInterest(pitEntry, egress, interest);
+    Face& outFace = it->getFace();
+    NFD_LOG_DEBUG(interest << " from=" << ingress << " newPitEntry-to=" << outFace.getId());
+    this->sendInterest(pitEntry, outFace, interest);
     return;
   }
 
   // find an unused upstream with lowest cost except downstream
-  it = std::find_if(nexthops.begin(), nexthops.end(), [&] (const auto& nexthop) {
-    return isNextHopEligible(ingress.face, interest, nexthop, pitEntry, true, time::steady_clock::now());
-  });
+  it = std::find_if(nexthops.begin(), nexthops.end(),
+                    [&, now = time::steady_clock::now()] (const auto& nexthop) {
+                      return isNextHopEligible(ingress.face, interest, nexthop, pitEntry, true, now);
+                    });
 
   if (it != nexthops.end()) {
-    auto egress = FaceEndpoint(it->getFace(), 0);
-    this->sendInterest(pitEntry, egress, interest);
-    NFD_LOG_DEBUG(interest << " from=" << ingress << " retransmit-unused-to=" << egress);
+    Face& outFace = it->getFace();
+    this->sendInterest(pitEntry, outFace, interest);
+    NFD_LOG_DEBUG(interest << " from=" << ingress << " retransmit-unused-to=" << outFace.getId());
     return;
   }
 
@@ -116,9 +117,9 @@
     NFD_LOG_DEBUG(interest << " from=" << ingress << " retransmitNoNextHop");
   }
   else {
-    auto egress = FaceEndpoint(it->getFace(), 0);
-    this->sendInterest(pitEntry, egress, interest);
-    NFD_LOG_DEBUG(interest << " from=" << ingress << " retransmit-retry-to=" << egress);
+    Face& outFace = it->getFace();
+    this->sendInterest(pitEntry, outFace, interest);
+    NFD_LOG_DEBUG(interest << " from=" << ingress << " retransmit-retry-to=" << outFace.getId());
   }
 }
 
diff --git a/daemon/fw/best-route-strategy2.hpp b/daemon/fw/best-route-strategy2.hpp
index 598469f..bcb994d 100644
--- a/daemon/fw/best-route-strategy2.hpp
+++ b/daemon/fw/best-route-strategy2.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -49,8 +49,6 @@
  *
  *  This strategy returns Nack to all downstreams if all upstreams have returned Nacks.
  *  The reason of the sent Nack equals the least severe reason among received Nacks.
- *
- *  \note This strategy is not EndpointId-aware.
  */
 class BestRouteStrategy2 : public Strategy
                          , public ProcessNackTraits<BestRouteStrategy2>
diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp
index 55a9062..bd4d692 100644
--- a/daemon/fw/forwarder.cpp
+++ b/daemon/fw/forwarder.cpp
@@ -68,7 +68,7 @@
       });
     face.onDroppedInterest.connect(
       [this, &face] (const Interest& interest) {
-        this->onDroppedInterest(FaceEndpoint(face, 0), interest);
+        this->onDroppedInterest(face, interest);
       });
   });
 
@@ -206,7 +206,7 @@
                     << " nexthop-faceid=" << nextHopFace->getId());
       // go to outgoing Interest pipeline
       // scope control is unnecessary, because privileged app explicitly wants to forward
-      this->onOutgoingInterest(pitEntry, FaceEndpoint(*nextHopFace, 0), interest);
+      this->onOutgoingInterest(pitEntry, *nextHopFace, interest);
     }
     return;
   }
@@ -242,23 +242,23 @@
 
 void
 Forwarder::onOutgoingInterest(const shared_ptr<pit::Entry>& pitEntry,
-                              const FaceEndpoint& egress, const Interest& interest)
+                              Face& egress, const Interest& interest)
 {
   // drop if HopLimit == 0 but sending on non-local face
-  if (interest.getHopLimit() == 0 && egress.face.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL) {
-    NFD_LOG_DEBUG("onOutgoingInterest out=" << egress << " interest=" << pitEntry->getName()
+  if (interest.getHopLimit() == 0 && egress.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL) {
+    NFD_LOG_DEBUG("onOutgoingInterest out=" << egress.getId() << " interest=" << pitEntry->getName()
                   << " non-local hop-limit=0");
-    ++const_cast<PacketCounter&>(egress.face.getCounters().nOutHopLimitZero);
+    ++const_cast<PacketCounter&>(egress.getCounters().nOutHopLimitZero);
     return;
   }
 
-  NFD_LOG_DEBUG("onOutgoingInterest out=" << egress << " interest=" << pitEntry->getName());
+  NFD_LOG_DEBUG("onOutgoingInterest out=" << egress.getId() << " interest=" << pitEntry->getName());
 
   // insert out-record
-  pitEntry->insertOrUpdateOutRecord(egress.face, interest);
+  pitEntry->insertOrUpdateOutRecord(egress, interest);
 
   // send Interest
-  egress.face.sendInterest(interest);
+  egress.sendInterest(interest);
   ++m_counters.nOutInterests;
 }
 
@@ -329,7 +329,7 @@
     pitEntry->isSatisfied = true;
     pitEntry->dataFreshnessPeriod = data.getFreshnessPeriod();
 
-    // Dead Nonce List insert if necessary (for out-record of inFace)
+    // Dead Nonce List insert if necessary (for out-record of ingress face)
     this->insertDeadNonceList(*pitEntry, &ingress.face);
 
     // delete PIT entry's out-record
@@ -338,7 +338,7 @@
   // when more than one PIT entry is matched, trigger strategy: before satisfy Interest,
   // and send Data to all matched out faces
   else {
-    std::set<std::pair<Face*, EndpointId>> pendingDownstreams;
+    std::set<Face*> pendingDownstreams;
     auto now = time::steady_clock::now();
 
     for (const auto& pitEntry : pitMatches) {
@@ -347,7 +347,7 @@
       // remember pending downstreams
       for (const pit::InRecord& inRecord : pitEntry->getInRecords()) {
         if (inRecord.getExpiry() > now) {
-          pendingDownstreams.emplace(&inRecord.getFace(), 0);
+          pendingDownstreams.insert(&inRecord.getFace());
         }
       }
 
@@ -362,7 +362,7 @@
       pitEntry->isSatisfied = true;
       pitEntry->dataFreshnessPeriod = data.getFreshnessPeriod();
 
-      // Dead Nonce List insert if necessary (for out-record of inFace)
+      // Dead Nonce List insert if necessary (for out-record of ingress face)
       this->insertDeadNonceList(*pitEntry, &ingress.face);
 
       // clear PIT entry's in and out records
@@ -372,13 +372,12 @@
 
     // foreach pending downstream
     for (const auto& pendingDownstream : pendingDownstreams) {
-      if (pendingDownstream.first->getId() == ingress.face.getId() &&
-          pendingDownstream.second == ingress.endpoint &&
-          pendingDownstream.first->getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) {
+      if (pendingDownstream->getId() == ingress.face.getId() &&
+          pendingDownstream->getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) {
         continue;
       }
       // goto outgoing Data pipeline
-      this->onOutgoingData(data, FaceEndpoint(*pendingDownstream.first, pendingDownstream.second));
+      this->onOutgoingData(data, *pendingDownstream);
     }
   }
 }
@@ -393,24 +392,26 @@
     m_cs.insert(data, true);
   }
 
-  NFD_LOG_DEBUG("onDataUnsolicited in=" << ingress << " data=" << data.getName() << " decision=" << decision);
+  NFD_LOG_DEBUG("onDataUnsolicited in=" << ingress << " data=" << data.getName()
+                << " decision=" << decision);
   ++m_counters.nUnsolicitedData;
 }
 
 void
-Forwarder::onOutgoingData(const Data& data, const FaceEndpoint& egress)
+Forwarder::onOutgoingData(const Data& data, Face& egress)
 {
-  if (egress.face.getId() == face::INVALID_FACEID) {
+  if (egress.getId() == face::INVALID_FACEID) {
     NFD_LOG_WARN("onOutgoingData out=(invalid) data=" << data.getName());
     return;
   }
-  NFD_LOG_DEBUG("onOutgoingData out=" << egress << " data=" << data.getName());
+  NFD_LOG_DEBUG("onOutgoingData out=" << egress.getId() << " data=" << data.getName());
 
   // /localhost scope control
-  bool isViolatingLocalhost = egress.face.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
+  bool isViolatingLocalhost = egress.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
                               scope_prefix::LOCALHOST.isPrefixOf(data.getName());
   if (isViolatingLocalhost) {
-    NFD_LOG_DEBUG("onOutgoingData out=" << egress << " data=" << data.getName() << " violates /localhost");
+    NFD_LOG_DEBUG("onOutgoingData out=" << egress.getId() << " data=" << data.getName()
+                  << " violates /localhost");
     // (drop)
     return;
   }
@@ -418,7 +419,7 @@
   // TODO traffic manager
 
   // send Data
-  egress.face.sendData(data);
+  egress.sendData(data);
   ++m_counters.nOutData;
 }
 
@@ -481,34 +482,34 @@
 
 void
 Forwarder::onOutgoingNack(const shared_ptr<pit::Entry>& pitEntry,
-                          const FaceEndpoint& egress, const lp::NackHeader& nack)
+                          Face& egress, const lp::NackHeader& nack)
 {
-  if (egress.face.getId() == face::INVALID_FACEID) {
+  if (egress.getId() == face::INVALID_FACEID) {
     NFD_LOG_WARN("onOutgoingNack out=(invalid)"
                  << " nack=" << pitEntry->getInterest().getName() << "~" << nack.getReason());
     return;
   }
 
   // has in-record?
-  auto inRecord = pitEntry->getInRecord(egress.face);
+  auto inRecord = pitEntry->getInRecord(egress);
 
   // if no in-record found, drop
   if (inRecord == pitEntry->in_end()) {
-    NFD_LOG_DEBUG("onOutgoingNack out=" << egress
+    NFD_LOG_DEBUG("onOutgoingNack out=" << egress.getId()
                   << " nack=" << pitEntry->getInterest().getName()
                   << "~" << nack.getReason() << " no-in-record");
     return;
   }
 
   // if multi-access or ad hoc face, drop
-  if (egress.face.getLinkType() != ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
-    NFD_LOG_DEBUG("onOutgoingNack out=" << egress
+  if (egress.getLinkType() != ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
+    NFD_LOG_DEBUG("onOutgoingNack out=" << egress.getId()
                   << " nack=" << pitEntry->getInterest().getName() << "~" << nack.getReason()
-                  << " link-type=" << egress.face.getLinkType());
+                  << " link-type=" << egress.getLinkType());
     return;
   }
 
-  NFD_LOG_DEBUG("onOutgoingNack out=" << egress
+  NFD_LOG_DEBUG("onOutgoingNack out=" << egress.getId()
                 << " nack=" << pitEntry->getInterest().getName()
                 << "~" << nack.getReason() << " OK");
 
@@ -517,15 +518,15 @@
   nackPkt.setHeader(nack);
 
   // erase in-record
-  pitEntry->deleteInRecord(egress.face);
+  pitEntry->deleteInRecord(egress);
 
   // send Nack on face
-  egress.face.sendNack(nackPkt);
+  egress.sendNack(nackPkt);
   ++m_counters.nOutNacks;
 }
 
 void
-Forwarder::onDroppedInterest(const FaceEndpoint& egress, const Interest& interest)
+Forwarder::onDroppedInterest(const Face& egress, const Interest& interest)
 {
   m_strategyChoice.findEffectiveStrategy(interest.getName()).onDroppedInterest(egress, interest);
 }
diff --git a/daemon/fw/forwarder.hpp b/daemon/fw/forwarder.hpp
index 2da7c87..a3d4771 100644
--- a/daemon/fw/forwarder.hpp
+++ b/daemon/fw/forwarder.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -192,7 +192,7 @@
    */
   VIRTUAL_WITH_TESTS void
   onOutgoingInterest(const shared_ptr<pit::Entry>& pitEntry,
-                     const FaceEndpoint& egress, const Interest& interest);
+                     Face& egress, const Interest& interest);
 
   /** \brief Interest finalize pipeline
    */
@@ -212,7 +212,7 @@
   /** \brief outgoing Data pipeline
    */
   VIRTUAL_WITH_TESTS void
-  onOutgoingData(const Data& data, const FaceEndpoint& egress);
+  onOutgoingData(const Data& data, Face& egress);
 
   /** \brief incoming Nack pipeline
    */
@@ -223,10 +223,10 @@
    */
   VIRTUAL_WITH_TESTS void
   onOutgoingNack(const shared_ptr<pit::Entry>& pitEntry,
-                 const FaceEndpoint& egress, const lp::NackHeader& nack);
+                 Face& egress, const lp::NackHeader& nack);
 
   VIRTUAL_WITH_TESTS void
-  onDroppedInterest(const FaceEndpoint& egress, const Interest& interest);
+  onDroppedInterest(const Face& egress, const Interest& interest);
 
   VIRTUAL_WITH_TESTS void
   onNewNextHop(const Name& prefix, const fib::NextHop& nextHop);
diff --git a/daemon/fw/multicast-strategy.cpp b/daemon/fw/multicast-strategy.cpp
index d861071..244ccfc 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-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -89,7 +89,7 @@
       continue;
     }
 
-    this->sendInterest(pitEntry, FaceEndpoint(outFace, 0), interest);
+    this->sendInterest(pitEntry, outFace, interest);
     NFD_LOG_DEBUG(interest << " from=" << ingress << " pitEntry-to=" << outFace.getId());
 
     if (suppressResult == RetxSuppressionResult::FORWARD) {
@@ -103,7 +103,7 @@
 
     lp::NackHeader nackHeader;
     nackHeader.setReason(lp::NackReason::NO_ROUTE);
-    this->sendNack(pitEntry, ingress, nackHeader);
+    this->sendNack(pitEntry, ingress.face, nackHeader);
 
     this->rejectPendingInterest(pitEntry);
   }
diff --git a/daemon/fw/multicast-strategy.hpp b/daemon/fw/multicast-strategy.hpp
index 15f0d0c..711680a 100644
--- a/daemon/fw/multicast-strategy.hpp
+++ b/daemon/fw/multicast-strategy.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -33,9 +33,7 @@
 namespace nfd {
 namespace fw {
 
-/** \brief a forwarding strategy that forwards Interest to all FIB nexthops
- *
- *  \note This strategy is not EndpointId-aware.
+/** \brief A forwarding strategy that forwards Interests to all FIB nexthops
  */
 class MulticastStrategy : public Strategy
                         , public ProcessNackTraits<MulticastStrategy>
diff --git a/daemon/fw/ncc-strategy.cpp b/daemon/fw/ncc-strategy.cpp
index 9909272..060821a 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-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -90,7 +90,7 @@
     deferFirst = meInfo.prediction;
     deferRange = time::microseconds((deferFirst.count() + 1) / 2);
     --nUpstreams;
-    this->sendInterest(pitEntry, FaceEndpoint(*bestFace, 0), interest);
+    this->sendInterest(pitEntry, *bestFace, interest);
     pitEntryInfo->bestFaceTimeout = getScheduler().schedule(meInfo.prediction,
       bind(&NccStrategy::timeoutOnBestFace, this, weak_ptr<pit::Entry>(pitEntry)));
   }
@@ -103,7 +103,7 @@
                  canForwardToLegacy(*pitEntry, outFace);
         });
     if (firstEligibleNexthop != nexthops.end()) {
-      this->sendInterest(pitEntry, FaceEndpoint(firstEligibleNexthop->getFace(), 0), interest);
+      this->sendInterest(pitEntry, firstEligibleNexthop->getFace(), interest);
     }
     else {
       this->rejectPendingInterest(pitEntry);
@@ -161,7 +161,7 @@
   if (previousFace != nullptr && fibEntry.hasNextHop(*previousFace) &&
       !wouldViolateScope(*inFace, interest, *previousFace) &&
       canForwardToLegacy(*pitEntry, *previousFace)) {
-    this->sendInterest(pitEntry, FaceEndpoint(*previousFace, 0), interest);
+    this->sendInterest(pitEntry, *previousFace, interest);
   }
 
   bool isForwarded = false;
@@ -170,7 +170,7 @@
     if (!wouldViolateScope(*inFace, interest, face) &&
         canForwardToLegacy(*pitEntry, face)) {
       isForwarded = true;
-      this->sendInterest(pitEntry, FaceEndpoint(face, 0), interest);
+      this->sendInterest(pitEntry, face, interest);
       break;
     }
   }
diff --git a/daemon/fw/ncc-strategy.hpp b/daemon/fw/ncc-strategy.hpp
index 473a87d..dadbded 100644
--- a/daemon/fw/ncc-strategy.hpp
+++ b/daemon/fw/ncc-strategy.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -31,9 +31,7 @@
 namespace nfd {
 namespace fw {
 
-/** \brief a forwarding strategy similar to CCNx 0.7.2
- *
- *  \note This strategy is not EndpointId-aware.
+/** \brief A forwarding strategy similar to CCNx 0.7.2
  */
 class NccStrategy : public Strategy
 {
diff --git a/daemon/fw/process-nack-traits.hpp b/daemon/fw/process-nack-traits.hpp
index 86e31e1..654c896 100644
--- a/daemon/fw/process-nack-traits.hpp
+++ b/daemon/fw/process-nack-traits.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -58,7 +58,7 @@
 
 private:
   virtual void
-  sendNackForProcessNackTraits(const shared_ptr<pit::Entry>& pitEntry, const Face& outFace,
+  sendNackForProcessNackTraits(const shared_ptr<pit::Entry>& pitEntry, Face& outFace,
                                const lp::NackHeader& header) = 0;
 
   virtual void
@@ -78,10 +78,10 @@
 
 private:
   void
-  sendNackForProcessNackTraits(const shared_ptr<pit::Entry>& pitEntry, const Face& outFace,
+  sendNackForProcessNackTraits(const shared_ptr<pit::Entry>& pitEntry, Face& outFace,
                                const lp::NackHeader& header) override
   {
-    m_strategy->sendNack(pitEntry, FaceEndpoint(outFace, 0), header);
+    m_strategy->sendNack(pitEntry, outFace, header);
   }
 
   void
diff --git a/daemon/fw/random-strategy.cpp b/daemon/fw/random-strategy.cpp
index a95d2cd..44e7b35 100644
--- a/daemon/fw/random-strategy.cpp
+++ b/daemon/fw/random-strategy.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -61,24 +61,23 @@
                                      const shared_ptr<pit::Entry>& pitEntry)
 {
   const fib::Entry& fibEntry = this->lookupFib(*pitEntry);
-  const Face& inFace = ingress.face;
   fib::NextHopList nhs;
 
   std::copy_if(fibEntry.getNextHops().begin(), fibEntry.getNextHops().end(), std::back_inserter(nhs),
-               [&] (const auto& nh) { return isNextHopEligible(inFace, interest, nh, pitEntry); });
+               [&] (const auto& nh) { return isNextHopEligible(ingress.face, interest, nh, pitEntry); });
 
   if (nhs.empty()) {
     NFD_LOG_DEBUG(interest << " from=" << ingress << " no nexthop");
 
     lp::NackHeader nackHeader;
     nackHeader.setReason(lp::NackReason::NO_ROUTE);
-    this->sendNack(pitEntry, ingress, nackHeader);
+    this->sendNack(pitEntry, ingress.face, nackHeader);
     this->rejectPendingInterest(pitEntry);
     return;
   }
 
   std::shuffle(nhs.begin(), nhs.end(), ndn::random::getRandomNumberEngine());
-  this->sendInterest(pitEntry, FaceEndpoint(nhs.front().getFace(), 0), interest);
+  this->sendInterest(pitEntry, nhs.front().getFace(), interest);
 }
 
 void
diff --git a/daemon/fw/random-strategy.hpp b/daemon/fw/random-strategy.hpp
index 6837e29..1aa170a 100644
--- a/daemon/fw/random-strategy.hpp
+++ b/daemon/fw/random-strategy.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -32,11 +32,9 @@
 namespace nfd {
 namespace fw {
 
-/** \brief Randomly chooses a nexthop
+/** \brief A forwarding strategy that randomly chooses a nexthop
  *
- * Sends an incoming interest to a random outgoing face,
- * excluding the incoming face.
- *
+ *  Sends the incoming Interest to a random outgoing face, excluding the incoming face.
  */
 class RandomStrategy : public Strategy
                      , public ProcessNackTraits<RandomStrategy>
diff --git a/daemon/fw/self-learning-strategy.cpp b/daemon/fw/self-learning-strategy.cpp
index 20fc28d..ea32709 100644
--- a/daemon/fw/self-learning-strategy.cpp
+++ b/daemon/fw/self-learning-strategy.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -80,7 +80,7 @@
       NFD_LOG_DEBUG("NACK non-discovery Interest=" << interest << " from=" << ingress << " noNextHop");
       lp::NackHeader nackHeader;
       nackHeader.setReason(lp::NackReason::NO_ROUTE);
-      this->sendNack(pitEntry, ingress, nackHeader);
+      this->sendNack(pitEntry, ingress.face, nackHeader);
       this->rejectPendingInterest(pitEntry);
     }
     else { // multicast it if matching FIB entry exists
@@ -112,7 +112,7 @@
   OutRecordInfo* outRecordInfo = outRecord->getStrategyInfo<OutRecordInfo>();
   if (outRecordInfo && outRecordInfo->isNonDiscoveryInterest) { // outgoing Interest was non-discovery
     if (!needPrefixAnn(pitEntry)) { // no need to attach a PA (common cases)
-      sendDataToAll(pitEntry, ingress, data);
+      sendDataToAll(pitEntry, ingress.face, data);
     }
     else { // needs a PA (to respond discovery Interest)
       asyncProcessData(pitEntry, ingress.face, data);
@@ -125,7 +125,7 @@
     }
     else { // Data contains no PrefixAnnouncement, upstreams do not support self-learning
     }
-    sendDataToAll(pitEntry, ingress, data);
+    sendDataToAll(pitEntry, ingress.face, data);
   }
 }
 
@@ -152,7 +152,7 @@
         wouldViolateScope(inFace, interest, outFace) || outFace.getScope() == ndn::nfd::FACE_SCOPE_LOCAL) {
       continue;
     }
-    this->sendInterest(pitEntry, FaceEndpoint(outFace, 0), interest);
+    this->sendInterest(pitEntry, outFace, interest);
     pitEntry->getOutRecord(outFace)->insertStrategyInfo<OutRecordInfo>().first->isNonDiscoveryInterest = false;
     NFD_LOG_DEBUG("send discovery Interest=" << interest << " from="
                   << inFace.getId() << " to=" << outFace.getId());
@@ -170,7 +170,7 @@
         wouldViolateScope(inFace, interest, outFace)) {
       continue;
     }
-    this->sendInterest(pitEntry, FaceEndpoint(outFace, 0), interest);
+    this->sendInterest(pitEntry, outFace, interest);
     pitEntry->getOutRecord(outFace)->insertStrategyInfo<OutRecordInfo>().first->isNonDiscoveryInterest = true;
     NFD_LOG_DEBUG("send non-discovery Interest=" << interest << " from="
                   << inFace.getId() << " to=" << outFace.getId());
@@ -195,7 +195,7 @@
             if (pitEntry && inFace) {
               NFD_LOG_DEBUG("found PrefixAnnouncement=" << pa.getAnnouncedName());
               data.setTag(make_shared<lp::PrefixAnnouncementTag>(lp::PrefixAnnouncementHeader(pa)));
-              this->sendDataToAll(pitEntry, FaceEndpoint(*inFace, 0), data);
+              this->sendDataToAll(pitEntry, *inFace, data);
               this->setExpiryTimer(pitEntry, 0_ms);
             }
             else {
diff --git a/daemon/fw/self-learning-strategy.hpp b/daemon/fw/self-learning-strategy.hpp
index 7265a9e..9b01005 100644
--- a/daemon/fw/self-learning-strategy.hpp
+++ b/daemon/fw/self-learning-strategy.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -33,14 +33,12 @@
 namespace nfd {
 namespace fw {
 
-/** \brief Self-learning strategy
+/** \brief Self-learning forwarding strategy
  *
  *  This strategy first broadcasts Interest to learn a single path towards data,
- *  then unicasts subsequent Interests along the learned path
+ *  then unicasts subsequent Interests along the learned path.
  *
  *  \see https://redmine.named-data.net/attachments/864/Self-learning-strategy-v1.pdf
- *
- *  \note This strategy is not EndpointId-aware
  */
 class SelfLearningStrategy : public Strategy
 {
diff --git a/daemon/fw/strategy.cpp b/daemon/fw/strategy.cpp
index d33ad9a..785cc8b 100644
--- a/daemon/fw/strategy.cpp
+++ b/daemon/fw/strategy.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -165,7 +165,7 @@
   NFD_LOG_DEBUG("afterContentStoreHit pitEntry=" << pitEntry->getName()
                 << " in=" << ingress << " data=" << data.getName());
 
-  this->sendData(pitEntry, data, ingress);
+  this->sendData(pitEntry, data, ingress.face);
 }
 
 void
@@ -177,25 +177,24 @@
 
   this->beforeSatisfyInterest(pitEntry, ingress, data);
 
-  this->sendDataToAll(pitEntry, ingress, data);
+  this->sendDataToAll(pitEntry, ingress.face, data);
 }
 
 void
-Strategy::afterReceiveNack(const FaceEndpoint& ingress, const lp::Nack& nack,
+Strategy::afterReceiveNack(const FaceEndpoint& ingress, const lp::Nack&,
                            const shared_ptr<pit::Entry>& pitEntry)
 {
   NFD_LOG_DEBUG("afterReceiveNack in=" << ingress << " pitEntry=" << pitEntry->getName());
 }
 
 void
-Strategy::onDroppedInterest(const FaceEndpoint& egress, const Interest& interest)
+Strategy::onDroppedInterest(const Face& egress, const Interest& interest)
 {
-  NFD_LOG_DEBUG("onDroppedInterest out=" << egress << " name=" << interest.getName());
+  NFD_LOG_DEBUG("onDroppedInterest out=" << egress.getId() << " name=" << interest.getName());
 }
 
 void
-Strategy::sendInterest(const shared_ptr<pit::Entry>& pitEntry,
-                       const FaceEndpoint& egress, const Interest& interest)
+Strategy::sendInterest(const shared_ptr<pit::Entry>& pitEntry, Face& egress, const Interest& interest)
 {
   if (interest.getTag<lp::PitToken>() != nullptr) {
     Interest interest2 = interest; // make a copy to preserve tag on original packet
@@ -214,20 +213,19 @@
 }
 
 void
-Strategy::sendData(const shared_ptr<pit::Entry>& pitEntry, const Data& data,
-                   const FaceEndpoint& egress)
+Strategy::sendData(const shared_ptr<pit::Entry>& pitEntry, const Data& data, Face& egress)
 {
   BOOST_ASSERT(pitEntry->getInterest().matchesData(data));
 
   shared_ptr<lp::PitToken> pitToken;
-  auto inRecord = pitEntry->getInRecord(egress.face);
+  auto inRecord = pitEntry->getInRecord(egress);
   if (inRecord != pitEntry->in_end()) {
     pitToken = inRecord->getInterest().getTag<lp::PitToken>();
   }
 
   // delete the PIT entry's in-record based on egress,
-  // since Data is sent to face and endpoint from which the Interest was received
-  pitEntry->deleteInRecord(egress.face);
+  // since the Data is sent to the face from which the Interest was received
+  pitEntry->deleteInRecord(egress);
 
   if (pitToken != nullptr) {
     Data data2 = data; // make a copy so each downstream can get a different PIT token
@@ -240,7 +238,7 @@
 
 void
 Strategy::sendDataToAll(const shared_ptr<pit::Entry>& pitEntry,
-                        const FaceEndpoint& ingress, const Data& data)
+                        const Face& inFace, const Data& data)
 {
   std::set<Face*> pendingDownstreams;
   auto now = time::steady_clock::now();
@@ -248,7 +246,7 @@
   // remember pending downstreams
   for (const pit::InRecord& inRecord : pitEntry->getInRecords()) {
     if (inRecord.getExpiry() > now) {
-      if (inRecord.getFace().getId() == ingress.face.getId() &&
+      if (inRecord.getFace().getId() == inFace.getId() &&
           inRecord.getFace().getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) {
         continue;
       }
@@ -257,29 +255,27 @@
   }
 
   for (const auto& pendingDownstream : pendingDownstreams) {
-    this->sendData(pitEntry, data, FaceEndpoint(*pendingDownstream, 0));
+    this->sendData(pitEntry, data, *pendingDownstream);
   }
 }
 
 void
 Strategy::sendNacks(const shared_ptr<pit::Entry>& pitEntry, const lp::NackHeader& header,
-                    std::initializer_list<FaceEndpoint> exceptFaceEndpoints)
+                    std::initializer_list<const Face*> exceptFaces)
 {
   // populate downstreams with all downstreams faces
-  std::set<Face*> downstreams;
+  std::unordered_set<Face*> downstreams;
   std::transform(pitEntry->in_begin(), pitEntry->in_end(), std::inserter(downstreams, downstreams.end()),
-                 [] (const pit::InRecord& inR) {
-                  return &inR.getFace();
-                 });
+                 [] (const pit::InRecord& inR) { return &inR.getFace(); });
 
   // delete excluded faces
-  for (const auto& exceptFaceEndpoint : exceptFaceEndpoints) {
-    downstreams.erase(&exceptFaceEndpoint.face);
+  for (auto exceptFace : exceptFaces) {
+    downstreams.erase(const_cast<Face*>(exceptFace));
   }
 
   // send Nacks
-  for (const auto& downstream : downstreams) {
-    this->sendNack(pitEntry, FaceEndpoint(*downstream, 0), header);
+  for (auto downstream : downstreams) {
+    this->sendNack(pitEntry, *downstream, header);
   }
   // warning: don't loop on pitEntry->getInRecords(), because in-record is deleted when sending Nack
 }
diff --git a/daemon/fw/strategy.hpp b/daemon/fw/strategy.hpp
index 190f1f2..7c93327 100644
--- a/daemon/fw/strategy.hpp
+++ b/daemon/fw/strategy.hpp
@@ -240,7 +240,7 @@
    *  In the base class this method does nothing.
    */
   virtual void
-  onDroppedInterest(const FaceEndpoint& egress, const Interest& interest);
+  onDroppedInterest(const Face& egress, const Interest& interest);
 
   /** \brief Trigger after new nexthop is added
    *
@@ -251,36 +251,35 @@
   afterNewNextHop(const fib::NextHop& nextHop, const shared_ptr<pit::Entry>& pitEntry);
 
 protected: // actions
-  /** \brief Send Interest to egress
-   *  \param pitEntry PIT entry
-   *  \param egress face through which to send out the Interest and destination endpoint
+  /** \brief Send an Interest packet.
+   *  \param pitEntry the PIT entry
+   *  \param egress face through which to send out the Interest
    *  \param interest the Interest packet
    */
   VIRTUAL_WITH_TESTS void
-  sendInterest(const shared_ptr<pit::Entry>& pitEntry,
-               const FaceEndpoint& egress, const Interest& interest);
+  sendInterest(const shared_ptr<pit::Entry>& pitEntry, Face& egress,
+               const Interest& interest);
 
-  /** \brief Send \p data to \p egress
-   *  \param pitEntry PIT entry
+  /** \brief Send a Data packet.
+   *  \param pitEntry the PIT entry
    *  \param data the Data packet
-   *  \param egress face through which to send out the Data and destination endpoint
+   *  \param egress face through which to send out the Data
    */
   VIRTUAL_WITH_TESTS void
-  sendData(const shared_ptr<pit::Entry>& pitEntry, const Data& data, const FaceEndpoint& egress);
+  sendData(const shared_ptr<pit::Entry>& pitEntry, const Data& data, Face& egress);
 
-  /** \brief Send \p data to all matched and qualified face-endpoint pairs
+  /** \brief Send a Data packet to all matched and qualified faces.
    *
-   *  A matched face is qualified if it is ad-hoc or it is NOT \p ingress
+   *  A matched face is qualified if it is ad-hoc or it is NOT \p inFace.
    *
-   *  \param pitEntry PIT entry
-   *  \param ingress face through which the Data comes from and endpoint of the sender
+   *  \param pitEntry the PIT entry
+   *  \param inFace face on which the Data arrived
    *  \param data the Data packet
    */
   VIRTUAL_WITH_TESTS void
-  sendDataToAll(const shared_ptr<pit::Entry>& pitEntry,
-                const FaceEndpoint& ingress, const Data& data);
+  sendDataToAll(const shared_ptr<pit::Entry>& pitEntry, const Face& inFace, const Data& data);
 
-  /** \brief Schedule the PIT entry for immediate deletion
+  /** \brief Schedule the PIT entry for immediate deletion.
    *
    *  This helper function sets the PIT entry expiry time to zero.
    *  The strategy should invoke this function when it concludes that the Interest cannot
@@ -292,31 +291,32 @@
     this->setExpiryTimer(pitEntry, 0_ms);
   }
 
-  /** \brief Send Nack to egress
-   *  \param pitEntry PIT entry
-   *  \param egress face through which to send out the Nack and destination endpoint
-   *  \param header Nack header
+  /** \brief Send a Nack packet.
    *
-   *  The egress must have a PIT in-record, otherwise this method has no effect.
+   *  The egress face must have a PIT in-record, otherwise this method has no effect.
+   *
+   *  \param pitEntry the PIT entry
+   *  \param egress face through which to send out the Nack
+   *  \param header the Nack header
    */
   VIRTUAL_WITH_TESTS void
-  sendNack(const shared_ptr<pit::Entry>& pitEntry,
-           const FaceEndpoint& egress, const lp::NackHeader& header)
+  sendNack(const shared_ptr<pit::Entry>& pitEntry, Face& egress,
+           const lp::NackHeader& header)
   {
     m_forwarder.onOutgoingNack(pitEntry, egress, header);
   }
 
-  /** \brief Send Nack to every face-endpoint pair that has an in-record, except those in \p exceptFaceEndpoints
-   *  \param pitEntry PIT entry
-   *  \param header NACK header
-   *  \param exceptFaceEndpoints list of face-endpoint pairs that should be excluded from sending Nacks
-   *  \note This is not an action, but a helper that invokes the sendNack action.
+  /** \brief Send Nack to every face that has an in-record, except those in \p exceptFaces
+   *  \param pitEntry the PIT entry
+   *  \param header the Nack header
+   *  \param exceptFaces list of faces that should be excluded from sending Nacks
+   *  \note This is not an action, but a helper that invokes the sendNack() action.
    */
   void
   sendNacks(const shared_ptr<pit::Entry>& pitEntry, const lp::NackHeader& header,
-            std::initializer_list<FaceEndpoint> exceptFaceEndpoints = {});
+            std::initializer_list<const Face*> exceptFaces = {});
 
-  /** \brief Schedule the PIT entry to be erased after \p duration
+  /** \brief Schedule the PIT entry to be erased after \p duration.
    */
   void
   setExpiryTimer(const shared_ptr<pit::Entry>& pitEntry, time::milliseconds duration)
@@ -325,7 +325,7 @@
   }
 
 protected: // accessors
-  /** \brief Performs a FIB lookup, considering Link object if present
+  /** \brief Performs a FIB lookup, considering Link object if present.
    */
   const fib::Entry&
   lookupFib(const pit::Entry& pitEntry) const;