1 | // [License] |
---|
2 | // The Ariba-Underlay Copyright |
---|
3 | // |
---|
4 | // Copyright (c) 2008-2009, Institute of Telematics, UniversitÀt Karlsruhe (TH) |
---|
5 | // |
---|
6 | // Institute of Telematics |
---|
7 | // UniversitÀt Karlsruhe (TH) |
---|
8 | // Zirkel 2, 76128 Karlsruhe |
---|
9 | // Germany |
---|
10 | // |
---|
11 | // Redistribution and use in source and binary forms, with or without |
---|
12 | // modification, are permitted provided that the following conditions are |
---|
13 | // met: |
---|
14 | // |
---|
15 | // 1. Redistributions of source code must retain the above copyright |
---|
16 | // notice, this list of conditions and the following disclaimer. |
---|
17 | // 2. Redistributions in binary form must reproduce the above copyright |
---|
18 | // notice, this list of conditions and the following disclaimer in the |
---|
19 | // documentation and/or other materials provided with the distribution. |
---|
20 | // |
---|
21 | // THIS SOFTWARE IS PROVIDED BY THE INSTITUTE OF TELEMATICS ``AS IS'' AND |
---|
22 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
23 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
---|
24 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ARIBA PROJECT OR |
---|
25 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
---|
26 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
---|
27 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
---|
28 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
---|
29 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
---|
30 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
---|
31 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
---|
32 | // |
---|
33 | // The views and conclusions contained in the software and documentation |
---|
34 | // are those of the authors and should not be interpreted as representing |
---|
35 | // official policies, either expressed or implied, of the Institute of |
---|
36 | // Telematics. |
---|
37 | // [License] |
---|
38 | |
---|
39 | #include "ariba/overlay/BaseOverlay.h" |
---|
40 | #include "ariba/overlay/messages/OverlayMsg.h" |
---|
41 | |
---|
42 | #include "Chord.h" |
---|
43 | #include "detail/chord_routing_table.hpp" |
---|
44 | |
---|
45 | #include "messages/Discovery.h" |
---|
46 | |
---|
47 | namespace ariba { |
---|
48 | namespace overlay { |
---|
49 | |
---|
50 | enum signalMessageTypes { |
---|
51 | typeDiscovery = OverlayMsg::typeSignalingStart + 0x01, |
---|
52 | typeLeave = OverlayMsg::typeSignalingStart + 0x02, |
---|
53 | }; |
---|
54 | |
---|
55 | typedef chord_routing_table::item route_item; |
---|
56 | |
---|
57 | use_logging_cpp( Chord ); |
---|
58 | |
---|
59 | Chord::Chord(BaseOverlay& _baseoverlay, const NodeID& _nodeid, |
---|
60 | OverlayStructureEvents* _eventsReceiver, const OverlayParameterSet& param) : |
---|
61 | OverlayInterface(_baseoverlay, _nodeid, _eventsReceiver, param) { |
---|
62 | |
---|
63 | // create routing table |
---|
64 | this->table = new chord_routing_table(_nodeid, 4); |
---|
65 | orphan_removal_counter = 0; |
---|
66 | discovery_count = 0; |
---|
67 | stabilize_counter = 0; |
---|
68 | stabilize_finger = 0; |
---|
69 | } |
---|
70 | |
---|
71 | Chord::~Chord() { |
---|
72 | |
---|
73 | // delete routing table |
---|
74 | delete table; |
---|
75 | } |
---|
76 | |
---|
77 | /// helper: sets up a link using the base overlay |
---|
78 | LinkID Chord::setup(const EndpointDescriptor& endpoint, const NodeID& remote ) { |
---|
79 | |
---|
80 | // check if we already have a connection |
---|
81 | for (size_t i=0; i<table->size(); i++) |
---|
82 | if ((*table)[i]->ref_count > 0 && (*table)[i]->id == remote && !((*table)[i]->info.isUnspecified())) |
---|
83 | return LinkID::UNSPECIFIED; |
---|
84 | |
---|
85 | // check if we are already trying to establish a link |
---|
86 | for (size_t i=0; i<pending.size(); i++) |
---|
87 | if ( pending[i] == remote ) { |
---|
88 | logging_debug("Already trying to establish a link to node " |
---|
89 | << remote.toString() ); |
---|
90 | return LinkID::UNSPECIFIED; |
---|
91 | } |
---|
92 | |
---|
93 | // adding node to list of pending connections |
---|
94 | pending.push_back( remote ); |
---|
95 | |
---|
96 | logging_info("Request to setup link to " << endpoint.toString() ); |
---|
97 | |
---|
98 | // establish link via base overlay |
---|
99 | return baseoverlay.establishLink( endpoint, remote, |
---|
100 | OverlayInterface::OVERLAY_SERVICE_ID ); |
---|
101 | } |
---|
102 | |
---|
103 | /// helper: sends a message using the "base overlay" |
---|
104 | seqnum_t Chord::send( OverlayMsg* msg, const LinkID& link ) { |
---|
105 | if (link.isUnspecified()) return 0; |
---|
106 | return baseoverlay.send_link( msg, link ); |
---|
107 | } |
---|
108 | |
---|
109 | /// sends a discovery message |
---|
110 | void Chord::send_discovery_to(const NodeID& remote, int ttl) { |
---|
111 | LinkID link = getNextLinkId(remote); |
---|
112 | if ( remote == nodeid || link.isUnspecified()) return; |
---|
113 | if ( table->size() == 0 ) return; |
---|
114 | ttl = 2; |
---|
115 | |
---|
116 | OverlayMsg msg( typeDiscovery ); |
---|
117 | msg.setRegisterRelay(true); |
---|
118 | Discovery dmsg( Discovery::normal, (uint8_t)ttl, baseoverlay.getEndpointDescriptor() ); |
---|
119 | msg.encapsulate(&dmsg); |
---|
120 | |
---|
121 | // send to node |
---|
122 | baseoverlay.send_node( &msg, remote ); |
---|
123 | } |
---|
124 | |
---|
125 | void Chord::discover_neighbors( const LinkID& link ) { |
---|
126 | uint8_t ttl = 1; |
---|
127 | { |
---|
128 | // send predecessor discovery |
---|
129 | OverlayMsg msg( typeDiscovery ); |
---|
130 | msg.setRegisterRelay(true); |
---|
131 | Discovery dmsg( Discovery::predecessor, ttl, |
---|
132 | baseoverlay.getEndpointDescriptor() ); |
---|
133 | msg.encapsulate(&dmsg); |
---|
134 | send(&msg, link); |
---|
135 | } |
---|
136 | { |
---|
137 | // send successor discovery |
---|
138 | OverlayMsg msg( typeDiscovery ); |
---|
139 | msg.setSourceEndpoint( baseoverlay.getEndpointDescriptor() ); |
---|
140 | msg.setRegisterRelay(true); |
---|
141 | Discovery dmsg( Discovery::successor, ttl, |
---|
142 | baseoverlay.getEndpointDescriptor() ); |
---|
143 | msg.encapsulate(&dmsg); |
---|
144 | send(&msg, link); |
---|
145 | } |
---|
146 | } |
---|
147 | |
---|
148 | |
---|
149 | void Chord::createOverlay() { |
---|
150 | } |
---|
151 | |
---|
152 | void Chord::deleteOverlay() { |
---|
153 | |
---|
154 | } |
---|
155 | |
---|
156 | void Chord::joinOverlay(const EndpointDescriptor& boot) { |
---|
157 | logging_info( "joining Chord overlay structure through end-point " << |
---|
158 | (boot.isUnspecified() ? "local" : boot.toString()) ); |
---|
159 | |
---|
160 | // initiator? no->setup first link |
---|
161 | if (!boot.isUnspecified()) |
---|
162 | bootstrapLinks.push_back( setup(boot) ); |
---|
163 | |
---|
164 | // timer for stabilization management |
---|
165 | Timer::setInterval(1000); |
---|
166 | Timer::start(); |
---|
167 | } |
---|
168 | |
---|
169 | void Chord::leaveOverlay() { |
---|
170 | Timer::stop(); |
---|
171 | for (size_t i = 0; i < table->size(); i++) { |
---|
172 | route_item* it = (*table)[i]; |
---|
173 | OverlayMsg msg( typeLeave ); |
---|
174 | send( &msg, it->info ); |
---|
175 | } |
---|
176 | } |
---|
177 | |
---|
178 | /// @see OverlayInterface.h |
---|
179 | const EndpointDescriptor& Chord::resolveNode(const NodeID& node) { |
---|
180 | const route_item* item = table->get(node); |
---|
181 | if (item == NULL || item->info.isUnspecified()) return EndpointDescriptor::UNSPECIFIED(); |
---|
182 | return baseoverlay.getEndpointDescriptor(item->info); |
---|
183 | } |
---|
184 | |
---|
185 | /// @see OverlayInterface.h |
---|
186 | bool Chord::isClosestNodeTo( const NodeID& node ) { |
---|
187 | return table->is_closest_to(node); |
---|
188 | } |
---|
189 | |
---|
190 | /// @see OverlayInterface.h |
---|
191 | const LinkID& Chord::getNextLinkId( const NodeID& id ) const { |
---|
192 | // get next hop |
---|
193 | const route_item* item = table->get_next_hop(id); |
---|
194 | |
---|
195 | // returns a unspecified id when this is itself |
---|
196 | if (item == NULL || item->id == nodeid) |
---|
197 | return LinkID::UNSPECIFIED; |
---|
198 | |
---|
199 | /// return routing info |
---|
200 | return item->info; |
---|
201 | } |
---|
202 | |
---|
203 | OverlayInterface::NodeList Chord::getKnownNodes(bool deep) const { |
---|
204 | OverlayInterface::NodeList nodelist; |
---|
205 | |
---|
206 | if( deep ){ |
---|
207 | // all nodes that I know, fingers, succ/pred |
---|
208 | for (size_t i = 0; i < table->size(); i++){ |
---|
209 | if ((*table)[i]->ref_count != 0 |
---|
210 | && !(*table)[i]->info.isUnspecified()) |
---|
211 | nodelist.push_back((*table)[i]->id); |
---|
212 | } |
---|
213 | } else { |
---|
214 | // only succ and pred |
---|
215 | if( table->get_predesessor() != NULL ){ |
---|
216 | nodelist.push_back( *(table->get_predesessor()) ); |
---|
217 | } |
---|
218 | if( table->get_successor() != NULL ){ |
---|
219 | OverlayInterface::NodeList::iterator i = |
---|
220 | std::find( nodelist.begin(), nodelist.end(), *(table->get_successor()) ); |
---|
221 | if( i == nodelist.end() ) |
---|
222 | nodelist.push_back( *(table->get_successor()) ); |
---|
223 | } |
---|
224 | } |
---|
225 | |
---|
226 | return nodelist; |
---|
227 | } |
---|
228 | |
---|
229 | /// @see CommunicationListener.h |
---|
230 | /// @see OverlayInterface.h |
---|
231 | void Chord::onLinkUp(const LinkID& lnk, const NodeID& remote) { |
---|
232 | logging_info("link_up: link=" << lnk.toString() << " remote=" << |
---|
233 | remote.toString() ); |
---|
234 | for (vector<NodeID>::iterator i=pending.begin(); i!=pending.end(); i++) |
---|
235 | if (*i == remote) { |
---|
236 | pending.erase(i); |
---|
237 | break; |
---|
238 | } |
---|
239 | |
---|
240 | if (remote==nodeid) { |
---|
241 | logging_warn("dropping link that has been established to myself (nodes have same nodeid?)"); |
---|
242 | baseoverlay.dropLink(lnk); |
---|
243 | return; |
---|
244 | } |
---|
245 | |
---|
246 | route_item* item = table->insert(remote); |
---|
247 | |
---|
248 | // item added to routing table? |
---|
249 | if (item != NULL) { // yes-> add to routing table |
---|
250 | logging_info("new routing neighbor: " << remote.toString() |
---|
251 | << " with link " << lnk.toString()); |
---|
252 | |
---|
253 | // replace with new link if link is "better" |
---|
254 | if (item->info!=lnk && item->info.isUnspecified()==false) { |
---|
255 | if (baseoverlay.compare( item->info, lnk ) == 1) { |
---|
256 | logging_info("Replacing link due to concurrent link establishment."); |
---|
257 | baseoverlay.dropLink(item->info); |
---|
258 | item->info = lnk; |
---|
259 | } |
---|
260 | } else { |
---|
261 | item->info = lnk; |
---|
262 | } |
---|
263 | |
---|
264 | // discover neighbors of new overlay neighbor |
---|
265 | showLinks(); |
---|
266 | } else { // no-> add orphan entry to routing table |
---|
267 | logging_info("new orphan: " << remote.toString() |
---|
268 | << " with link " << lnk.toString()); |
---|
269 | table->insert_orphan(remote)->info = lnk; |
---|
270 | } |
---|
271 | |
---|
272 | // erase bootstrap link |
---|
273 | vector<LinkID>::iterator it = std::find(bootstrapLinks.begin(), bootstrapLinks.end(), lnk); |
---|
274 | if( it != bootstrapLinks.end() ) bootstrapLinks.erase( it ); |
---|
275 | } |
---|
276 | |
---|
277 | /// @see CommunicationListener.h or @see OverlayInterface.h |
---|
278 | void Chord::onLinkDown(const LinkID& lnk, const NodeID& remote) { |
---|
279 | logging_debug("link_down: link=" << lnk.toString() << " remote=" << |
---|
280 | remote.toString() ); |
---|
281 | |
---|
282 | // remove link from routing table |
---|
283 | route_item* item = table->get(remote); |
---|
284 | if (item!=NULL && item->info==lnk) { |
---|
285 | item->info = LinkID::UNSPECIFIED; |
---|
286 | table->remove(remote); |
---|
287 | } |
---|
288 | } |
---|
289 | |
---|
290 | /// @see CommunicationListener.h |
---|
291 | /// @see OverlayInterface.h |
---|
292 | void Chord::onMessage(const DataMessage& msg, const NodeID& remote, |
---|
293 | const LinkID& link) { |
---|
294 | |
---|
295 | // decode message |
---|
296 | OverlayMsg* m = dynamic_cast<OverlayMsg*>(msg.getMessage()); |
---|
297 | if (m == NULL) return; |
---|
298 | |
---|
299 | // handle messages |
---|
300 | switch ((signalMessageTypes)m->getType()) { |
---|
301 | |
---|
302 | // discovery request |
---|
303 | case typeDiscovery: { |
---|
304 | // decapsulate message |
---|
305 | Discovery* dmsg = m->decapsulate<Discovery> (); |
---|
306 | logging_debug("Received discovery message with" |
---|
307 | << " src=" << m->getSourceNode().toString() |
---|
308 | << " dst=" << m->getDestinationNode().toString() |
---|
309 | << " ttl=" << (int)dmsg->getTTL() |
---|
310 | << " type=" << (int)dmsg->getType() |
---|
311 | ); |
---|
312 | |
---|
313 | // add discovery node id |
---|
314 | bool found = false; |
---|
315 | BOOST_FOREACH( NodeID& value, discovery ) |
---|
316 | if (value == m->getSourceNode()) { |
---|
317 | found = true; |
---|
318 | break; |
---|
319 | } |
---|
320 | if (!found) discovery.push_back(m->getSourceNode()); |
---|
321 | |
---|
322 | // check if source node can be added to routing table and setup link |
---|
323 | if (m->getSourceNode() != nodeid) |
---|
324 | setup( dmsg->getEndpoint(), m->getSourceNode() ); |
---|
325 | |
---|
326 | // process discovery message -------------------------- switch start -- |
---|
327 | switch (dmsg->getType()) { |
---|
328 | |
---|
329 | // normal: route discovery message like every other message |
---|
330 | case Discovery::normal: { |
---|
331 | // closest node? yes-> split to follow successor and predecessor |
---|
332 | if ( table->is_closest_to(m->getDestinationNode()) ) { |
---|
333 | logging_debug("Discovery split:"); |
---|
334 | if (!table->get_successor()->isUnspecified()) { |
---|
335 | OverlayMsg omsg(*m); |
---|
336 | dmsg->setType(Discovery::successor); |
---|
337 | omsg.encapsulate(dmsg); |
---|
338 | logging_debug("* Routing to successor " |
---|
339 | << table->get_successor()->toString() ); |
---|
340 | baseoverlay.send( &omsg, *table->get_successor() ); |
---|
341 | } |
---|
342 | |
---|
343 | // send predecessor message |
---|
344 | if (!table->get_predesessor()->isUnspecified()) { |
---|
345 | OverlayMsg omsg(*m); |
---|
346 | dmsg->setType(Discovery::predecessor); |
---|
347 | omsg.encapsulate(dmsg); |
---|
348 | logging_debug("* Routing to predecessor " |
---|
349 | << table->get_predesessor()->toString() ); |
---|
350 | baseoverlay.send( &omsg, *table->get_predesessor() ); |
---|
351 | } |
---|
352 | } |
---|
353 | // no-> route message |
---|
354 | else { |
---|
355 | baseoverlay.route( m ); |
---|
356 | } |
---|
357 | break; |
---|
358 | } |
---|
359 | |
---|
360 | // successor mode: follow the successor until TTL is zero |
---|
361 | case Discovery::successor: |
---|
362 | case Discovery::predecessor: { |
---|
363 | // reached destination? no->forward! |
---|
364 | if (m->getDestinationNode() != nodeid) { |
---|
365 | OverlayMsg omsg(*m); |
---|
366 | omsg.encapsulate(dmsg); |
---|
367 | omsg.setService(OverlayInterface::OVERLAY_SERVICE_ID); |
---|
368 | baseoverlay.route( &omsg ); |
---|
369 | break; |
---|
370 | } |
---|
371 | |
---|
372 | // time to live ended? yes-> stop routing |
---|
373 | if (dmsg->getTTL() == 0 || dmsg->getTTL() > 10) break; |
---|
374 | |
---|
375 | // decrease time-to-live |
---|
376 | dmsg->setTTL(dmsg->getTTL() - 1); |
---|
377 | |
---|
378 | const route_item* item = NULL; |
---|
379 | if (dmsg->getType() == Discovery::successor && |
---|
380 | table->get_successor() != NULL) { |
---|
381 | item = table->get(*table->get_successor()); |
---|
382 | } else { |
---|
383 | if (table->get_predesessor()!=NULL) |
---|
384 | item = table->get(*table->get_predesessor()); |
---|
385 | } |
---|
386 | if (item == NULL) |
---|
387 | break; |
---|
388 | |
---|
389 | logging_debug("Routing discovery message to succ/pred " |
---|
390 | << item->id.toString() ); |
---|
391 | OverlayMsg omsg(*m); |
---|
392 | omsg.encapsulate(dmsg); |
---|
393 | omsg.setDestinationNode(item->id); |
---|
394 | omsg.setService(OverlayInterface::OVERLAY_SERVICE_ID); |
---|
395 | baseoverlay.send(&omsg, omsg.getDestinationNode()); |
---|
396 | break; |
---|
397 | } |
---|
398 | case Discovery::invalid: |
---|
399 | break; |
---|
400 | |
---|
401 | default: |
---|
402 | break; |
---|
403 | } |
---|
404 | // process discovery message ---------------------------- switch end -- |
---|
405 | |
---|
406 | delete dmsg; |
---|
407 | break; |
---|
408 | } |
---|
409 | |
---|
410 | // leave |
---|
411 | case typeLeave: { |
---|
412 | if (link!=LinkID::UNSPECIFIED) { |
---|
413 | route_item* item = table->get(remote); |
---|
414 | if (item!=NULL) item->info = LinkID::UNSPECIFIED; |
---|
415 | table->remove(remote); |
---|
416 | baseoverlay.dropLink(link); |
---|
417 | } |
---|
418 | break; |
---|
419 | }} |
---|
420 | } |
---|
421 | |
---|
422 | void Chord::eventFunction() { |
---|
423 | stabilize_counter++; |
---|
424 | if (stabilize_counter < 0 || stabilize_counter == 2) { |
---|
425 | |
---|
426 | // reset counter |
---|
427 | stabilize_counter = 0; |
---|
428 | |
---|
429 | // clear pending connections |
---|
430 | pending.clear(); |
---|
431 | |
---|
432 | // get number of real neighbors |
---|
433 | size_t numNeighbors = 0; |
---|
434 | for (size_t i = 0; i < table->size(); i++) { |
---|
435 | route_item* it = (*table)[i]; |
---|
436 | if (it->ref_count != 0 && !it->info.isUnspecified()) numNeighbors++; |
---|
437 | } |
---|
438 | logging_info("Running stabilization: #links=" |
---|
439 | << table->size() << " #neighbors=" << numNeighbors ); |
---|
440 | |
---|
441 | // updating neighbors |
---|
442 | logging_debug("Discover new ring neighbors"); |
---|
443 | for (size_t i=0; i<table->size(); i++) { |
---|
444 | LinkID id = (*table)[i]->info; |
---|
445 | if (!id.isUnspecified()) discover_neighbors(id); |
---|
446 | } |
---|
447 | |
---|
448 | // sending discovery |
---|
449 | logging_debug("Sending discovery message to my neighbors and fingers"); |
---|
450 | stabilize_finger = ((stabilize_finger+1) % table->get_finger_table_size() ); |
---|
451 | const NodeID disc = table->get_finger_table(stabilize_finger).get_compare().get_center(); |
---|
452 | if (disc != nodeid) |
---|
453 | send_discovery_to(disc); |
---|
454 | |
---|
455 | // remove orphan links |
---|
456 | orphan_removal_counter++; |
---|
457 | if (orphan_removal_counter <0 || orphan_removal_counter >= 2) { |
---|
458 | logging_info("Discovered nodes: "); |
---|
459 | BOOST_FOREACH( NodeID& id, discovery ) |
---|
460 | logging_info("* " << id.toString()); |
---|
461 | discovery.clear(); |
---|
462 | logging_info("Running orphan removal"); |
---|
463 | orphan_removal_counter = 0; |
---|
464 | for (size_t i = 0; i < table->size(); i++) { |
---|
465 | route_item* it = (*table)[i]; |
---|
466 | if (it->ref_count == 0 && !it->info.isUnspecified()) { |
---|
467 | logging_info("Dropping orphaned link " << it->info.toString() << " to " << it->id.toString()); |
---|
468 | table->insert(it->id); |
---|
469 | if (it->ref_count==0) { |
---|
470 | LinkID id = it->info; |
---|
471 | it->info = LinkID::UNSPECIFIED; |
---|
472 | baseoverlay.dropLink(id); |
---|
473 | } |
---|
474 | } |
---|
475 | } |
---|
476 | } |
---|
477 | } |
---|
478 | } |
---|
479 | |
---|
480 | void Chord::showLinks() { |
---|
481 | logging_info("--- chord routing information ----------------------------------"); |
---|
482 | logging_info("predecessor: " << (table->get_predesessor()==NULL? "<none>" : |
---|
483 | table->get_predesessor()->toString()) ); |
---|
484 | logging_info("node_id : " << nodeid.toString() ); |
---|
485 | logging_info("successor : " << (table->get_successor()==NULL? "<none>" : |
---|
486 | table->get_successor()->toString())); |
---|
487 | logging_info("----------------------------------------------------------------"); |
---|
488 | } |
---|
489 | |
---|
490 | /// @see OverlayInterface.h |
---|
491 | std::string Chord::debugInformation() const { |
---|
492 | std::ostringstream s; |
---|
493 | s << "protocol : Chord" << endl; |
---|
494 | s << "node_id : " << nodeid.toString() << endl; |
---|
495 | s << "predecessor: " << (table->get_predesessor()==NULL? "<none>" : |
---|
496 | table->get_predesessor()->toString()) << endl; |
---|
497 | s << "successor : " << (table->get_successor()==NULL? "<none>" : |
---|
498 | table->get_successor()->toString()) << endl; |
---|
499 | s << "nodes: " << endl; |
---|
500 | for (size_t i = 0; i < table->size(); i++) { |
---|
501 | route_item* it = (*table)[i]; |
---|
502 | if (it->ref_count != 0 && !it->info.isUnspecified()) { |
---|
503 | s << it->id.toString().substr(0,6) |
---|
504 | << " using " << it->info.toString().substr(0,6) << endl; |
---|
505 | } |
---|
506 | } |
---|
507 | return s.str(); |
---|
508 | } |
---|
509 | |
---|
510 | |
---|
511 | |
---|
512 | }} // namespace ariba, overlay |
---|