blob: d209f6397dcdc7fdb3e1ab9592fcb28793661d71 [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/*
3 * Copyright (c) 2014-2018, 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"
Vince Lehman281ded72014-08-21 12:17:08 -050028#include "core/logger.hpp"
29
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
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070048Rib::Rib()
Vince12e49462014-06-09 13:29:32 -050049 : m_nItems(0)
Vince Lehman76c751c2014-11-18 17:36:38 -060050 , m_isUpdateInProgress(false)
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070051{
52}
53
Vince Lehman76c751c2014-11-18 17:36:38 -060054void
55Rib::setFibUpdater(FibUpdater* updater)
56{
57 m_fibUpdater = updater;
58}
59
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070060Rib::const_iterator
Vince12e49462014-06-09 13:29:32 -050061Rib::find(const Name& prefix) const
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070062{
Vince12e49462014-06-09 13:29:32 -050063 return m_rib.find(prefix);
64}
65
Vince Lehman218be0a2015-01-15 17:25:20 -060066Route*
67Rib::find(const Name& prefix, const Route& route) const
Vince12e49462014-06-09 13:29:32 -050068{
Davide Pesaventoe4b22382018-06-10 14:37:24 -040069 auto ribIt = m_rib.find(prefix);
Vince12e49462014-06-09 13:29:32 -050070
71 // Name prefix exists
Vince Lehman76c751c2014-11-18 17:36:38 -060072 if (ribIt != m_rib.end()) {
73 shared_ptr<RibEntry> entry = ribIt->second;
Davide Pesaventoe4b22382018-06-10 14:37:24 -040074 auto routeIt = entry->findRoute(route);
Vince Lehman76c751c2014-11-18 17:36:38 -060075 if (routeIt != entry->end()) {
Davide Pesaventoe4b22382018-06-10 14:37:24 -040076 return &*routeIt;
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070077 }
Vince Lehman76c751c2014-11-18 17:36:38 -060078 }
Vince Lehman218be0a2015-01-15 17:25:20 -060079
80 return nullptr;
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070081}
82
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070083void
Vince Lehman218be0a2015-01-15 17:25:20 -060084Rib::insert(const Name& prefix, const Route& route)
Alexander Afanasyev3ecec502014-04-16 13:42:44 -070085{
Davide Pesaventoe4b22382018-06-10 14:37:24 -040086 auto ribIt = m_rib.find(prefix);
Vince12e49462014-06-09 13:29:32 -050087
88 // Name prefix exists
Vince Lehman76c751c2014-11-18 17:36:38 -060089 if (ribIt != m_rib.end()) {
90 shared_ptr<RibEntry> entry(ribIt->second);
Vince12e49462014-06-09 13:29:32 -050091
Nick Gordon89c4cca2016-11-02 15:42:32 +000092 RibEntry::iterator entryIt;
93 bool didInsert = false;
94 std::tie(entryIt, didInsert) = entry->insertRoute(route);
Vince12e49462014-06-09 13:29:32 -050095
Nick Gordon89c4cca2016-11-02 15:42:32 +000096 if (didInsert) {
97 // The route was new and we successfully inserted it.
Vince Lehman76c751c2014-11-18 17:36:38 -060098 m_nItems++;
Vince12e49462014-06-09 13:29:32 -050099
Nick Gordon89c4cca2016-11-02 15:42:32 +0000100 afterAddRoute(RibRouteRef{entry, entryIt});
101
Vince12e49462014-06-09 13:29:32 -0500102 // Register with face lookup table
Vince Lehman218be0a2015-01-15 17:25:20 -0600103 m_faceMap[route.faceId].push_back(entry);
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700104 }
Vince Lehman76c751c2014-11-18 17:36:38 -0600105 else {
106 // Route exists, update fields
107 // First cancel old scheduled event, if any, then set the EventId to new one
Davide Pesaventoe1bdc082018-10-11 21:20:23 -0400108 if (entryIt->getExpirationEvent()) {
109 NFD_LOG_TRACE("Cancelling expiration event for " << entry->getName() << " " << *entryIt);
110 entryIt->cancelExpirationEvent();
Vince Lehman76c751c2014-11-18 17:36:38 -0600111 }
112
Junxiao Shid47cd632018-09-11 03:10:00 +0000113 *entryIt = route;
Vince Lehman76c751c2014-11-18 17:36:38 -0600114 }
115 }
116 else {
117 // New name prefix
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400118 auto entry = make_shared<RibEntry>();
Vince Lehman76c751c2014-11-18 17:36:38 -0600119
120 m_rib[prefix] = entry;
121 m_nItems++;
122
123 entry->setName(prefix);
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400124 auto routeIt = entry->insertRoute(route).first;
Vince Lehman76c751c2014-11-18 17:36:38 -0600125
126 // Find prefix's parent
127 shared_ptr<RibEntry> parent = findParent(prefix);
128
129 // Add self to parent's children
130 if (parent != nullptr) {
131 parent->addChild(entry);
132 }
133
134 RibEntryList children = findDescendants(prefix);
135
136 for (const auto& child : children) {
137 if (child->getParent() == parent) {
138 // Remove child from parent and inherit parent's child
139 if (parent != nullptr) {
140 parent->removeChild(child);
141 }
142
143 entry->addChild(child);
144 }
145 }
146
147 // Register with face lookup table
148 m_faceMap[route.faceId].push_back(entry);
149
150 // do something after inserting an entry
151 afterInsertEntry(prefix);
Nick Gordon89c4cca2016-11-02 15:42:32 +0000152 afterAddRoute(RibRouteRef{entry, routeIt});
Vince Lehman76c751c2014-11-18 17:36:38 -0600153 }
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700154}
155
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700156void
Vince Lehman218be0a2015-01-15 17:25:20 -0600157Rib::erase(const Name& prefix, const Route& route)
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700158{
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400159 auto ribIt = m_rib.find(prefix);
Vince12e49462014-06-09 13:29:32 -0500160
161 // Name prefix exists
Vince Lehman76c751c2014-11-18 17:36:38 -0600162 if (ribIt != m_rib.end()) {
Davide Pesaventoe94804b2016-09-19 17:23:21 +0000163 shared_ptr<RibEntry> entry = ribIt->second;
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400164 auto routeIt = entry->findRoute(route);
Vince Lehman4387e782014-06-19 16:57:45 -0500165
Vince Lehman76c751c2014-11-18 17:36:38 -0600166 if (routeIt != entry->end()) {
Nick Gordon89c4cca2016-11-02 15:42:32 +0000167 beforeRemoveRoute(RibRouteRef{entry, routeIt});
168
Davide Pesaventoe94804b2016-09-19 17:23:21 +0000169 auto faceId = route.faceId;
Vince Lehman76c751c2014-11-18 17:36:38 -0600170 entry->eraseRoute(routeIt);
171 m_nItems--;
Vince Lehman4387e782014-06-19 16:57:45 -0500172
Vince Lehman76c751c2014-11-18 17:36:38 -0600173 // If this RibEntry no longer has this faceId, unregister from face lookup table
Davide Pesaventoe94804b2016-09-19 17:23:21 +0000174 if (!entry->hasFaceId(faceId)) {
175 m_faceMap[faceId].remove(entry);
Vince Lehman76c751c2014-11-18 17:36:38 -0600176 }
Syed Obaid3313a372014-07-01 01:31:33 -0500177
Vince Lehman76c751c2014-11-18 17:36:38 -0600178 // If a RibEntry's route list is empty, remove it from the tree
Davide Pesaventoe94804b2016-09-19 17:23:21 +0000179 if (entry->getRoutes().empty()) {
Vince Lehman76c751c2014-11-18 17:36:38 -0600180 eraseEntry(ribIt);
181 }
Vince12e49462014-06-09 13:29:32 -0500182 }
Vince Lehman76c751c2014-11-18 17:36:38 -0600183 }
Vince12e49462014-06-09 13:29:32 -0500184}
185
186void
Vince Lehman76c751c2014-11-18 17:36:38 -0600187Rib::onRouteExpiration(const Name& prefix, const Route& route)
Vince12e49462014-06-09 13:29:32 -0500188{
Vince Lehman76c751c2014-11-18 17:36:38 -0600189 NFD_LOG_DEBUG(route << " for " << prefix << " has expired");
Vince12e49462014-06-09 13:29:32 -0500190
Vince Lehman76c751c2014-11-18 17:36:38 -0600191 RibUpdate update;
192 update.setAction(RibUpdate::UNREGISTER)
193 .setName(prefix)
194 .setRoute(route);
Vince12e49462014-06-09 13:29:32 -0500195
Vince Lehman76c751c2014-11-18 17:36:38 -0600196 beginApplyUpdate(update, nullptr, nullptr);
Vince12e49462014-06-09 13:29:32 -0500197}
198
199shared_ptr<RibEntry>
200Rib::findParent(const Name& prefix) const
201{
Vince Lehman76c751c2014-11-18 17:36:38 -0600202 for (int i = prefix.size() - 1; i >= 0; i--) {
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400203 auto it = m_rib.find(prefix.getPrefix(i));
Vince Lehman76c751c2014-11-18 17:36:38 -0600204 if (it != m_rib.end()) {
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400205 return it->second;
Vince12e49462014-06-09 13:29:32 -0500206 }
Vince Lehman76c751c2014-11-18 17:36:38 -0600207 }
Vince12e49462014-06-09 13:29:32 -0500208
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400209 return nullptr;
Vince12e49462014-06-09 13:29:32 -0500210}
211
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400212std::list<shared_ptr<RibEntry>>
Vince12e49462014-06-09 13:29:32 -0500213Rib::findDescendants(const Name& prefix) const
214{
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400215 std::list<shared_ptr<RibEntry>> children;
Vince12e49462014-06-09 13:29:32 -0500216
217 RibTable::const_iterator it = m_rib.find(prefix);
Vince Lehman76c751c2014-11-18 17:36:38 -0600218 if (it != m_rib.end()) {
219 ++it;
220 for (; it != m_rib.end(); ++it) {
221 if (prefix.isPrefixOf(it->first)) {
222 children.push_back((it->second));
223 }
224 else {
225 break;
226 }
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700227 }
Vince Lehman76c751c2014-11-18 17:36:38 -0600228 }
229
230 return children;
231}
232
233std::list<shared_ptr<RibEntry>>
234Rib::findDescendantsForNonInsertedName(const Name& prefix) const
235{
236 std::list<shared_ptr<RibEntry>> children;
237
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400238 for (const auto& pair : m_rib) {
Vince Lehman76c751c2014-11-18 17:36:38 -0600239 if (prefix.isPrefixOf(pair.first)) {
240 children.push_back(pair.second);
241 }
242 }
Vince12e49462014-06-09 13:29:32 -0500243
244 return children;
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700245}
246
Vince12e49462014-06-09 13:29:32 -0500247Rib::RibTable::iterator
248Rib::eraseEntry(RibTable::iterator it)
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700249{
Vince12e49462014-06-09 13:29:32 -0500250 // Entry does not exist
Vince Lehman76c751c2014-11-18 17:36:38 -0600251 if (it == m_rib.end()) {
252 return m_rib.end();
253 }
Vince12e49462014-06-09 13:29:32 -0500254
255 shared_ptr<RibEntry> entry(it->second);
256
257 shared_ptr<RibEntry> parent = entry->getParent();
258
259 // Remove self from parent's children
Vince Lehman76c751c2014-11-18 17:36:38 -0600260 if (parent != nullptr) {
261 parent->removeChild(entry);
262 }
263
264 for (auto childIt = entry->getChildren().begin(); childIt != entry->getChildren().end(); ) {
265 shared_ptr<RibEntry> child = *childIt;
266
267 // Advance iterator so it is not invalidated by removal
268 ++childIt;
269
270 // Remove children from self
271 entry->removeChild(child);
272
273 // Update parent's children
274 if (parent != nullptr) {
275 parent->addChild(child);
Vince12e49462014-06-09 13:29:32 -0500276 }
Vince Lehman76c751c2014-11-18 17:36:38 -0600277 }
Vince12e49462014-06-09 13:29:32 -0500278
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400279 auto nextIt = m_rib.erase(it);
Vince12e49462014-06-09 13:29:32 -0500280
Yanbiao Lic17de832014-11-21 17:51:45 -0800281 // do something after erasing an entry.
282 afterEraseEntry(entry->getName());
283
Vince12e49462014-06-09 13:29:32 -0500284 return nextIt;
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700285}
286
Vince Lehman218be0a2015-01-15 17:25:20 -0600287Rib::RouteSet
288Rib::getAncestorRoutes(const RibEntry& entry) const
Vince Lehman4387e782014-06-19 16:57:45 -0500289{
Vince Lehman218be0a2015-01-15 17:25:20 -0600290 RouteSet ancestorRoutes(&sortRoutes);
Vince Lehman4387e782014-06-19 16:57:45 -0500291
292 shared_ptr<RibEntry> parent = entry.getParent();
293
Vince Lehman76c751c2014-11-18 17:36:38 -0600294 while (parent != nullptr) {
295 for (const Route& route : parent->getRoutes()) {
296 if (route.isChildInherit()) {
297 ancestorRoutes.insert(route);
Vince Lehman4387e782014-06-19 16:57:45 -0500298 }
Vince Lehman4387e782014-06-19 16:57:45 -0500299 }
300
Vince Lehman76c751c2014-11-18 17:36:38 -0600301 if (parent->hasCapture()) {
302 break;
Vince Lehman4387e782014-06-19 16:57:45 -0500303 }
Vince Lehman76c751c2014-11-18 17:36:38 -0600304
305 parent = parent->getParent();
306 }
307
308 return ancestorRoutes;
309}
310
311Rib::RouteSet
312Rib::getAncestorRoutes(const Name& name) const
313{
314 RouteSet ancestorRoutes(&sortRoutes);
315
316 shared_ptr<RibEntry> parent = findParent(name);
317
318 while (parent != nullptr) {
319 for (const Route& route : parent->getRoutes()) {
320 if (route.isChildInherit()) {
321 ancestorRoutes.insert(route);
322 }
323 }
324
325 if (parent->hasCapture()) {
326 break;
327 }
328
329 parent = parent->getParent();
330 }
331
332 return ancestorRoutes;
333}
334
335void
336Rib::beginApplyUpdate(const RibUpdate& update,
337 const Rib::UpdateSuccessCallback& onSuccess,
338 const Rib::UpdateFailureCallback& onFailure)
339{
340 BOOST_ASSERT(m_fibUpdater != nullptr);
341
342 addUpdateToQueue(update, onSuccess, onFailure);
343
344 sendBatchFromQueue();
345}
346
347void
348Rib::beginRemoveFace(uint64_t faceId)
349{
350 for (const auto& nameAndRoute : findRoutesWithFaceId(faceId)) {
351 RibUpdate update;
352 update.setAction(RibUpdate::REMOVE_FACE)
353 .setName(nameAndRoute.first)
354 .setRoute(nameAndRoute.second);
355
356 addUpdateToQueue(update, nullptr, nullptr);
357 }
358
359 sendBatchFromQueue();
360}
361
362void
363Rib::addUpdateToQueue(const RibUpdate& update,
364 const Rib::UpdateSuccessCallback& onSuccess,
365 const Rib::UpdateFailureCallback& onFailure)
366{
367 RibUpdateBatch batch(update.getRoute().faceId);
368 batch.add(update);
369
370 UpdateQueueItem item{batch, onSuccess, onFailure};
371 m_updateBatches.push_back(std::move(item));
372}
373
374void
375Rib::sendBatchFromQueue()
376{
377 if (m_updateBatches.empty() || m_isUpdateInProgress) {
378 return;
379 }
380
381 m_isUpdateInProgress = true;
382
383 UpdateQueueItem item = std::move(m_updateBatches.front());
384 m_updateBatches.pop_front();
385
386 RibUpdateBatch& batch = item.batch;
387
388 // Until task #1698, each RibUpdateBatch contains exactly one RIB update
389 BOOST_ASSERT(batch.size() == 1);
390
Junxiao Shi52009042018-09-10 12:33:56 +0000391 auto fibSuccessCb = bind(&Rib::onFibUpdateSuccess, this, batch, _1, item.managerSuccessCallback);
392 auto fibFailureCb = bind(&Rib::onFibUpdateFailure, this, item.managerFailureCallback, _1, _2);
Vince Lehman76c751c2014-11-18 17:36:38 -0600393
Junxiao Shi52009042018-09-10 12:33:56 +0000394#ifdef WITH_TESTS
395 if (mockFibResponse != nullptr) {
396 m_fibUpdater->computeAndSendFibUpdates(batch, bind([]{}), bind([]{}));
397 bool shouldFibSucceed = mockFibResponse(batch);
398 if (wantMockFibResponseOnce) {
399 mockFibResponse = nullptr;
400 }
401 if (shouldFibSucceed) {
402 fibSuccessCb(m_fibUpdater->m_inheritedRoutes);
403 }
404 else {
405 fibFailureCb(504, "mocked failure");
406 }
407 return;
Vince Lehman76c751c2014-11-18 17:36:38 -0600408 }
Junxiao Shi52009042018-09-10 12:33:56 +0000409#endif
410
411 m_fibUpdater->computeAndSendFibUpdates(batch, fibSuccessCb, fibFailureCb);
Vince Lehman76c751c2014-11-18 17:36:38 -0600412}
413
414void
415Rib::onFibUpdateSuccess(const RibUpdateBatch& batch,
416 const RibUpdateList& inheritedRoutes,
417 const Rib::UpdateSuccessCallback& onSuccess)
418{
419 for (const RibUpdate& update : batch) {
420 switch (update.getAction()) {
421 case RibUpdate::REGISTER:
422 insert(update.getName(), update.getRoute());
423 break;
424 case RibUpdate::UNREGISTER:
425 case RibUpdate::REMOVE_FACE:
426 erase(update.getName(), update.getRoute());
427 break;
428 }
429 }
430
431 // Add and remove precalculated inherited routes to RibEntries
432 modifyInheritedRoutes(inheritedRoutes);
433
434 m_isUpdateInProgress = false;
435
436 if (onSuccess != nullptr) {
437 onSuccess();
438 }
439
440 // Try to advance the batch queue
441 sendBatchFromQueue();
442}
443
444void
445Rib::onFibUpdateFailure(const Rib::UpdateFailureCallback& onFailure,
446 uint32_t code, const std::string& error)
447{
448 m_isUpdateInProgress = false;
449
450 if (onFailure != nullptr) {
451 onFailure(code, error);
452 }
453
454 // Try to advance the batch queue
455 sendBatchFromQueue();
456}
457
458void
459Rib::modifyInheritedRoutes(const RibUpdateList& inheritedRoutes)
460{
461 for (const RibUpdate& update : inheritedRoutes) {
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400462 auto ribIt = m_rib.find(update.getName());
Vince Lehman76c751c2014-11-18 17:36:38 -0600463 BOOST_ASSERT(ribIt != m_rib.end());
464 shared_ptr<RibEntry> entry(ribIt->second);
465
466 switch (update.getAction()) {
467 case RibUpdate::REGISTER:
468 entry->addInheritedRoute(update.getRoute());
469 break;
470 case RibUpdate::UNREGISTER:
471 entry->removeInheritedRoute(update.getRoute());
472 break;
473 case RibUpdate::REMOVE_FACE:
474 break;
475 }
476 }
477}
478
479std::list<Rib::NameAndRoute>
480Rib::findRoutesWithFaceId(uint64_t faceId)
481{
482 std::list<NameAndRoute> routes;
483
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400484 auto lookupIt = m_faceMap.find(faceId);
Vince Lehman76c751c2014-11-18 17:36:38 -0600485 if (lookupIt == m_faceMap.end()) {
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400486 // No RIB entries have this face
Vince Lehman76c751c2014-11-18 17:36:38 -0600487 return routes;
488 }
489
Vince Lehman76c751c2014-11-18 17:36:38 -0600490 // For each RIB entry that has faceId
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400491 for (const auto& entry : lookupIt->second) {
Vince Lehman76c751c2014-11-18 17:36:38 -0600492 // Find the routes in the entry
493 for (const Route& route : *entry) {
494 if (route.faceId == faceId) {
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400495 routes.emplace_back(entry->getName(), route);
Vince Lehman76c751c2014-11-18 17:36:38 -0600496 }
497 }
498 }
499
500 return routes;
Vince Lehman4387e782014-06-19 16:57:45 -0500501}
502
Alexander Afanasyev20d31442014-04-19 17:00:53 -0700503std::ostream&
Vince12e49462014-06-09 13:29:32 -0500504operator<<(std::ostream& os, const Rib& rib)
Alexander Afanasyev20d31442014-04-19 17:00:53 -0700505{
Vince Lehman76c751c2014-11-18 17:36:38 -0600506 for (const auto& item : rib) {
Weiwei Liuaaa58a62016-11-28 23:15:15 -0700507 os << *item.second << "\n";
Vince Lehman76c751c2014-11-18 17:36:38 -0600508 }
Alexander Afanasyev20d31442014-04-19 17:00:53 -0700509
510 return os;
511}
512
Alexander Afanasyev3ecec502014-04-16 13:42:44 -0700513} // namespace rib
514} // namespace nfd