blob: e726d27acab46bbd91713a496715c491ef2c5fb7 [file] [log] [blame]
Junxiao Shi80ee7cb2014-12-14 10:53:05 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2015, 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 "access-strategy.hpp"
27#include "core/logger.hpp"
28
29namespace nfd {
30namespace fw {
31
32NFD_LOG_INIT("AccessStrategy");
33
34const Name AccessStrategy::STRATEGY_NAME("ndn:/localhost/nfd/strategy/access/%FD%01");
35
36AccessStrategy::AccessStrategy(Forwarder& forwarder, const Name& name)
37 : Strategy(forwarder, name)
38 , m_removeFaceInfoConn(this->beforeRemoveFace.connect(
39 bind(&AccessStrategy::removeFaceInfo, this, _1)))
40{
41}
42
43AccessStrategy::~AccessStrategy()
44{
45}
46
47void
48AccessStrategy::afterReceiveInterest(const Face& inFace,
49 const Interest& interest,
50 shared_ptr<fib::Entry> fibEntry,
51 shared_ptr<pit::Entry> pitEntry)
52{
Junxiao Shia788e252015-01-28 17:06:25 -070053 RetxSuppression::Result suppressResult = m_retxSuppression.decide(inFace, interest, *pitEntry);
Junxiao Shi80ee7cb2014-12-14 10:53:05 -070054 switch (suppressResult) {
Junxiao Shia788e252015-01-28 17:06:25 -070055 case RetxSuppression::NEW:
Junxiao Shi80ee7cb2014-12-14 10:53:05 -070056 this->afterReceiveNewInterest(inFace, interest, fibEntry, pitEntry);
57 break;
Junxiao Shia788e252015-01-28 17:06:25 -070058 case RetxSuppression::FORWARD:
Junxiao Shi80ee7cb2014-12-14 10:53:05 -070059 this->afterReceiveRetxInterest(inFace, interest, fibEntry, pitEntry);
60 break;
Junxiao Shia788e252015-01-28 17:06:25 -070061 case RetxSuppression::SUPPRESS:
Junxiao Shi80ee7cb2014-12-14 10:53:05 -070062 NFD_LOG_DEBUG(interest << " interestFrom " << inFace.getId() << " retx-suppress");
63 break;
64 default:
65 BOOST_ASSERT(false);
66 break;
67 }
68}
69
70void
71AccessStrategy::afterReceiveNewInterest(const Face& inFace,
72 const Interest& interest,
73 shared_ptr<fib::Entry> fibEntry,
74 shared_ptr<pit::Entry> pitEntry)
75{
76 Name miName;
77 shared_ptr<MtInfo> mi;
78 std::tie(miName, mi) = this->findPrefixMeasurements(*pitEntry);
79
80 // has measurements for Interest Name?
81 if (mi != nullptr) {
82 NFD_LOG_DEBUG(interest << " interestFrom " << inFace.getId() <<
83 " new-interest mi=" << miName);
84
85 // send to last working nexthop
86 bool isSentToLastNexthop = this->sendToLastNexthop(inFace, pitEntry, *mi, fibEntry);
87
88 if (isSentToLastNexthop) {
89 return;
90 }
91 }
92 else {
93 NFD_LOG_DEBUG(interest << " interestFrom " << inFace.getId() <<
94 " new-interest no-mi");
95 }
96
97 // no measurements, or last working nexthop unavailable
98
99 // multicast to all nexthops
100 this->multicast(pitEntry, fibEntry);
101}
102
103void
104AccessStrategy::afterReceiveRetxInterest(const Face& inFace,
105 const Interest& interest,
106 shared_ptr<fib::Entry> fibEntry,
107 shared_ptr<pit::Entry> pitEntry)
108{
109 NFD_LOG_DEBUG(interest << " interestFrom " << inFace.getId() << " retx-forward");
110 this->multicast(pitEntry, fibEntry, std::unordered_set<FaceId>{inFace.getId()});
111}
112
113bool
114AccessStrategy::sendToLastNexthop(const Face& inFace, shared_ptr<pit::Entry> pitEntry, MtInfo& mi,
115 shared_ptr<fib::Entry> fibEntry)
116{
117 if (mi.lastNexthop == INVALID_FACEID) {
118 NFD_LOG_DEBUG(pitEntry->getInterest() << " no-last-nexthop");
119 return false;
120 }
121
122 if (mi.lastNexthop == inFace.getId()) {
123 NFD_LOG_DEBUG(pitEntry->getInterest() << " last-nexthop-is-downstream");
124 return false;
125 }
126
127 shared_ptr<Face> face = this->getFace(mi.lastNexthop);
128 if (face == nullptr || !fibEntry->hasNextHop(face)) {
129 NFD_LOG_DEBUG(pitEntry->getInterest() << " last-nexthop-gone");
130 return false;
131 }
132
133 if (pitEntry->violatesScope(*face)) {
134 NFD_LOG_DEBUG(pitEntry->getInterest() << " last-nexthop-violates-scope");
135 return false;
136 }
137
138 RttEstimator::Duration rto = mi.rtt.computeRto();
139 NFD_LOG_DEBUG(pitEntry->getInterest() << " interestTo " << mi.lastNexthop <<
140 " last-nexthop rto=" << time::duration_cast<time::microseconds>(rto).count());
141
142 this->sendInterest(pitEntry, face);
143
144 // schedule RTO timeout
145 shared_ptr<PitInfo> pi = pitEntry->getOrCreateStrategyInfo<PitInfo>();
146 pi->rtoTimer = scheduler::schedule(rto,
147 bind(&AccessStrategy::afterRtoTimeout, this, weak_ptr<pit::Entry>(pitEntry),
148 weak_ptr<fib::Entry>(fibEntry), inFace.getId(), mi.lastNexthop));
149
150 return true;
151}
152
153void
154AccessStrategy::afterRtoTimeout(weak_ptr<pit::Entry> pitWeak, weak_ptr<fib::Entry> fibWeak,
155 FaceId inFace, FaceId firstOutFace)
156{
157 shared_ptr<pit::Entry> pitEntry = pitWeak.lock();
158 BOOST_ASSERT(pitEntry != nullptr);
159 // pitEntry can't become nullptr, because RTO timer should be cancelled upon pitEntry destruction
160
161 shared_ptr<fib::Entry> fibEntry = fibWeak.lock();
162 if (fibEntry == nullptr) {
163 NFD_LOG_DEBUG(pitEntry->getInterest() << " timeoutFrom " << firstOutFace << " fib-gone");
164 return;
165 }
166
167 NFD_LOG_DEBUG(pitEntry->getInterest() << " timeoutFrom " << firstOutFace <<
168 " multicast-except " << inFace << ',' << firstOutFace);
169 this->multicast(pitEntry, fibEntry, std::unordered_set<FaceId>{inFace, firstOutFace});
170}
171
172void
173AccessStrategy::multicast(shared_ptr<pit::Entry> pitEntry, shared_ptr<fib::Entry> fibEntry,
174 std::unordered_set<FaceId> exceptFaces)
175{
176 for (const fib::NextHop& nexthop : fibEntry->getNextHops()) {
177 shared_ptr<Face> face = nexthop.getFace();
178 if (exceptFaces.count(face->getId()) > 0) {
179 continue;
180 }
181 NFD_LOG_DEBUG(pitEntry->getInterest() << " interestTo " << face->getId() <<
182 " multicast");
183 this->sendInterest(pitEntry, face);
184 }
185}
186
187void
188AccessStrategy::beforeSatisfyInterest(shared_ptr<pit::Entry> pitEntry,
189 const Face& inFace, const Data& data)
190{
191 shared_ptr<PitInfo> pi = pitEntry->getStrategyInfo<PitInfo>();
192 if (pi != nullptr) {
193 pi->rtoTimer.cancel();
194 }
195
196 if (pitEntry->getInRecords().empty()) { // already satisfied by another upstream
197 NFD_LOG_DEBUG(pitEntry->getInterest() << " dataFrom " << inFace.getId() <<
198 " not-fastest");
199 return;
200 }
201
202 pit::OutRecordCollection::const_iterator outRecord = pitEntry->getOutRecord(inFace);
203 if (outRecord == pitEntry->getOutRecords().end()) { // no OutRecord
204 NFD_LOG_DEBUG(pitEntry->getInterest() << " dataFrom " << inFace.getId() <<
205 " no-out-record");
206 return;
207 }
208
209 time::steady_clock::Duration rtt = time::steady_clock::now() - outRecord->getLastRenewed();
210 NFD_LOG_DEBUG(pitEntry->getInterest() << " dataFrom " << inFace.getId() <<
211 " rtt=" << time::duration_cast<time::microseconds>(rtt).count());
212 this->updateMeasurements(inFace, data, time::duration_cast<RttEstimator::Duration>(rtt));
213}
214
215void
216AccessStrategy::updateMeasurements(const Face& inFace, const Data& data,
217 const RttEstimator::Duration& rtt)
218{
219 FaceInfo& fi = m_fit[inFace.getId()];
220 fi.rtt.addMeasurement(rtt);
221
222 shared_ptr<MtInfo> mi = this->addPrefixMeasurements(data);
223 if (mi->lastNexthop != inFace.getId()) {
224 mi->lastNexthop = inFace.getId();
225 mi->rtt = fi.rtt;
226 }
227 else {
228 mi->rtt.addMeasurement(rtt);
229 }
230}
231
232AccessStrategy::MtInfo::MtInfo()
233 : lastNexthop(INVALID_FACEID)
234 , rtt(1, time::milliseconds(1), 0.1)
235{
236}
237
238std::tuple<Name, shared_ptr<AccessStrategy::MtInfo>>
239AccessStrategy::findPrefixMeasurements(const pit::Entry& pitEntry)
240{
241 shared_ptr<measurements::Entry> me = this->getMeasurements().findLongestPrefixMatch(pitEntry);
242 if (me == nullptr) {
243 return std::forward_as_tuple(Name(), nullptr);
244 }
245
246 shared_ptr<MtInfo> mi = me->getStrategyInfo<MtInfo>();
247 BOOST_ASSERT(mi != nullptr);
248 // XXX after runtime strategy change, it's possible that me exists but mi doesn't exist;
249 // this case needs another longest prefix match until mi is found
250 return std::forward_as_tuple(me->getName(), mi);
251}
252
253shared_ptr<AccessStrategy::MtInfo>
254AccessStrategy::addPrefixMeasurements(const Data& data)
255{
256 shared_ptr<measurements::Entry> me;
257 if (data.getName().size() >= 1) {
258 me = this->getMeasurements().get(data.getName().getPrefix(-1));
259 }
260 if (me == nullptr) { // parent of Data Name is not in this strategy, or Data Name is empty
261 me = this->getMeasurements().get(data.getName());
262 // Data Name must be in this strategy
263 BOOST_ASSERT(me != nullptr);
264 }
265
266 return me->getOrCreateStrategyInfo<MtInfo>();
267}
268
269AccessStrategy::FaceInfo::FaceInfo()
270 : rtt(1, time::milliseconds(1), 0.1)
271{
272}
273
274void
275AccessStrategy::removeFaceInfo(shared_ptr<Face> face)
276{
277 m_fit.erase(face->getId());
278}
279
280} // namespace fw
281} // namespace nfd