blob: a81e77a519ed3419cdd334958d1f2de1ee758b62 [file] [log] [blame]
Alexander Afanasyev3ecec502014-04-16 13:42:44 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesaventoa3148082018-04-12 18:21:54 -04002/*
Davide Pesavento412c9822021-07-02 00:21:05 -04003 * Copyright (c) 2014-2021, Regents of the University of California,
Alexander Afanasyev7c10b3b2015-01-20 12:24:27 -08004 * 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.
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070010 *
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/>.
Vince12e49462014-06-09 13:29:32 -050024 */
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070025
26#include "rib.hpp"
Vince Lehman76c751c2014-11-18 17:36:38 -060027#include "fib-updater.hpp"
Davide Pesavento2cae8ca2019-04-18 20:48:05 -040028#include "common/logger.hpp"
Vince Lehman281ded72014-08-21 12:17:08 -050029
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070030namespace nfd {
31namespace rib {
32
Davide Pesaventoa3148082018-04-12 18:21:54 -040033NFD_LOG_INIT(Rib);
34
Junxiao Shi89c0ea02017-03-06 19:52:05 +000035bool
36operator<(const RibRouteRef& lhs, const RibRouteRef& rhs)
37{
38 return std::tie(lhs.entry->getName(), lhs.route->faceId, lhs.route->origin) <
39 std::tie(rhs.entry->getName(), rhs.route->faceId, rhs.route->origin);
40}
41
Vince Lehman4387e782014-06-19 16:57:45 -050042static inline bool
Vince Lehman218be0a2015-01-15 17:25:20 -060043sortRoutes(const Route& lhs, const Route& rhs)
Vince Lehman4387e782014-06-19 16:57:45 -050044{
Vince Lehman218be0a2015-01-15 17:25:20 -060045 return lhs.faceId < rhs.faceId;
Vince Lehman4387e782014-06-19 16:57:45 -050046}
47
Vince Lehman76c751c2014-11-18 17:36:38 -060048void
49Rib::setFibUpdater(FibUpdater* updater)
50{
51 m_fibUpdater = updater;
52}
53
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070054Rib::const_iterator
Vince12e49462014-06-09 13:29:32 -050055Rib::find(const Name& prefix) const
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070056{
Vince12e49462014-06-09 13:29:32 -050057 return m_rib.find(prefix);
58}
59
Vince Lehman218be0a2015-01-15 17:25:20 -060060Route*
61Rib::find(const Name& prefix, const Route& route) const
Vince12e49462014-06-09 13:29:32 -050062{
Davide Pesaventoe4b22382018-06-10 14:37:24 -040063 auto ribIt = m_rib.find(prefix);
Vince12e49462014-06-09 13:29:32 -050064
65 // Name prefix exists
Vince Lehman76c751c2014-11-18 17:36:38 -060066 if (ribIt != m_rib.end()) {
67 shared_ptr<RibEntry> entry = ribIt->second;
Davide Pesaventoe4b22382018-06-10 14:37:24 -040068 auto routeIt = entry->findRoute(route);
Vince Lehman76c751c2014-11-18 17:36:38 -060069 if (routeIt != entry->end()) {
Davide Pesaventoe4b22382018-06-10 14:37:24 -040070 return &*routeIt;
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070071 }
Vince Lehman76c751c2014-11-18 17:36:38 -060072 }
Vince Lehman218be0a2015-01-15 17:25:20 -060073
74 return nullptr;
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070075}
76
Teng Lianga4e6ec32018-10-21 09:25:00 -070077Route*
78Rib::findLongestPrefix(const Name& prefix, const Route& route) const
79{
80 Route* existingRoute = find(prefix, route);
81 if (existingRoute == nullptr) {
82 auto parent = findParent(prefix);
83 if (parent) {
84 existingRoute = find(parent->getName(), route);
85 }
86 }
87
88 return existingRoute;
89}
90
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070091void
Vince Lehman218be0a2015-01-15 17:25:20 -060092Rib::insert(const Name& prefix, const Route& route)
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070093{
Davide Pesaventoe4b22382018-06-10 14:37:24 -040094 auto ribIt = m_rib.find(prefix);
Vince12e49462014-06-09 13:29:32 -050095
96 // Name prefix exists
Vince Lehman76c751c2014-11-18 17:36:38 -060097 if (ribIt != m_rib.end()) {
98 shared_ptr<RibEntry> entry(ribIt->second);
Vince12e49462014-06-09 13:29:32 -050099
Nick Gordon89c4cca2016-11-02 15:42:32 +0000100 RibEntry::iterator entryIt;
101 bool didInsert = false;
102 std::tie(entryIt, didInsert) = entry->insertRoute(route);
Vince12e49462014-06-09 13:29:32 -0500103
Nick Gordon89c4cca2016-11-02 15:42:32 +0000104 if (didInsert) {
105 // The route was new and we successfully inserted it.
Vince Lehman76c751c2014-11-18 17:36:38 -0600106 m_nItems++;
Vince12e49462014-06-09 13:29:32 -0500107
Nick Gordon89c4cca2016-11-02 15:42:32 +0000108 afterAddRoute(RibRouteRef{entry, entryIt});
109
Vince12e49462014-06-09 13:29:32 -0500110 // Register with face lookup table
Junxiao Shi17a70012019-06-25 10:50:32 +0000111 m_faceEntries.emplace(route.faceId, entry);
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700112 }
Vince Lehman76c751c2014-11-18 17:36:38 -0600113 else {
114 // Route exists, update fields
115 // First cancel old scheduled event, if any, then set the EventId to new one
Davide Pesaventoe1bdc082018-10-11 21:20:23 -0400116 if (entryIt->getExpirationEvent()) {
117 NFD_LOG_TRACE("Cancelling expiration event for " << entry->getName() << " " << *entryIt);
118 entryIt->cancelExpirationEvent();
Vince Lehman76c751c2014-11-18 17:36:38 -0600119 }
120
Junxiao Shid47cd632018-09-11 03:10:00 +0000121 *entryIt = route;
Vince Lehman76c751c2014-11-18 17:36:38 -0600122 }
123 }
124 else {
125 // New name prefix
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400126 auto entry = make_shared<RibEntry>();
Vince Lehman76c751c2014-11-18 17:36:38 -0600127
128 m_rib[prefix] = entry;
129 m_nItems++;
130
131 entry->setName(prefix);
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400132 auto routeIt = entry->insertRoute(route).first;
Vince Lehman76c751c2014-11-18 17:36:38 -0600133
134 // Find prefix's parent
135 shared_ptr<RibEntry> parent = findParent(prefix);
136
137 // Add self to parent's children
138 if (parent != nullptr) {
139 parent->addChild(entry);
140 }
141
142 RibEntryList children = findDescendants(prefix);
143
144 for (const auto& child : children) {
145 if (child->getParent() == parent) {
146 // Remove child from parent and inherit parent's child
147 if (parent != nullptr) {
148 parent->removeChild(child);
149 }
150
151 entry->addChild(child);
152 }
153 }
154
155 // Register with face lookup table
Junxiao Shi17a70012019-06-25 10:50:32 +0000156 m_faceEntries.emplace(route.faceId, entry);
Vince Lehman76c751c2014-11-18 17:36:38 -0600157
158 // do something after inserting an entry
159 afterInsertEntry(prefix);
Nick Gordon89c4cca2016-11-02 15:42:32 +0000160 afterAddRoute(RibRouteRef{entry, routeIt});
Vince Lehman76c751c2014-11-18 17:36:38 -0600161 }
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700162}
163
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700164void
Vince Lehman218be0a2015-01-15 17:25:20 -0600165Rib::erase(const Name& prefix, const Route& route)
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700166{
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400167 auto ribIt = m_rib.find(prefix);
Junxiao Shi17a70012019-06-25 10:50:32 +0000168 if (ribIt == m_rib.end()) {
169 // Name prefix does not exist
170 return;
171 }
Vince12e49462014-06-09 13:29:32 -0500172
Junxiao Shi17a70012019-06-25 10:50:32 +0000173 shared_ptr<RibEntry> entry = ribIt->second;
174 auto routeIt = entry->findRoute(route);
Vince Lehman4387e782014-06-19 16:57:45 -0500175
Junxiao Shi17a70012019-06-25 10:50:32 +0000176 if (routeIt != entry->end()) {
177 beforeRemoveRoute(RibRouteRef{entry, routeIt});
Nick Gordon89c4cca2016-11-02 15:42:32 +0000178
Junxiao Shi17a70012019-06-25 10:50:32 +0000179 auto faceId = route.faceId;
180 entry->eraseRoute(routeIt);
181 m_nItems--;
Vince Lehman4387e782014-06-19 16:57:45 -0500182
Junxiao Shi17a70012019-06-25 10:50:32 +0000183 // If this RibEntry no longer has this faceId, unregister from face lookup table
184 if (!entry->hasFaceId(faceId)) {
185 auto range = m_faceEntries.equal_range(faceId);
186 for (auto it = range.first; it != range.second; ++it) {
187 if (it->second == entry) {
188 m_faceEntries.erase(it);
189 break;
190 }
Vince Lehman76c751c2014-11-18 17:36:38 -0600191 }
Junxiao Shi17a70012019-06-25 10:50:32 +0000192 }
Syed Obaid3313a372014-07-01 01:31:33 -0500193
Junxiao Shi17a70012019-06-25 10:50:32 +0000194 // If a RibEntry's route list is empty, remove it from the tree
195 if (entry->getRoutes().empty()) {
196 eraseEntry(ribIt);
Vince12e49462014-06-09 13:29:32 -0500197 }
Vince Lehman76c751c2014-11-18 17:36:38 -0600198 }
Vince12e49462014-06-09 13:29:32 -0500199}
200
201void
Vince Lehman76c751c2014-11-18 17:36:38 -0600202Rib::onRouteExpiration(const Name& prefix, const Route& route)
Vince12e49462014-06-09 13:29:32 -0500203{
Vince Lehman76c751c2014-11-18 17:36:38 -0600204 NFD_LOG_DEBUG(route << " for " << prefix << " has expired");
Vince12e49462014-06-09 13:29:32 -0500205
Vince Lehman76c751c2014-11-18 17:36:38 -0600206 RibUpdate update;
207 update.setAction(RibUpdate::UNREGISTER)
208 .setName(prefix)
209 .setRoute(route);
Vince12e49462014-06-09 13:29:32 -0500210
Vince Lehman76c751c2014-11-18 17:36:38 -0600211 beginApplyUpdate(update, nullptr, nullptr);
Vince12e49462014-06-09 13:29:32 -0500212}
213
214shared_ptr<RibEntry>
215Rib::findParent(const Name& prefix) const
216{
Vince Lehman76c751c2014-11-18 17:36:38 -0600217 for (int i = prefix.size() - 1; i >= 0; i--) {
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400218 auto it = m_rib.find(prefix.getPrefix(i));
Vince Lehman76c751c2014-11-18 17:36:38 -0600219 if (it != m_rib.end()) {
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400220 return it->second;
Vince12e49462014-06-09 13:29:32 -0500221 }
Vince Lehman76c751c2014-11-18 17:36:38 -0600222 }
Vince12e49462014-06-09 13:29:32 -0500223
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400224 return nullptr;
Vince12e49462014-06-09 13:29:32 -0500225}
226
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400227std::list<shared_ptr<RibEntry>>
Vince12e49462014-06-09 13:29:32 -0500228Rib::findDescendants(const Name& prefix) const
229{
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400230 std::list<shared_ptr<RibEntry>> children;
Vince12e49462014-06-09 13:29:32 -0500231
232 RibTable::const_iterator it = m_rib.find(prefix);
Vince Lehman76c751c2014-11-18 17:36:38 -0600233 if (it != m_rib.end()) {
234 ++it;
235 for (; it != m_rib.end(); ++it) {
236 if (prefix.isPrefixOf(it->first)) {
237 children.push_back((it->second));
238 }
239 else {
240 break;
241 }
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700242 }
Vince Lehman76c751c2014-11-18 17:36:38 -0600243 }
244
245 return children;
246}
247
248std::list<shared_ptr<RibEntry>>
249Rib::findDescendantsForNonInsertedName(const Name& prefix) const
250{
251 std::list<shared_ptr<RibEntry>> children;
252
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400253 for (const auto& pair : m_rib) {
Vince Lehman76c751c2014-11-18 17:36:38 -0600254 if (prefix.isPrefixOf(pair.first)) {
255 children.push_back(pair.second);
256 }
257 }
Vince12e49462014-06-09 13:29:32 -0500258
259 return children;
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700260}
261
Vince12e49462014-06-09 13:29:32 -0500262Rib::RibTable::iterator
263Rib::eraseEntry(RibTable::iterator it)
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700264{
Vince12e49462014-06-09 13:29:32 -0500265 // Entry does not exist
Vince Lehman76c751c2014-11-18 17:36:38 -0600266 if (it == m_rib.end()) {
267 return m_rib.end();
268 }
Vince12e49462014-06-09 13:29:32 -0500269
270 shared_ptr<RibEntry> entry(it->second);
271
272 shared_ptr<RibEntry> parent = entry->getParent();
273
274 // Remove self from parent's children
Vince Lehman76c751c2014-11-18 17:36:38 -0600275 if (parent != nullptr) {
276 parent->removeChild(entry);
277 }
278
279 for (auto childIt = entry->getChildren().begin(); childIt != entry->getChildren().end(); ) {
280 shared_ptr<RibEntry> child = *childIt;
281
282 // Advance iterator so it is not invalidated by removal
283 ++childIt;
284
285 // Remove children from self
286 entry->removeChild(child);
287
288 // Update parent's children
289 if (parent != nullptr) {
290 parent->addChild(child);
Vince12e49462014-06-09 13:29:32 -0500291 }
Vince Lehman76c751c2014-11-18 17:36:38 -0600292 }
Vince12e49462014-06-09 13:29:32 -0500293
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400294 auto nextIt = m_rib.erase(it);
Vince12e49462014-06-09 13:29:32 -0500295
Yanbiao Lic17de832014-11-21 17:51:45 -0800296 // do something after erasing an entry.
297 afterEraseEntry(entry->getName());
298
Vince12e49462014-06-09 13:29:32 -0500299 return nextIt;
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700300}
301
Vince Lehman218be0a2015-01-15 17:25:20 -0600302Rib::RouteSet
303Rib::getAncestorRoutes(const RibEntry& entry) const
Vince Lehman4387e782014-06-19 16:57:45 -0500304{
Vince Lehman218be0a2015-01-15 17:25:20 -0600305 RouteSet ancestorRoutes(&sortRoutes);
Vince Lehman4387e782014-06-19 16:57:45 -0500306
307 shared_ptr<RibEntry> parent = entry.getParent();
308
Vince Lehman76c751c2014-11-18 17:36:38 -0600309 while (parent != nullptr) {
310 for (const Route& route : parent->getRoutes()) {
311 if (route.isChildInherit()) {
312 ancestorRoutes.insert(route);
Vince Lehman4387e782014-06-19 16:57:45 -0500313 }
Vince Lehman4387e782014-06-19 16:57:45 -0500314 }
315
Vince Lehman76c751c2014-11-18 17:36:38 -0600316 if (parent->hasCapture()) {
317 break;
Vince Lehman4387e782014-06-19 16:57:45 -0500318 }
Vince Lehman76c751c2014-11-18 17:36:38 -0600319
320 parent = parent->getParent();
321 }
322
323 return ancestorRoutes;
324}
325
326Rib::RouteSet
327Rib::getAncestorRoutes(const Name& name) const
328{
329 RouteSet ancestorRoutes(&sortRoutes);
330
331 shared_ptr<RibEntry> parent = findParent(name);
332
333 while (parent != nullptr) {
334 for (const Route& route : parent->getRoutes()) {
335 if (route.isChildInherit()) {
336 ancestorRoutes.insert(route);
337 }
338 }
339
340 if (parent->hasCapture()) {
341 break;
342 }
343
344 parent = parent->getParent();
345 }
346
347 return ancestorRoutes;
348}
349
350void
351Rib::beginApplyUpdate(const RibUpdate& update,
352 const Rib::UpdateSuccessCallback& onSuccess,
353 const Rib::UpdateFailureCallback& onFailure)
354{
355 BOOST_ASSERT(m_fibUpdater != nullptr);
356
357 addUpdateToQueue(update, onSuccess, onFailure);
358
359 sendBatchFromQueue();
360}
361
362void
363Rib::beginRemoveFace(uint64_t faceId)
364{
Junxiao Shi17a70012019-06-25 10:50:32 +0000365 auto range = m_faceEntries.equal_range(faceId);
366 for (auto it = range.first; it != range.second; ++it) {
367 enqueueRemoveFace(*it->second, faceId);
368 }
369 sendBatchFromQueue();
370}
371
372void
373Rib::beginRemoveFailedFaces(const std::set<uint64_t>& activeFaceIds)
374{
375 for (auto it = m_faceEntries.begin(); it != m_faceEntries.end(); ++it) {
376 if (activeFaceIds.count(it->first) > 0) {
377 continue;
378 }
379 enqueueRemoveFace(*it->second, it->first);
380 }
381 sendBatchFromQueue();
382}
383
384void
385Rib::enqueueRemoveFace(const RibEntry& entry, uint64_t faceId)
386{
387 for (const Route& route : entry) {
388 if (route.faceId != faceId) {
389 continue;
390 }
391
Vince Lehman76c751c2014-11-18 17:36:38 -0600392 RibUpdate update;
393 update.setAction(RibUpdate::REMOVE_FACE)
Junxiao Shi17a70012019-06-25 10:50:32 +0000394 .setName(entry.getName())
395 .setRoute(route);
Vince Lehman76c751c2014-11-18 17:36:38 -0600396 addUpdateToQueue(update, nullptr, nullptr);
397 }
Vince Lehman76c751c2014-11-18 17:36:38 -0600398}
399
400void
401Rib::addUpdateToQueue(const RibUpdate& update,
402 const Rib::UpdateSuccessCallback& onSuccess,
403 const Rib::UpdateFailureCallback& onFailure)
404{
405 RibUpdateBatch batch(update.getRoute().faceId);
406 batch.add(update);
407
408 UpdateQueueItem item{batch, onSuccess, onFailure};
409 m_updateBatches.push_back(std::move(item));
410}
411
412void
413Rib::sendBatchFromQueue()
414{
415 if (m_updateBatches.empty() || m_isUpdateInProgress) {
416 return;
417 }
418
419 m_isUpdateInProgress = true;
420
421 UpdateQueueItem item = std::move(m_updateBatches.front());
422 m_updateBatches.pop_front();
423
424 RibUpdateBatch& batch = item.batch;
425
426 // Until task #1698, each RibUpdateBatch contains exactly one RIB update
427 BOOST_ASSERT(batch.size() == 1);
428
Davide Pesavento412c9822021-07-02 00:21:05 -0400429 auto fibSuccessCb = std::bind(&Rib::onFibUpdateSuccess, this, batch, _1, item.managerSuccessCallback);
430 auto fibFailureCb = std::bind(&Rib::onFibUpdateFailure, this, item.managerFailureCallback, _1, _2);
Vince Lehman76c751c2014-11-18 17:36:38 -0600431
Junxiao Shi52009042018-09-10 12:33:56 +0000432 m_fibUpdater->computeAndSendFibUpdates(batch, fibSuccessCb, fibFailureCb);
Vince Lehman76c751c2014-11-18 17:36:38 -0600433}
434
435void
436Rib::onFibUpdateSuccess(const RibUpdateBatch& batch,
437 const RibUpdateList& inheritedRoutes,
438 const Rib::UpdateSuccessCallback& onSuccess)
439{
440 for (const RibUpdate& update : batch) {
441 switch (update.getAction()) {
442 case RibUpdate::REGISTER:
443 insert(update.getName(), update.getRoute());
444 break;
445 case RibUpdate::UNREGISTER:
446 case RibUpdate::REMOVE_FACE:
447 erase(update.getName(), update.getRoute());
448 break;
449 }
450 }
451
452 // Add and remove precalculated inherited routes to RibEntries
453 modifyInheritedRoutes(inheritedRoutes);
454
455 m_isUpdateInProgress = false;
456
457 if (onSuccess != nullptr) {
458 onSuccess();
459 }
460
461 // Try to advance the batch queue
462 sendBatchFromQueue();
463}
464
465void
466Rib::onFibUpdateFailure(const Rib::UpdateFailureCallback& onFailure,
467 uint32_t code, const std::string& error)
468{
469 m_isUpdateInProgress = false;
470
471 if (onFailure != nullptr) {
472 onFailure(code, error);
473 }
474
475 // Try to advance the batch queue
476 sendBatchFromQueue();
477}
478
479void
480Rib::modifyInheritedRoutes(const RibUpdateList& inheritedRoutes)
481{
482 for (const RibUpdate& update : inheritedRoutes) {
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400483 auto ribIt = m_rib.find(update.getName());
Vince Lehman76c751c2014-11-18 17:36:38 -0600484 BOOST_ASSERT(ribIt != m_rib.end());
485 shared_ptr<RibEntry> entry(ribIt->second);
486
487 switch (update.getAction()) {
488 case RibUpdate::REGISTER:
489 entry->addInheritedRoute(update.getRoute());
490 break;
491 case RibUpdate::UNREGISTER:
492 entry->removeInheritedRoute(update.getRoute());
493 break;
494 case RibUpdate::REMOVE_FACE:
495 break;
496 }
497 }
498}
499
Alexander Afanasyev20d31442014-04-19 17:00:53 -0700500std::ostream&
Vince12e49462014-06-09 13:29:32 -0500501operator<<(std::ostream& os, const Rib& rib)
Alexander Afanasyev20d31442014-04-19 17:00:53 -0700502{
Vince Lehman76c751c2014-11-18 17:36:38 -0600503 for (const auto& item : rib) {
Weiwei Liuaaa58a62016-11-28 23:15:15 -0700504 os << *item.second << "\n";
Vince Lehman76c751c2014-11-18 17:36:38 -0600505 }
Alexander Afanasyev20d31442014-04-19 17:00:53 -0700506
507 return os;
508}
509
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700510} // namespace rib
511} // namespace nfd