blob: 58300fe1b535ecc0425db9160cd1c720d3f1bd0e [file] [log] [blame]
Junxiao Shi986b8492014-08-20 12:07:14 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014, Regents of the University of California,
4 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis
10 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#include "best-route-strategy2.hpp"
27#include "core/logger.hpp"
28
29namespace nfd {
30namespace fw {
31
32NFD_LOG_INIT("BestRouteStrategy2");
33
34/// \todo set to ndn:/localhost/nfd/strategy/best-route/%FD%02 after #1893 completion
35const Name BestRouteStrategy2::STRATEGY_NAME("ndn:/localhost/nfd/strategy/best-route");
36/// \todo don't use fixed interval; make it adaptive or use exponential back-off #1913
37const time::milliseconds BestRouteStrategy2::MIN_RETRANSMISSION_INTERVAL(100);
38
39BestRouteStrategy2::BestRouteStrategy2(Forwarder& forwarder, const Name& name)
40 : Strategy(forwarder, name)
41{
42}
43
44/** \brief determines whether a NextHop is eligible
45 * \param currentDownstream incoming FaceId of current Interest
46 * \param wantUnused if true, NextHop must not have unexpired OutRecord
47 * \param now time::steady_clock::now(), ignored if !wantUnused
48 */
49static inline bool
50predicate_NextHop_eligible(const shared_ptr<pit::Entry>& pitEntry,
51 const fib::NextHop& nexthop, FaceId currentDownstream,
52 bool wantUnused = false,
53 time::steady_clock::TimePoint now = time::steady_clock::TimePoint::min())
54{
55 shared_ptr<Face> upstream = nexthop.getFace();
56
57 // upstream is current downstream
58 if (upstream->getId() == currentDownstream)
59 return false;
60
61 // forwarding would violate scope
62 if (pitEntry->violatesScope(*upstream))
63 return false;
64
65 if (wantUnused) {
66 // NextHop must not have unexpired OutRecord
67 pit::OutRecordCollection::const_iterator outRecord = pitEntry->getOutRecord(upstream);
68 if (outRecord != pitEntry->getOutRecords().end() &&
69 outRecord->getExpiry() > now) {
70 return false;
71 }
72 }
73
74 return true;
75}
76
77static inline bool
78compare_OutRecord_lastRenewed(const pit::OutRecord& a, const pit::OutRecord& b)
79{
80 return a.getLastRenewed() < b.getLastRenewed();
81}
82
83/** \brief pick an eligible NextHop with earliest OutRecord
84 * \note It is assumed that every nexthop has an OutRecord
85 */
86static inline fib::NextHopList::const_iterator
87findEligibleNextHopWithEarliestOutRecord(const shared_ptr<pit::Entry>& pitEntry,
88 const fib::NextHopList& nexthops,
89 FaceId currentDownstream)
90{
91 fib::NextHopList::const_iterator found = nexthops.end();
92 time::steady_clock::TimePoint earliestRenewed = time::steady_clock::TimePoint::max();
93 for (fib::NextHopList::const_iterator it = nexthops.begin(); it != nexthops.end(); ++it) {
94 if (!predicate_NextHop_eligible(pitEntry, *it, currentDownstream))
95 continue;
96 pit::OutRecordCollection::const_iterator outRecord = pitEntry->getOutRecord(it->getFace());
97 BOOST_ASSERT(outRecord != pitEntry->getOutRecords().end());
98 if (outRecord->getLastRenewed() < earliestRenewed) {
99 found = it;
100 earliestRenewed = outRecord->getLastRenewed();
101 }
102 }
103 return found;
104}
105
106void
107BestRouteStrategy2::afterReceiveInterest(const Face& inFace,
108 const Interest& interest,
109 shared_ptr<fib::Entry> fibEntry,
110 shared_ptr<pit::Entry> pitEntry)
111{
112 const fib::NextHopList& nexthops = fibEntry->getNextHops();
113 fib::NextHopList::const_iterator it = nexthops.end();
114
115 bool isNewPitEntry = !pitEntry->hasUnexpiredOutRecords();
116 if (isNewPitEntry) {
117 // forward to nexthop with lowest cost except downstream
118 it = std::find_if(nexthops.begin(), nexthops.end(),
119 bind(&predicate_NextHop_eligible, pitEntry, _1, inFace.getId(),
120 false, time::steady_clock::TimePoint::min()));
121
122 if (it == nexthops.end()) {
123 NFD_LOG_DEBUG(interest << " from=" << inFace.getId() << " noNextHop");
124 this->rejectPendingInterest(pitEntry);
125 return;
126 }
127
128 shared_ptr<Face> outFace = it->getFace();
129 this->sendInterest(pitEntry, outFace);
130 NFD_LOG_DEBUG(interest << " from=" << inFace.getId()
131 << " newPitEntry-to=" << outFace->getId());
132 return;
133 }
134
135 // when was the last outgoing Interest?
136 const pit::OutRecordCollection& outRecords = pitEntry->getOutRecords();
137 pit::OutRecordCollection::const_iterator lastOutgoing = std::max_element(
138 outRecords.begin(), outRecords.end(), &compare_OutRecord_lastRenewed);
139 BOOST_ASSERT(lastOutgoing != outRecords.end()); // otherwise it's new PIT entry
140
141 time::steady_clock::TimePoint now = time::steady_clock::now();
142 time::steady_clock::Duration sinceLastOutgoing = now - lastOutgoing->getLastRenewed();
143 bool shouldRetransmit = sinceLastOutgoing >= MIN_RETRANSMISSION_INTERVAL;
144 if (!shouldRetransmit) {
145 NFD_LOG_DEBUG(interest << " from=" << inFace.getId()
146 << " dontRetransmit sinceLastOutgoing=" << sinceLastOutgoing.count());
147 return;
148 }
149
150 // find an unused upstream with lowest cost except downstream
151 it = std::find_if(nexthops.begin(), nexthops.end(),
152 bind(&predicate_NextHop_eligible, pitEntry, _1, inFace.getId(), true, now));
153 if (it != nexthops.end()) {
154 shared_ptr<Face> outFace = it->getFace();
155 this->sendInterest(pitEntry, outFace);
156 NFD_LOG_DEBUG(interest << " from=" << inFace.getId()
157 << " retransmit-unused-to=" << outFace->getId());
158 return;
159 }
160
161 // find an eligible upstream that is used earliest
162 it = findEligibleNextHopWithEarliestOutRecord(pitEntry, nexthops, inFace.getId());
163 if (it == nexthops.end()) {
164 NFD_LOG_DEBUG(interest << " from=" << inFace.getId() << " retransmitNoNextHop");
165 }
166 else {
167 shared_ptr<Face> outFace = it->getFace();
168 this->sendInterest(pitEntry, outFace);
169 NFD_LOG_DEBUG(interest << " from=" << inFace.getId()
170 << " retransmit-retry-to=" << outFace->getId());
171 }
172}
173
174} // namespace fw
175} // namespace nfd