blob: 6a45c7e75548b7e5bf90d1ceca7c205cf8f68dd0 [file] [log] [blame]
Vince Lehman8a4c29e2016-07-11 08:49:35 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2016, 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 "asf-strategy.hpp"
27
28#include "core/logger.hpp"
29
30namespace nfd {
31namespace fw {
32namespace asf {
33
34NFD_LOG_INIT("AsfStrategy");
35
36const Name AsfStrategy::STRATEGY_NAME("ndn:/localhost/nfd/strategy/asf/%FD%01");
37const time::milliseconds AsfStrategy::RETX_SUPPRESSION_INITIAL(10);
38const time::milliseconds AsfStrategy::RETX_SUPPRESSION_MAX(250);
39
40NFD_REGISTER_STRATEGY(AsfStrategy);
41
42AsfStrategy::AsfStrategy(Forwarder& forwarder, const Name& name)
43 : Strategy(forwarder, name)
44 , m_measurements(getMeasurements())
45 , m_probing(m_measurements)
46 , m_retxSuppression(RETX_SUPPRESSION_INITIAL,
47 RetxSuppressionExponential::DEFAULT_MULTIPLIER,
48 RETX_SUPPRESSION_MAX)
49{
50}
51
52AsfStrategy::~AsfStrategy()
53{
54}
55
56void
57AsfStrategy::afterReceiveInterest(const Face& inFace,
58 const Interest& interest,
59 shared_ptr<fib::Entry> fibEntry,
60 shared_ptr<pit::Entry> pitEntry)
61{
62 // Should the Interest be suppressed?
63 RetxSuppression::Result suppressResult = m_retxSuppression.decide(inFace, interest, *pitEntry);
64
65 switch (suppressResult) {
66 case RetxSuppression::NEW:
67 case RetxSuppression::FORWARD:
68 break;
69 case RetxSuppression::SUPPRESS:
70 NFD_LOG_DEBUG(interest << " from=" << inFace.getId() << " suppressed");
71 return;
72 }
73
74 const fib::NextHopList& nexthops = fibEntry->getNextHops();
75
76 if (nexthops.size() == 0) {
77 sendNoRouteNack(inFace, interest, pitEntry);
78 this->rejectPendingInterest(pitEntry);
79 return;
80 }
81
82 const shared_ptr<Face> faceToUse = getBestFaceForForwarding(*fibEntry, interest, inFace);
83
84 if (faceToUse == nullptr) {
85 sendNoRouteNack(inFace, interest, pitEntry);
86 this->rejectPendingInterest(pitEntry);
87 return;
88 }
89
90 forwardInterest(interest, *fibEntry, pitEntry, faceToUse);
91
92 // If necessary, send probe
93 if (m_probing.isProbingNeeded(fibEntry, interest)) {
94 shared_ptr<Face> faceToProbe = m_probing.getFaceToProbe(inFace, interest, fibEntry, *faceToUse);
95
96 if (faceToProbe != nullptr) {
97 NFD_LOG_TRACE("Sending probe for " << fibEntry->getPrefix()
98 << " to FaceId: " << faceToProbe->getId());
99
100 bool wantNewNonce = true;
101 forwardInterest(interest, *fibEntry, pitEntry, faceToProbe, wantNewNonce);
102 m_probing.afterForwardingProbe(fibEntry, interest);
103 }
104 }
105}
106
107void
108AsfStrategy::beforeSatisfyInterest(shared_ptr<pit::Entry> pitEntry,
109 const Face& inFace,
110 const Data& data)
111{
112 shared_ptr<NamespaceInfo> namespaceInfo = m_measurements.getNamespaceInfo(pitEntry->getName());
113
114 if (namespaceInfo == nullptr) {
115 NFD_LOG_TRACE("Could not find measurements entry for " << pitEntry->getName());
116 return;
117 }
118
119 // Record the RTT between the Interest out to Data in
120 FaceInfo& faceInfo = namespaceInfo->get(inFace.getId());
121 faceInfo.recordRtt(pitEntry, inFace);
122
123 // Extend lifetime for measurements associated with Face
124 namespaceInfo->extendFaceInfoLifetime(faceInfo, inFace);
125
126 if (faceInfo.isTimeoutScheduled()) {
127 faceInfo.cancelTimeoutEvent(data.getName());
128 }
129}
130
131void
132AsfStrategy::afterReceiveNack(const Face& inFace, const lp::Nack& nack,
133 shared_ptr<fib::Entry> fibEntry,
134 shared_ptr<pit::Entry> pitEntry)
135{
136 NFD_LOG_DEBUG("Nack for " << nack.getInterest() << " from=" << inFace.getId() << ": " << nack.getReason());
137 onTimeout(pitEntry->getName(), inFace.getId());
138}
139
140////////////////////////////////////////////////////////////////////////////////
141////////////////////////////////////////////////////////////////////////////////
142
143void
144AsfStrategy::forwardInterest(const Interest& interest,
145 const fib::Entry& fibEntry,
146 shared_ptr<pit::Entry> pitEntry,
147 shared_ptr<Face> outFace,
148 bool wantNewNonce)
149{
150 this->sendInterest(pitEntry, outFace, wantNewNonce);
151
152 FaceInfo& faceInfo = m_measurements.getOrCreateFaceInfo(fibEntry, interest, *outFace);
153
154 // Refresh measurements since Face is being used for forwarding
155 NamespaceInfo& namespaceInfo = m_measurements.getOrCreateNamespaceInfo(fibEntry, interest);
156 namespaceInfo.extendFaceInfoLifetime(faceInfo, *outFace);
157
158 if (!faceInfo.isTimeoutScheduled()) {
159 // Estimate and schedule timeout
160 RttEstimator::Duration timeout = faceInfo.computeRto();
161
162 NFD_LOG_TRACE("Scheduling timeout for " << fibEntry.getPrefix()
163 << " FaceId: " << outFace->getId()
164 << " in " << time::duration_cast<time::milliseconds>(timeout) << " ms");
165
166 scheduler::EventId id = scheduler::schedule(timeout,
167 bind(&AsfStrategy::onTimeout, this, interest.getName(), outFace->getId()));
168
169 faceInfo.setTimeoutEvent(id, interest.getName());
170 }
171}
172
173struct FaceStats
174{
175 shared_ptr<Face> face;
176 RttStats::Rtt rtt;
177 RttStats::Rtt srtt;
178 uint64_t cost;
179};
180
181double
182getValueForSorting(const FaceStats& stats)
183{
184 // These values allow faces with no measurements to be ranked better than timeouts
185 // srtt < RTT_NO_MEASUREMENT < RTT_TIMEOUT
186 static const RttStats::Rtt SORTING_RTT_TIMEOUT = time::microseconds::max();
187 static const RttStats::Rtt SORTING_RTT_NO_MEASUREMENT = SORTING_RTT_TIMEOUT / 2;
188
189 if (stats.rtt == RttStats::RTT_TIMEOUT) {
190 return SORTING_RTT_TIMEOUT.count();
191 }
192 else if (stats.rtt == RttStats::RTT_NO_MEASUREMENT) {
193 return SORTING_RTT_NO_MEASUREMENT.count();
194 }
195 else {
196 return stats.srtt.count();
197 }
198}
199
200const shared_ptr<Face>
201AsfStrategy::getBestFaceForForwarding(const fib::Entry& fibEntry, const ndn::Interest& interest, const Face& inFace)
202{
203 NFD_LOG_TRACE("Looking for best face for " << fibEntry.getPrefix());
204
205 typedef std::function<bool(const FaceStats&, const FaceStats&)> FaceStatsPredicate;
206 typedef std::set<FaceStats, FaceStatsPredicate> FaceStatsSet;
207
208 FaceStatsSet rankedFaces(
209 [] (const FaceStats& lhs, const FaceStats& rhs) -> bool {
210 // Sort by RTT and then by cost
211 double lhsValue = getValueForSorting(lhs);
212 double rhsValue = getValueForSorting(rhs);
213
214 if (lhsValue < rhsValue) {
215 return true;
216 }
217 else if (lhsValue == rhsValue) {
218 return lhs.cost < rhs.cost;
219 }
220 else {
221 return false;
222 }
223 });
224
225 for (const fib::NextHop& hop : fibEntry.getNextHops()) {
226
227 const shared_ptr<Face>& hopFace = hop.getFace();
228
229 if (hopFace->getId() == inFace.getId()) {
230 continue;
231 }
232
233 FaceInfo* info = m_measurements.getFaceInfo(fibEntry, interest, *hopFace);
234
235 if (info == nullptr) {
236 FaceStats stats = {hopFace,
237 RttStats::RTT_NO_MEASUREMENT,
238 RttStats::RTT_NO_MEASUREMENT,
239 hop.getCost()};
240
241 rankedFaces.insert(stats);
242 }
243 else {
244 FaceStats stats = {hopFace, info->getRtt(), info->getSrtt(), hop.getCost()};
245 rankedFaces.insert(stats);
246 }
247 }
248
249 FaceStatsSet::iterator it = rankedFaces.begin();
250
251 if (it != rankedFaces.end()) {
252 return it->face;
253 }
254 else {
255 return nullptr;
256 }
257}
258
259void
260AsfStrategy::onTimeout(const ndn::Name& interestName, nfd::face::FaceId faceId)
261{
262 NFD_LOG_TRACE("FaceId: " << faceId << " for " << interestName << " has timed-out");
263
264 shared_ptr<NamespaceInfo> namespaceInfo = m_measurements.getNamespaceInfo(interestName);
265
266 if (namespaceInfo == nullptr) {
267 NFD_LOG_TRACE("FibEntry for " << interestName << " has been removed since timeout scheduling");
268 return;
269 }
270
271 FaceInfoTable::iterator it = namespaceInfo->find(faceId);
272
273 if (it == namespaceInfo->end()) {
274 it = namespaceInfo->insert(faceId);
275 }
276
277 FaceInfo& faceInfo = it->second;
278 faceInfo.recordTimeout(interestName);
279}
280
281void
282AsfStrategy::sendNoRouteNack(const Face& inFace, const Interest& interest, shared_ptr<pit::Entry> pitEntry)
283{
284 NFD_LOG_DEBUG(interest << " from=" << inFace.getId() << " noNextHop");
285
286 lp::NackHeader nackHeader;
287 nackHeader.setReason(lp::NackReason::NO_ROUTE);
288 this->sendNack(pitEntry, inFace, nackHeader);
289}
290
291} // namespace asf
292} // namespace fw
293} // namespace nfd