blob: ea4fc16fcf53544ef15c6ce4a8e1f83557d0965a [file] [log] [blame]
akmhoque3d06e792014-05-27 16:23:20 -05001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Ashlesh Gawande0421bc62020-05-08 20:42:19 -07002/*
awlane6d7c37f2025-03-07 11:53:58 -06003 * Copyright (c) 2014-2025, The University of Memphis,
Vince Lehmanc2e51f62015-01-20 15:03:11 -06004 * Regents of the University of California,
5 * Arizona Board of Regents.
akmhoque3d06e792014-05-27 16:23:20 -05006 *
7 * This file is part of NLSR (Named-data Link State Routing).
8 * See AUTHORS.md for complete list of NLSR authors and contributors.
9 *
10 * NLSR is free software: you can redistribute it and/or modify it under the terms
11 * of the GNU General Public License as published by the Free Software Foundation,
12 * either version 3 of the License, or (at your option) any later version.
13 *
14 * NLSR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
15 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 * PURPOSE. See the GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * NLSR, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
Ashlesh Gawande0421bc62020-05-08 20:42:19 -070020 */
Vince Lehmanc2e51f62015-01-20 15:03:11 -060021
Vince Lehmancae33b62015-06-05 09:21:30 -050022#include "name-prefix-table.hpp"
23
24#include "logger.hpp"
25#include "nlsr.hpp"
26#include "routing-table.hpp"
27
28#include <algorithm>
akmhoque53353462014-04-22 08:43:45 -050029#include <list>
30#include <utility>
akmhoque53353462014-04-22 08:43:45 -050031
32namespace nlsr {
33
dmcoomescf8d0ed2017-02-21 11:39:01 -060034INIT_LOGGER(route.NamePrefixTable);
akmhoque674b0b12014-05-20 14:33:28 -050035
Ashlesh Gawande5d93aa52020-06-13 18:57:45 -070036NamePrefixTable::NamePrefixTable(const ndn::Name& ownRouterName, Fib& fib,
37 RoutingTable& routingTable,
38 AfterRoutingChange& afterRoutingChangeSignal,
39 Lsdb::AfterLsdbModified& afterLsdbModifiedSignal)
40 : m_ownRouterName(ownRouterName)
41 , m_fib(fib)
Ashlesh Gawande85998a12017-12-07 22:22:13 -060042 , m_routingTable(routingTable)
Nick Gordonb7b58392017-08-17 16:29:21 -050043{
Ashlesh Gawande5d93aa52020-06-13 18:57:45 -070044 m_afterRoutingChangeConnection = afterRoutingChangeSignal.connect(
Nick Gordonb7b58392017-08-17 16:29:21 -050045 [this] (const std::list<RoutingTableEntry>& entries) {
46 updateWithNewRoute(entries);
47 });
Ashlesh Gawande5d93aa52020-06-13 18:57:45 -070048
49 m_afterLsdbModified = afterLsdbModifiedSignal.connect(
50 [this] (std::shared_ptr<Lsa> lsa, LsdbUpdate updateType,
51 const auto& namesToAdd, const auto& namesToRemove) {
52 updateFromLsdb(lsa, updateType, namesToAdd, namesToRemove);
53 }
54 );
Nick Gordonb7b58392017-08-17 16:29:21 -050055}
56
57NamePrefixTable::~NamePrefixTable()
58{
59 m_afterRoutingChangeConnection.disconnect();
Ashlesh Gawande5d93aa52020-06-13 18:57:45 -070060 m_afterLsdbModified.disconnect();
61}
62
63void
64NamePrefixTable::updateFromLsdb(std::shared_ptr<Lsa> lsa, LsdbUpdate updateType,
awlane6d7c37f2025-03-07 11:53:58 -060065 const std::list<nlsr::PrefixInfo>& namesToAdd,
66 const std::list<nlsr::PrefixInfo>& namesToRemove)
Ashlesh Gawande5d93aa52020-06-13 18:57:45 -070067{
68 if (m_ownRouterName == lsa->getOriginRouter()) {
69 return;
70 }
71 NLSR_LOG_TRACE("Got update from Lsdb for router: " << lsa->getOriginRouter());
72
73 if (updateType == LsdbUpdate::INSTALLED) {
74 addEntry(lsa->getOriginRouter(), lsa->getOriginRouter());
75
76 if (lsa->getType() == Lsa::Type::NAME) {
77 auto nlsa = std::static_pointer_cast<NameLsa>(lsa);
awlane6d7c37f2025-03-07 11:53:58 -060078 for (const auto &prefix : nlsa->getNpl().getPrefixInfo()) {
79 if (prefix.getName() != m_ownRouterName) {
80 m_nexthopCost[DestNameKey(lsa->getOriginRouter(), prefix.getName())] = prefix.getCost();
81 addEntry(prefix.getName(), lsa->getOriginRouter());
Ashlesh Gawande5d93aa52020-06-13 18:57:45 -070082 }
83 }
84 }
85 }
86 else if (updateType == LsdbUpdate::UPDATED) {
87 if (lsa->getType() != Lsa::Type::NAME) {
88 return;
89 }
90
awlane6d7c37f2025-03-07 11:53:58 -060091 for (const auto &prefix : namesToAdd) {
92 if (prefix.getName() != m_ownRouterName) {
93 m_nexthopCost[DestNameKey(lsa->getOriginRouter(), prefix.getName())] = prefix.getCost();
94 addEntry(prefix.getName(), lsa->getOriginRouter());
Ashlesh Gawande5d93aa52020-06-13 18:57:45 -070095 }
96 }
97
awlane6d7c37f2025-03-07 11:53:58 -060098 for (const auto &prefix : namesToRemove) {
99 if (prefix.getName() != m_ownRouterName) {
100 m_nexthopCost.erase(m_nexthopCost.find(DestNameKey(lsa->getOriginRouter(), prefix.getName())));
101 removeEntry(prefix.getName(), lsa->getOriginRouter());
Ashlesh Gawande5d93aa52020-06-13 18:57:45 -0700102 }
103 }
104 }
105 else {
106 removeEntry(lsa->getOriginRouter(), lsa->getOriginRouter());
107 if (lsa->getType() == Lsa::Type::NAME) {
108 auto nlsa = std::static_pointer_cast<NameLsa>(lsa);
109 for (const auto& name : nlsa->getNpl().getNames()) {
110 if (name != m_ownRouterName) {
awlane6d7c37f2025-03-07 11:53:58 -0600111 m_nexthopCost.erase(m_nexthopCost.find(DestNameKey(lsa->getOriginRouter(), name)));
Ashlesh Gawande5d93aa52020-06-13 18:57:45 -0700112 removeEntry(name, lsa->getOriginRouter());
113 }
114 }
115 }
116 }
Nick Gordonb7b58392017-08-17 16:29:21 -0500117}
118
awlane6d7c37f2025-03-07 11:53:58 -0600119NexthopList
120NamePrefixTable::adjustNexthopCosts(const NexthopList& nhlist, const ndn::Name& nameToCheck, const ndn::Name& destRouterName)
121{
122 NexthopList new_nhList;
123 for (const auto& nh : nhlist.getNextHops()) {
124 const NextHop newNextHop = NextHop(nh.getConnectingFaceUri(), nh.getRouteCost() +
125 m_nexthopCost[DestNameKey(destRouterName, nameToCheck)]);
126 new_nhList.addNextHop(newNextHop);
127 }
128 return new_nhList;
129}
130
akmhoque53353462014-04-22 08:43:45 -0500131void
akmhoque31d1d4b2014-05-05 22:08:14 -0500132NamePrefixTable::addEntry(const ndn::Name& name, const ndn::Name& destRouter)
akmhoque53353462014-04-22 08:43:45 -0500133{
Nick Gordonb50e51b2016-07-22 16:05:57 -0500134 // Check if the advertised name prefix is in the table already.
Davide Pesaventoc1d0e8e2022-06-15 14:26:02 -0400135 auto nameItr = std::find_if(m_table.begin(), m_table.end(),
136 [&] (const auto& entry) { return name == entry->getNamePrefix(); });
Vince Lehmancae33b62015-06-05 09:21:30 -0500137
Nick Gordonb50e51b2016-07-22 16:05:57 -0500138 // Attempt to find a routing table pool entry (RTPE) we can use.
Davide Pesaventoc1d0e8e2022-06-15 14:26:02 -0400139 auto rtpeItr = m_rtpool.find(destRouter);
Nick Gordonb50e51b2016-07-22 16:05:57 -0500140
141 // These declarations just to make the compiler happy...
142 RoutingTablePoolEntry rtpe;
143 std::shared_ptr<RoutingTablePoolEntry> rtpePtr(nullptr);
144
145 // There isn't currently a routing table entry in the pool for this name
146 if (rtpeItr == m_rtpool.end()) {
147 // See if there is a routing table entry available we could use
Ashlesh Gawande85998a12017-12-07 22:22:13 -0600148 RoutingTableEntry* routeEntryPtr = m_routingTable.findRoutingTableEntry(destRouter);
Nick Gordonb50e51b2016-07-22 16:05:57 -0500149
150 // We have to create a new routing table entry
151 if (routeEntryPtr == nullptr) {
152 rtpe = RoutingTablePoolEntry(destRouter, 0);
153 }
154 // There was already a usable one in the routing table
155 else {
156 rtpe = RoutingTablePoolEntry(*routeEntryPtr, 0);
157 }
158
159 // Add the new pool object to the pool.
160 rtpePtr = addRtpeToPool(rtpe);
161 }
162 // There was one already, so just fetch that one.
163 else {
164 rtpePtr = (*rtpeItr).second;
165 }
166
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500167 std::shared_ptr<NamePrefixTableEntry> npte;
Nick Gordonb50e51b2016-07-22 16:05:57 -0500168 // Either we have to make a new NPT entry or there already was one.
169 if (nameItr == m_table.end()) {
dmcoomes5bcb39e2017-10-31 15:07:55 -0500170 NLSR_LOG_DEBUG("Adding origin: " << rtpePtr->getDestination()
Davide Pesaventod90338d2021-01-07 17:50:05 -0500171 << " to a new name prefix: " << name);
172 npte = std::make_shared<NamePrefixTableEntry>(name);
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500173 npte->addRoutingTableEntry(rtpePtr);
174 npte->generateNhlfromRteList();
Nick Gordonb50e51b2016-07-22 16:05:57 -0500175 m_table.push_back(npte);
Davide Pesaventod90338d2021-01-07 17:50:05 -0500176
Nick Gordonb50e51b2016-07-22 16:05:57 -0500177 // If this entry has next hops, we need to inform the FIB
Nick Gordonff9a6272017-10-12 13:38:29 -0500178 if (npte->getNexthopList().size() > 0) {
Ashlesh Gawandee8d8bd52018-08-09 17:18:51 -0500179 NLSR_LOG_TRACE("Updating FIB with next hops for " << npte->getNamePrefix());
awlane6d7c37f2025-03-07 11:53:58 -0600180 m_fib.update(name, adjustNexthopCosts(npte->getNexthopList(), name, destRouter));
Nick Gordonb50e51b2016-07-22 16:05:57 -0500181 }
182 // The routing table may recalculate and add a routing table entry
183 // with no next hops to replace an existing routing table entry. In
184 // this case, the name prefix is no longer reachable through a next
185 // hop and should be removed from the FIB. But, the prefix should
186 // remain in the Name Prefix Table as a future routing table
187 // calculation may add next hops.
188 else {
Ashlesh Gawandee8d8bd52018-08-09 17:18:51 -0500189 NLSR_LOG_TRACE(npte->getNamePrefix() << " has no next hops; removing from FIB");
Ashlesh Gawande85998a12017-12-07 22:22:13 -0600190 m_fib.remove(name);
Nick Gordonb50e51b2016-07-22 16:05:57 -0500191 }
akmhoque53353462014-04-22 08:43:45 -0500192 }
akmhoque157b0a42014-05-13 00:26:37 -0500193 else {
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500194 npte = *nameItr;
Ashlesh Gawandee5002b32018-12-20 21:07:31 -0600195 NLSR_LOG_TRACE("Adding origin: " << rtpePtr->getDestination() <<
196 " to existing prefix: " << **nameItr);
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500197 (*nameItr)->addRoutingTableEntry(rtpePtr);
198 (*nameItr)->generateNhlfromRteList();
Nick Gordonb50e51b2016-07-22 16:05:57 -0500199
Nick Gordonff9a6272017-10-12 13:38:29 -0500200 if ((*nameItr)->getNexthopList().size() > 0) {
Ashlesh Gawandee5002b32018-12-20 21:07:31 -0600201 NLSR_LOG_TRACE("Updating FIB with next hops for " << (**nameItr));
awlane6d7c37f2025-03-07 11:53:58 -0600202 m_fib.update(name, adjustNexthopCosts((*nameItr)->getNexthopList(), name, destRouter));
Nick Gordonb50e51b2016-07-22 16:05:57 -0500203 }
204 else {
Ashlesh Gawandee8d8bd52018-08-09 17:18:51 -0500205 NLSR_LOG_TRACE(npte->getNamePrefix() << " has no next hops; removing from FIB");
Ashlesh Gawande85998a12017-12-07 22:22:13 -0600206 m_fib.remove(name);
Nick Gordonb50e51b2016-07-22 16:05:57 -0500207 }
akmhoque53353462014-04-22 08:43:45 -0500208 }
Davide Pesaventod90338d2021-01-07 17:50:05 -0500209
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500210 // Add the reference to this NPT to the RTPE.
Davide Pesaventoc1d0e8e2022-06-15 14:26:02 -0400211 rtpePtr->namePrefixTableEntries.try_emplace(npte->getNamePrefix(),
212 std::weak_ptr<NamePrefixTableEntry>(npte));
akmhoque53353462014-04-22 08:43:45 -0500213}
214
215void
akmhoque31d1d4b2014-05-05 22:08:14 -0500216NamePrefixTable::removeEntry(const ndn::Name& name, const ndn::Name& destRouter)
akmhoque53353462014-04-22 08:43:45 -0500217{
dmcoomes5bcb39e2017-10-31 15:07:55 -0500218 NLSR_LOG_DEBUG("Removing origin: " << destRouter << " from " << name);
Vince Lehmancae33b62015-06-05 09:21:30 -0500219
Nick Gordonb50e51b2016-07-22 16:05:57 -0500220 // Fetch an iterator to the appropriate pair object in the pool.
Davide Pesaventoc1d0e8e2022-06-15 14:26:02 -0400221 auto rtpeItr = m_rtpool.find(destRouter);
Vince Lehmancae33b62015-06-05 09:21:30 -0500222
Nick Gordonb50e51b2016-07-22 16:05:57 -0500223 // Simple error checking to prevent any unusual behavior in the case
224 // that we try to remove an entry that isn't there.
225 if (rtpeItr == m_rtpool.end()) {
dmcoomes5bcb39e2017-10-31 15:07:55 -0500226 NLSR_LOG_DEBUG("No entry for origin: " << destRouter
Davide Pesaventoc1d0e8e2022-06-15 14:26:02 -0400227 << " found, so it cannot be removed from prefix: " << name);
Nick Gordonb50e51b2016-07-22 16:05:57 -0500228 return;
229 }
230 std::shared_ptr<RoutingTablePoolEntry> rtpePtr = rtpeItr->second;
231
232 // Ensure that the entry exists
Davide Pesaventoc1d0e8e2022-06-15 14:26:02 -0400233 auto nameItr = std::find_if(m_table.begin(), m_table.end(),
234 [&] (const auto& entry) { return entry->getNamePrefix() == name; });
Nick Gordonb50e51b2016-07-22 16:05:57 -0500235 if (nameItr != m_table.end()) {
dmcoomes5bcb39e2017-10-31 15:07:55 -0500236 NLSR_LOG_TRACE("Removing origin: " << rtpePtr->getDestination()
Davide Pesaventoc1d0e8e2022-06-15 14:26:02 -0400237 << " from prefix: " << **nameItr);
Nick Gordonb50e51b2016-07-22 16:05:57 -0500238
239 // Rather than iterating through the whole list periodically, just
240 // delete them here if they have no references.
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500241 if ((*nameItr)->removeRoutingTableEntry(rtpePtr) == 0) {
Nick Gordonb50e51b2016-07-22 16:05:57 -0500242 deleteRtpeFromPool(rtpePtr);
243 }
244
245 // If the prefix is a router prefix and it does not have any other
246 // routing table entries, the Adjacency/Coordinate LSA associated
247 // with that origin router has been removed from the LSDB and so
248 // the router prefix should be removed from the Name Prefix Table.
249 //
250 // If the prefix is an advertised name prefix: If another router
251 // advertises this name prefix, the RteList should have another
252 // entry for that router; the next hops should be recalculated
253 // and installed in the FIB.
254 //
255 // If no other router advertises this name prefix, the RteList
256 // should be empty and the prefix can be removed from the Name
257 // Prefix Table. Once a new Name LSA advertises this prefix, a
258 // new entry for the prefix will be created.
259 //
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500260 if ((*nameItr)->getRteListSize() == 0) {
dmcoomes5bcb39e2017-10-31 15:07:55 -0500261 NLSR_LOG_TRACE(**nameItr << " has no routing table entries;"
Davide Pesaventoc1d0e8e2022-06-15 14:26:02 -0400262 << " removing from table and FIB");
Nick Gordonb50e51b2016-07-22 16:05:57 -0500263 m_table.erase(nameItr);
Ashlesh Gawande85998a12017-12-07 22:22:13 -0600264 m_fib.remove(name);
Nick Gordonb50e51b2016-07-22 16:05:57 -0500265 }
266 else {
dmcoomes5bcb39e2017-10-31 15:07:55 -0500267 NLSR_LOG_TRACE(**nameItr << " has other routing table entries;"
Davide Pesaventoc1d0e8e2022-06-15 14:26:02 -0400268 << " updating FIB with next hops");
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500269 (*nameItr)->generateNhlfromRteList();
awlane6d7c37f2025-03-07 11:53:58 -0600270 m_fib.update(name, adjustNexthopCosts((*nameItr)->getNexthopList(), name, destRouter));
Nick Gordonb50e51b2016-07-22 16:05:57 -0500271 }
akmhoque53353462014-04-22 08:43:45 -0500272 }
akmhoque157b0a42014-05-13 00:26:37 -0500273 else {
dmcoomes5bcb39e2017-10-31 15:07:55 -0500274 NLSR_LOG_DEBUG("Attempted to remove origin: " << rtpePtr->getDestination()
Davide Pesaventoc1d0e8e2022-06-15 14:26:02 -0400275 << " from non-existent prefix: " << name);
akmhoque53353462014-04-22 08:43:45 -0500276 }
277}
278
279void
Nick Gordonb7b58392017-08-17 16:29:21 -0500280NamePrefixTable::updateWithNewRoute(const std::list<RoutingTableEntry>& entries)
akmhoque53353462014-04-22 08:43:45 -0500281{
dmcoomes5bcb39e2017-10-31 15:07:55 -0500282 NLSR_LOG_DEBUG("Updating table with newly calculated routes");
Vince Lehmancae33b62015-06-05 09:21:30 -0500283
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500284 // Iterate over each pool entry we have
285 for (auto&& poolEntryPair : m_rtpool) {
286 auto&& poolEntry = poolEntryPair.second;
Nick Gordonb7b58392017-08-17 16:29:21 -0500287 auto sourceEntry = std::find_if(entries.begin(), entries.end(),
288 [&poolEntry] (const RoutingTableEntry& entry) {
289 return poolEntry->getDestination() == entry.getDestination();
290 });
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500291 // If this pool entry has a corresponding entry in the routing table now
Nick Gordonb7b58392017-08-17 16:29:21 -0500292 if (sourceEntry != entries.end()
293 && poolEntry->getNexthopList() != sourceEntry->getNexthopList()) {
dmcoomes5bcb39e2017-10-31 15:07:55 -0500294 NLSR_LOG_DEBUG("Routing entry: " << poolEntry->getDestination() << " has changed next-hops.");
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500295 poolEntry->setNexthopList(sourceEntry->getNexthopList());
296 for (const auto& nameEntry : poolEntry->namePrefixTableEntries) {
297 auto nameEntryFullPtr = nameEntry.second.lock();
298 addEntry(nameEntryFullPtr->getNamePrefix(), poolEntry->getDestination());
299 }
300 }
Nick Gordonb7b58392017-08-17 16:29:21 -0500301 else if (sourceEntry == entries.end()) {
dmcoomes5bcb39e2017-10-31 15:07:55 -0500302 NLSR_LOG_DEBUG("Routing entry: " << poolEntry->getDestination() << " now has no next-hops.");
Ashlesh Gawande0421bc62020-05-08 20:42:19 -0700303 poolEntry->getNexthopList().clear();
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500304 for (const auto& nameEntry : poolEntry->namePrefixTableEntries) {
305 auto nameEntryFullPtr = nameEntry.second.lock();
306 addEntry(nameEntryFullPtr->getNamePrefix(), poolEntry->getDestination());
307 }
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500308 }
309 else {
dmcoomes5bcb39e2017-10-31 15:07:55 -0500310 NLSR_LOG_TRACE("No change in routing entry:" << poolEntry->getDestination()
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500311 << ", no action necessary.");
akmhoque53353462014-04-22 08:43:45 -0500312 }
313 }
314}
315
Davide Pesaventoc1d0e8e2022-06-15 14:26:02 -0400316// Inserts the routing table pool entry into the NPT's RTE storage
317// pool. This cannot fail, so the pool is guaranteed to contain the
318// item after this occurs.
Nick Gordonb50e51b2016-07-22 16:05:57 -0500319std::shared_ptr<RoutingTablePoolEntry>
320NamePrefixTable::addRtpeToPool(RoutingTablePoolEntry& rtpe)
321{
Davide Pesaventoc1d0e8e2022-06-15 14:26:02 -0400322 auto poolIt = m_rtpool.try_emplace(rtpe.getDestination(),
323 std::make_shared<RoutingTablePoolEntry>(rtpe)).first;
324 return poolIt->second;
Nick Gordonb50e51b2016-07-22 16:05:57 -0500325}
326
Davide Pesaventoc1d0e8e2022-06-15 14:26:02 -0400327// Removes the routing table pool entry from the storage pool. The
328// postconditions of this function are guaranteed to include that
329// the storage pool does not contain such an item. Additionally,
330// this function cannot fail, but nonetheless debug information is
331// given in the case that this function is called with an entry that
332// isn't in the pool.
Nick Gordonb50e51b2016-07-22 16:05:57 -0500333void
334NamePrefixTable::deleteRtpeFromPool(std::shared_ptr<RoutingTablePoolEntry> rtpePtr)
335{
336 if (m_rtpool.erase(rtpePtr->getDestination()) != 1) {
dmcoomes5bcb39e2017-10-31 15:07:55 -0500337 NLSR_LOG_DEBUG("Attempted to delete non-existent origin: "
Davide Pesaventoc1d0e8e2022-06-15 14:26:02 -0400338 << rtpePtr->getDestination()
339 << " from NPT routing table entry storage pool.");
Nick Gordonb50e51b2016-07-22 16:05:57 -0500340 }
341}
342
akmhoque53353462014-04-22 08:43:45 -0500343void
akmhoque674b0b12014-05-20 14:33:28 -0500344NamePrefixTable::writeLog()
345{
dmcoomes5bcb39e2017-10-31 15:07:55 -0500346 NLSR_LOG_DEBUG(*this);
akmhoque674b0b12014-05-20 14:33:28 -0500347}
348
Vince Lehmancae33b62015-06-05 09:21:30 -0500349std::ostream&
350operator<<(std::ostream& os, const NamePrefixTable& table)
351{
352 os << "----------------NPT----------------------\n";
353
Nick Gordonc0c6bcf2017-08-15 18:11:21 -0500354 for (const auto& entryPtr : table) {
355 os << *entryPtr << std::endl;
Vince Lehmancae33b62015-06-05 09:21:30 -0500356 }
357
358 return os;
359}
360
361} // namespace nlsr