blob: 4d0deeb9fee13cc01f54bd22dce98d882c9e6619 [file] [log] [blame]
Alexander Afanasyev33b72772014-01-26 23:22:58 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyev9bcbc7c2014-04-06 19:37:37 -07003 * 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 *
10 * This file is part of NFD (Named Data Networking Forwarding Daemon).
11 * See AUTHORS.md for complete list of NFD authors and contributors.
12 *
13 * NFD is free software: you can redistribute it and/or modify it under the terms
14 * of the GNU General Public License as published by the Free Software Foundation,
15 * either version 3 of the License, or (at your option) any later version.
16 *
17 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
18 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
19 * PURPOSE. See the GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along with
22 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
23 **/
Alexander Afanasyev33b72772014-01-26 23:22:58 -080024
25#include "forwarder.hpp"
Junxiao Shid938a6b2014-05-11 23:40:29 -070026#include <ndn-cxx/util/random.hpp>
Steve DiBenedettobf6a93d2014-03-21 14:03:02 -060027#include "core/logger.hpp"
Junxiao Shid938a6b2014-05-11 23:40:29 -070028#include "available-strategies.hpp"
Alexander Afanasyev33b72772014-01-26 23:22:58 -080029
Alexander Afanasyev18bbf812014-01-29 01:40:23 -080030namespace nfd {
Alexander Afanasyev33b72772014-01-26 23:22:58 -080031
Junxiao Shi8c8d2182014-01-30 22:33:00 -070032NFD_LOG_INIT("Forwarder");
33
Junxiao Shif3c07812014-03-11 21:48:49 -070034using fw::Strategy;
35
Junxiao Shif3c07812014-03-11 21:48:49 -070036const Name Forwarder::LOCALHOST_NAME("ndn:/localhost");
Junxiao Shi88884492014-02-15 15:57:43 -070037
Junxiao Shic041ca32014-02-25 20:01:15 -070038Forwarder::Forwarder()
Junxiao Shia4f2be82014-03-02 22:56:41 -070039 : m_faceTable(*this)
HangZhangad4afd12014-03-01 11:03:08 +080040 , m_fib(m_nameTree)
Haowei Yuan78c84d12014-02-27 15:35:13 -060041 , m_pit(m_nameTree)
HangZhangc85a23c2014-03-01 15:55:55 +080042 , m_measurements(m_nameTree)
Junxiao Shif3c07812014-03-11 21:48:49 -070043 , m_strategyChoice(m_nameTree, fw::makeDefaultStrategy(*this))
Alexander Afanasyev33b72772014-01-26 23:22:58 -080044{
Junxiao Shif3c07812014-03-11 21:48:49 -070045 fw::installStrategies(*this);
Alexander Afanasyev33b72772014-01-26 23:22:58 -080046}
47
Steve DiBenedettobf6a93d2014-03-21 14:03:02 -060048Forwarder::~Forwarder()
49{
50
51}
52
Alexander Afanasyev33b72772014-01-26 23:22:58 -080053void
Junxiao Shid3c792f2014-01-30 00:46:13 -070054Forwarder::onIncomingInterest(Face& inFace, const Interest& interest)
55{
56 // receive Interest
Junxiao Shif3c07812014-03-11 21:48:49 -070057 NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
58 " interest=" << interest.getName());
Junxiao Shi06887ac2014-02-13 20:15:42 -070059 const_cast<Interest&>(interest).setIncomingFaceId(inFace.getId());
Junxiao Shi6e694322014-04-03 10:27:13 -070060 m_counters.getNInInterests() ++;
Junxiao Shic041ca32014-02-25 20:01:15 -070061
Junxiao Shi88884492014-02-15 15:57:43 -070062 // /localhost scope control
Junxiao Shif3c07812014-03-11 21:48:49 -070063 bool isViolatingLocalhost = !inFace.isLocal() &&
64 LOCALHOST_NAME.isPrefixOf(interest.getName());
65 if (isViolatingLocalhost) {
66 NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
67 " interest=" << interest.getName() << " violates /localhost");
68 // (drop)
Junxiao Shi88884492014-02-15 15:57:43 -070069 return;
70 }
Junxiao Shic041ca32014-02-25 20:01:15 -070071
Junxiao Shid3c792f2014-01-30 00:46:13 -070072 // PIT insert
Junxiao Shi40631842014-03-01 13:52:37 -070073 shared_ptr<pit::Entry> pitEntry = m_pit.insert(interest).first;
Junxiao Shic041ca32014-02-25 20:01:15 -070074
Junxiao Shid3c792f2014-01-30 00:46:13 -070075 // detect loop and record Nonce
76 bool isLoop = ! pitEntry->addNonce(interest.getNonce());
77 if (isLoop) {
78 // goto Interest loop pipeline
79 this->onInterestLoop(inFace, interest, pitEntry);
80 return;
81 }
Junxiao Shic041ca32014-02-25 20:01:15 -070082
Junxiao Shid3c792f2014-01-30 00:46:13 -070083 // cancel unsatisfy & straggler timer
84 this->cancelUnsatisfyAndStragglerTimer(pitEntry);
Junxiao Shic041ca32014-02-25 20:01:15 -070085
Junxiao Shif3c07812014-03-11 21:48:49 -070086 // is pending?
Junxiao Shid3c792f2014-01-30 00:46:13 -070087 const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
Junxiao Shie17349a2014-03-25 00:55:38 -070088 bool isPending = inRecords.begin() != inRecords.end();
Junxiao Shid3c792f2014-01-30 00:46:13 -070089 if (!isPending) {
90 // CS lookup
91 const Data* csMatch = m_cs.find(interest);
92 if (csMatch != 0) {
93 // XXX should we lookup PIT for other Interests that also match csMatch?
94
95 // goto outgoing Data pipeline
96 this->onOutgoingData(*csMatch, inFace);
97 return;
98 }
99 }
Junxiao Shic041ca32014-02-25 20:01:15 -0700100
Junxiao Shid3c792f2014-01-30 00:46:13 -0700101 // insert InRecord
Junxiao Shi9b27bd22014-02-26 20:29:58 -0700102 pitEntry->insertOrUpdateInRecord(inFace.shared_from_this(), interest);
Junxiao Shic041ca32014-02-25 20:01:15 -0700103
Junxiao Shid3c792f2014-01-30 00:46:13 -0700104 // FIB lookup
Junxiao Shi40631842014-03-01 13:52:37 -0700105 shared_ptr<fib::Entry> fibEntry = m_fib.findLongestPrefixMatch(*pitEntry);
Junxiao Shic041ca32014-02-25 20:01:15 -0700106
Junxiao Shid3c792f2014-01-30 00:46:13 -0700107 // dispatch to strategy
Junxiao Shif3c07812014-03-11 21:48:49 -0700108 this->dispatchToStrategy(pitEntry, bind(&Strategy::afterReceiveInterest, _1,
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700109 cref(inFace), cref(interest), fibEntry, pitEntry));
Junxiao Shid3c792f2014-01-30 00:46:13 -0700110}
111
112void
113Forwarder::onInterestLoop(Face& inFace, const Interest& interest,
114 shared_ptr<pit::Entry> pitEntry)
115{
Junxiao Shif3c07812014-03-11 21:48:49 -0700116 NFD_LOG_DEBUG("onInterestLoop face=" << inFace.getId() <<
117 " interest=" << interest.getName());
Junxiao Shi8c8d2182014-01-30 22:33:00 -0700118
Junxiao Shif3c07812014-03-11 21:48:49 -0700119 // (drop)
120}
121
122/** \brief compare two InRecords for picking outgoing Interest
123 * \return true if b is preferred over a
124 *
125 * This function should be passed to std::max_element over InRecordCollection.
126 * The outgoing Interest picked is the last incoming Interest
127 * that does not come from outFace.
128 * If all InRecords come from outFace, it's fine to pick that. This happens when
129 * there's only one InRecord that comes from outFace. The legit use is for
130 * vehicular network; otherwise, strategy shouldn't send to the sole inFace.
131 */
132static inline bool
133compare_pickInterest(const pit::InRecord& a, const pit::InRecord& b, const Face* outFace)
134{
135 bool isOutFaceA = a.getFace().get() == outFace;
136 bool isOutFaceB = b.getFace().get() == outFace;
137
138 if (!isOutFaceA && isOutFaceB) {
139 return false;
140 }
141 if (isOutFaceA && !isOutFaceB) {
142 return true;
143 }
144
145 return a.getLastRenewed() > b.getLastRenewed();
Junxiao Shid3c792f2014-01-30 00:46:13 -0700146}
147
148void
Junxiao Shid938a6b2014-05-11 23:40:29 -0700149Forwarder::onOutgoingInterest(shared_ptr<pit::Entry> pitEntry, Face& outFace,
150 bool wantNewNonce)
Junxiao Shid3c792f2014-01-30 00:46:13 -0700151{
Junxiao Shif3c07812014-03-11 21:48:49 -0700152 NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
153 " interest=" << pitEntry->getName());
Junxiao Shi8c8d2182014-01-30 22:33:00 -0700154
Junxiao Shi57f0f312014-03-16 11:52:20 -0700155 // scope control
156 if (pitEntry->violatesScope(outFace)) {
Junxiao Shif3c07812014-03-11 21:48:49 -0700157 NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
Junxiao Shi57f0f312014-03-16 11:52:20 -0700158 " interest=" << pitEntry->getName() << " violates scope");
Junxiao Shi11bd9c22014-03-13 20:44:13 -0700159 return;
160 }
161
Junxiao Shid3c792f2014-01-30 00:46:13 -0700162 // pick Interest
Junxiao Shif3c07812014-03-11 21:48:49 -0700163 const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
164 pit::InRecordCollection::const_iterator pickedInRecord = std::max_element(
165 inRecords.begin(), inRecords.end(), bind(&compare_pickInterest, _1, _2, &outFace));
166 BOOST_ASSERT(pickedInRecord != inRecords.end());
Junxiao Shid938a6b2014-05-11 23:40:29 -0700167 shared_ptr<Interest> interest = const_pointer_cast<Interest>(
168 pickedInRecord->getInterest().shared_from_this());
169
170 if (wantNewNonce) {
171 interest = make_shared<Interest>(*interest);
172 interest->setNonce(ndn::random::generateWord32());
173 }
Junxiao Shic041ca32014-02-25 20:01:15 -0700174
Junxiao Shid3c792f2014-01-30 00:46:13 -0700175 // insert OutRecord
Junxiao Shid938a6b2014-05-11 23:40:29 -0700176 pitEntry->insertOrUpdateOutRecord(outFace.shared_from_this(), *interest);
Junxiao Shic041ca32014-02-25 20:01:15 -0700177
Junxiao Shid3c792f2014-01-30 00:46:13 -0700178 // set PIT unsatisfy timer
179 this->setUnsatisfyTimer(pitEntry);
Junxiao Shic041ca32014-02-25 20:01:15 -0700180
Junxiao Shid3c792f2014-01-30 00:46:13 -0700181 // send Interest
Junxiao Shid938a6b2014-05-11 23:40:29 -0700182 outFace.sendInterest(*interest);
Junxiao Shi6e694322014-04-03 10:27:13 -0700183 m_counters.getNOutInterests() ++;
Junxiao Shid3c792f2014-01-30 00:46:13 -0700184}
185
186void
Junxiao Shi09498f02014-02-26 19:41:08 -0700187Forwarder::onInterestReject(shared_ptr<pit::Entry> pitEntry)
Junxiao Shid3c792f2014-01-30 00:46:13 -0700188{
Junxiao Shid938a6b2014-05-11 23:40:29 -0700189 if (pitEntry->hasUnexpiredOutRecords()) {
190 NFD_LOG_ERROR("onInterestReject interest=" << pitEntry->getName() <<
191 " cannot reject forwarded Interest");
192 return;
193 }
Junxiao Shi09498f02014-02-26 19:41:08 -0700194 NFD_LOG_DEBUG("onInterestReject interest=" << pitEntry->getName());
Junxiao Shi8c8d2182014-01-30 22:33:00 -0700195
Junxiao Shid3c792f2014-01-30 00:46:13 -0700196 // set PIT straggler timer
197 this->setStragglerTimer(pitEntry);
198}
199
200void
201Forwarder::onInterestUnsatisfied(shared_ptr<pit::Entry> pitEntry)
202{
Junxiao Shi8c8d2182014-01-30 22:33:00 -0700203 NFD_LOG_DEBUG("onInterestUnsatisfied interest=" << pitEntry->getName());
204
Junxiao Shid3c792f2014-01-30 00:46:13 -0700205 // invoke PIT unsatisfied callback
Junxiao Shif3c07812014-03-11 21:48:49 -0700206 this->dispatchToStrategy(pitEntry, bind(&Strategy::beforeExpirePendingInterest, _1,
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700207 pitEntry));
Junxiao Shic041ca32014-02-25 20:01:15 -0700208
Junxiao Shif3c07812014-03-11 21:48:49 -0700209 // PIT delete
Junxiao Shid938a6b2014-05-11 23:40:29 -0700210 this->cancelUnsatisfyAndStragglerTimer(pitEntry);
Haowei Yuan78c84d12014-02-27 15:35:13 -0600211 m_pit.erase(pitEntry);
Junxiao Shid3c792f2014-01-30 00:46:13 -0700212}
213
214void
215Forwarder::onIncomingData(Face& inFace, const Data& data)
216{
217 // receive Data
Junxiao Shi8c8d2182014-01-30 22:33:00 -0700218 NFD_LOG_DEBUG("onIncomingData face=" << inFace.getId() << " data=" << data.getName());
Junxiao Shi06887ac2014-02-13 20:15:42 -0700219 const_cast<Data&>(data).setIncomingFaceId(inFace.getId());
Junxiao Shi6e694322014-04-03 10:27:13 -0700220 m_counters.getNInDatas() ++;
Junxiao Shic041ca32014-02-25 20:01:15 -0700221
Junxiao Shi88884492014-02-15 15:57:43 -0700222 // /localhost scope control
Junxiao Shif3c07812014-03-11 21:48:49 -0700223 bool isViolatingLocalhost = !inFace.isLocal() &&
224 LOCALHOST_NAME.isPrefixOf(data.getName());
225 if (isViolatingLocalhost) {
226 NFD_LOG_DEBUG("onIncomingData face=" << inFace.getId() <<
227 " data=" << data.getName() << " violates /localhost");
228 // (drop)
Junxiao Shi88884492014-02-15 15:57:43 -0700229 return;
230 }
Junxiao Shic041ca32014-02-25 20:01:15 -0700231
Junxiao Shid3c792f2014-01-30 00:46:13 -0700232 // PIT match
233 shared_ptr<pit::DataMatchResult> pitMatches = m_pit.findAllDataMatches(data);
234 if (pitMatches->begin() == pitMatches->end()) {
235 // goto Data unsolicited pipeline
236 this->onDataUnsolicited(inFace, data);
237 return;
238 }
Junxiao Shic041ca32014-02-25 20:01:15 -0700239
Junxiao Shid3c792f2014-01-30 00:46:13 -0700240 // CS insert
241 m_cs.insert(data);
Junxiao Shic041ca32014-02-25 20:01:15 -0700242
Junxiao Shid3c792f2014-01-30 00:46:13 -0700243 std::set<shared_ptr<Face> > pendingDownstreams;
244 // foreach PitEntry
245 for (pit::DataMatchResult::iterator it = pitMatches->begin();
246 it != pitMatches->end(); ++it) {
247 shared_ptr<pit::Entry> pitEntry = *it;
Junxiao Shi8c8d2182014-01-30 22:33:00 -0700248 NFD_LOG_DEBUG("onIncomingData matching=" << pitEntry->getName());
Junxiao Shic041ca32014-02-25 20:01:15 -0700249
Junxiao Shid3c792f2014-01-30 00:46:13 -0700250 // cancel unsatisfy & straggler timer
251 this->cancelUnsatisfyAndStragglerTimer(pitEntry);
Junxiao Shic041ca32014-02-25 20:01:15 -0700252
Junxiao Shid3c792f2014-01-30 00:46:13 -0700253 // remember pending downstreams
254 const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
255 for (pit::InRecordCollection::const_iterator it = inRecords.begin();
256 it != inRecords.end(); ++it) {
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700257 if (it->getExpiry() > time::steady_clock::now()) {
Junxiao Shid3c792f2014-01-30 00:46:13 -0700258 pendingDownstreams.insert(it->getFace());
259 }
260 }
Junxiao Shic041ca32014-02-25 20:01:15 -0700261
Junxiao Shid938a6b2014-05-11 23:40:29 -0700262 // invoke PIT satisfy callback
263 this->dispatchToStrategy(pitEntry, bind(&Strategy::beforeSatisfyPendingInterest, _1,
264 pitEntry, cref(inFace), cref(data)));
265
Junxiao Shid3c792f2014-01-30 00:46:13 -0700266 // mark PIT satisfied
267 pitEntry->deleteInRecords();
268 pitEntry->deleteOutRecord(inFace.shared_from_this());
Junxiao Shic041ca32014-02-25 20:01:15 -0700269
Junxiao Shid3c792f2014-01-30 00:46:13 -0700270 // set PIT straggler timer
271 this->setStragglerTimer(pitEntry);
Junxiao Shid3c792f2014-01-30 00:46:13 -0700272 }
Junxiao Shic041ca32014-02-25 20:01:15 -0700273
Junxiao Shid3c792f2014-01-30 00:46:13 -0700274 // foreach pending downstream
275 for (std::set<shared_ptr<Face> >::iterator it = pendingDownstreams.begin();
276 it != pendingDownstreams.end(); ++it) {
Junxiao Shida006f52014-05-16 11:18:00 -0700277 shared_ptr<Face> pendingDownstream = *it;
278 if (pendingDownstream.get() == &inFace) {
279 continue;
280 }
Junxiao Shid3c792f2014-01-30 00:46:13 -0700281 // goto outgoing Data pipeline
Junxiao Shida006f52014-05-16 11:18:00 -0700282 this->onOutgoingData(data, *pendingDownstream);
Junxiao Shid3c792f2014-01-30 00:46:13 -0700283 }
284}
285
286void
287Forwarder::onDataUnsolicited(Face& inFace, const Data& data)
288{
289 // accept to cache?
Junxiao Shif3c07812014-03-11 21:48:49 -0700290 bool acceptToCache = inFace.isLocal();
Junxiao Shid3c792f2014-01-30 00:46:13 -0700291 if (acceptToCache) {
292 // CS insert
Junxiao Shif3c07812014-03-11 21:48:49 -0700293 m_cs.insert(data, true);
Junxiao Shid3c792f2014-01-30 00:46:13 -0700294 }
Junxiao Shi8c8d2182014-01-30 22:33:00 -0700295
Junxiao Shif3c07812014-03-11 21:48:49 -0700296 NFD_LOG_DEBUG("onDataUnsolicited face=" << inFace.getId() <<
297 " data=" << data.getName() <<
298 (acceptToCache ? " cached" : " not cached"));
Junxiao Shid3c792f2014-01-30 00:46:13 -0700299}
300
301void
302Forwarder::onOutgoingData(const Data& data, Face& outFace)
303{
Junxiao Shi8c8d2182014-01-30 22:33:00 -0700304 NFD_LOG_DEBUG("onOutgoingData face=" << outFace.getId() << " data=" << data.getName());
305
Junxiao Shi9b27bd22014-02-26 20:29:58 -0700306 // /localhost scope control
Junxiao Shif3c07812014-03-11 21:48:49 -0700307 bool isViolatingLocalhost = !outFace.isLocal() &&
308 LOCALHOST_NAME.isPrefixOf(data.getName());
309 if (isViolatingLocalhost) {
310 NFD_LOG_DEBUG("onOutgoingData face=" << outFace.getId() <<
311 " data=" << data.getName() << " violates /localhost");
312 // (drop)
Junxiao Shi9b27bd22014-02-26 20:29:58 -0700313 return;
314 }
315
Junxiao Shif3c07812014-03-11 21:48:49 -0700316 // TODO traffic manager
Junxiao Shic041ca32014-02-25 20:01:15 -0700317
Junxiao Shid3c792f2014-01-30 00:46:13 -0700318 // send Data
319 outFace.sendData(data);
Junxiao Shi6e694322014-04-03 10:27:13 -0700320 m_counters.getNOutDatas() ++;
Junxiao Shid3c792f2014-01-30 00:46:13 -0700321}
322
323static inline bool
324compare_InRecord_expiry(const pit::InRecord& a, const pit::InRecord& b)
325{
326 return a.getExpiry() < b.getExpiry();
327}
328
329void
330Forwarder::setUnsatisfyTimer(shared_ptr<pit::Entry> pitEntry)
331{
332 const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
333 pit::InRecordCollection::const_iterator lastExpiring =
334 std::max_element(inRecords.begin(), inRecords.end(),
335 &compare_InRecord_expiry);
336
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700337 time::steady_clock::TimePoint lastExpiry = lastExpiring->getExpiry();
338 time::nanoseconds lastExpiryFromNow = lastExpiry - time::steady_clock::now();
Junxiao Shid3c792f2014-01-30 00:46:13 -0700339 if (lastExpiryFromNow <= time::seconds(0)) {
340 // TODO all InRecords are already expired; will this happen?
341 }
Junxiao Shic041ca32014-02-25 20:01:15 -0700342
Junxiao Shi9f7455b2014-04-07 21:02:16 -0700343 scheduler::cancel(pitEntry->m_unsatisfyTimer);
Junxiao Shic041ca32014-02-25 20:01:15 -0700344 pitEntry->m_unsatisfyTimer = scheduler::schedule(lastExpiryFromNow,
Junxiao Shid3c792f2014-01-30 00:46:13 -0700345 bind(&Forwarder::onInterestUnsatisfied, this, pitEntry));
346}
347
348void
349Forwarder::setStragglerTimer(shared_ptr<pit::Entry> pitEntry)
350{
Junxiao Shi57f0f312014-03-16 11:52:20 -0700351 if (pitEntry->hasUnexpiredOutRecords()) {
352 NFD_LOG_DEBUG("setStragglerTimer " << pitEntry->getName() <<
353 " cannot set StragglerTimer when an OutRecord is pending");
354 return;
355 }
356
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700357 time::nanoseconds stragglerTime = time::milliseconds(100);
Junxiao Shic041ca32014-02-25 20:01:15 -0700358
Junxiao Shi9f7455b2014-04-07 21:02:16 -0700359 scheduler::cancel(pitEntry->m_stragglerTimer);
Junxiao Shic041ca32014-02-25 20:01:15 -0700360 pitEntry->m_stragglerTimer = scheduler::schedule(stragglerTime,
Haowei Yuan78c84d12014-02-27 15:35:13 -0600361 bind(&Pit::erase, &m_pit, pitEntry));
Junxiao Shid3c792f2014-01-30 00:46:13 -0700362}
363
364void
365Forwarder::cancelUnsatisfyAndStragglerTimer(shared_ptr<pit::Entry> pitEntry)
366{
Junxiao Shic041ca32014-02-25 20:01:15 -0700367 scheduler::cancel(pitEntry->m_unsatisfyTimer);
368 scheduler::cancel(pitEntry->m_stragglerTimer);
Junxiao Shid3c792f2014-01-30 00:46:13 -0700369}
370
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800371} // namespace nfd