|
1 /* |
|
2 * Copyright 2007 Intel Corporation |
|
3 * |
|
4 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 * you may not use this file except in compliance with the License. |
|
6 * You may obtain a copy of the License at |
|
7 * |
|
8 * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 * |
|
10 * Unless required by applicable law or agreed to in writing, software |
|
11 * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 * See the License for the specific language governing permissions and |
|
14 * limitations under the License. |
|
15 */ |
|
16 |
|
17 #ifdef HAVE_CONFIG_H |
|
18 #include <dtn-config.h> |
|
19 #endif |
|
20 |
|
21 #include <algorithm> |
|
22 #include <oasys/debug/DebugUtils.h> |
|
23 |
|
24 #include "DTLSRRouter.h" |
|
25 #include "bundling/BundleDaemon.h" |
|
26 #include "bundling/SDNV.h" |
|
27 #include "bundling/TempBundle.h" |
|
28 #include "contacts/ContactManager.h" |
|
29 #include "routing/RouteTable.h" |
|
30 #include "session/Session.h" |
|
31 |
|
32 namespace oasys { |
|
33 |
|
34 //---------------------------------------------------------------------- |
|
35 template<> |
|
36 const char* |
|
37 InlineFormatter<dtn::DTLSRRouter::EdgeInfo> |
|
38 ::format(const dtn::DTLSRRouter::EdgeInfo& ei) |
|
39 { |
|
40 const char* state_str; |
|
41 if (ei.params_.state_ == dtn::DTLSRRouter::LinkParams::LINK_UP) { |
|
42 state_str = "UP"; |
|
43 } else if (ei.params_.state_ == dtn::DTLSRRouter::LinkParams::LINK_DOWN) { |
|
44 state_str = "DOWN"; |
|
45 } else { |
|
46 state_str = "__UNKNOWN__"; |
|
47 } |
|
48 |
|
49 buf_.appendf("%s: state=%s cost=%u delay=%u bw=%u", |
|
50 ei.id_.c_str(), state_str, |
|
51 ei.params_.cost_, ei.params_.delay_, ei.params_.bw_); |
|
52 return buf_.c_str(); |
|
53 } |
|
54 |
|
55 } // namespace oasys |
|
56 |
|
57 namespace dtn { |
|
58 |
|
59 //---------------------------------------------------------------------- |
|
60 class DTLSRRouter::CostWeightFn : public RoutingGraph::WeightFn { |
|
61 public: |
|
62 u_int32_t operator()(const RoutingGraph::SearchInfo& info, |
|
63 const RoutingGraph::Edge* edge) |
|
64 { |
|
65 (void)info; |
|
66 if (edge->info().params_.state_ == LinkParams::LINK_DOWN) { |
|
67 return 0xffffffff; |
|
68 } |
|
69 return edge->info().params_.cost_; |
|
70 } |
|
71 }; |
|
72 |
|
73 //---------------------------------------------------------------------- |
|
74 class DTLSRRouter::DelayWeightFn : public RoutingGraph::WeightFn { |
|
75 public: |
|
76 u_int32_t operator()(const RoutingGraph::SearchInfo& info, |
|
77 const RoutingGraph::Edge* edge) |
|
78 { |
|
79 (void)info; |
|
80 |
|
81 u_int32_t downtime = (info.now_ - edge->info().last_update_).sec_; |
|
82 if (DTLSRConfig::instance()->lsa_interval_ != 0 && |
|
83 downtime > (2 * DTLSRConfig::instance()->lsa_interval_)) |
|
84 { |
|
85 // don't know anything implies the link is "down" |
|
86 return 0xffffffff; |
|
87 } |
|
88 |
|
89 if (edge->info().params_.state_ == LinkParams::LINK_DOWN) { |
|
90 return 0xffffffff; |
|
91 } |
|
92 return edge->info().params_.delay_; |
|
93 } |
|
94 }; |
|
95 |
|
96 //---------------------------------------------------------------------- |
|
97 class DTLSRRouter::EstimatedDelayWeightFn : public RoutingGraph::WeightFn { |
|
98 public: |
|
99 EstimatedDelayWeightFn(DTLSRRouter* rtr) |
|
100 : router_(rtr) {} |
|
101 |
|
102 u_int32_t operator()(const RoutingGraph::SearchInfo& info, |
|
103 const RoutingGraph::Edge* edge) |
|
104 { |
|
105 if (edge->info().params_.state_ == LinkParams::LINK_DOWN) { |
|
106 |
|
107 // adjust the delay based on the uptime / downtime |
|
108 u_int32_t downtime = (info.now_ - edge->info().last_update_).sec_; |
|
109 |
|
110 // XXX/demmer for now make the bogus silly assumption that |
|
111 // it will take just as long to come back up as it's been |
|
112 // down for, plus a constant factor of 5 seconds to |
|
113 // establish the link, capped at 1 day |
|
114 // |
|
115 // really, we need a good way of succinctly characterizing |
|
116 // delay in a way that can be decayed but also take into |
|
117 // account historical data, i.e. a number that |
|
118 // grows/shrinks as the link gets worse but where a router |
|
119 // can take into account that some outages are expected |
|
120 u_int32_t ret = (downtime + 5) >> DTLSRConfig::instance()->weight_shift_; |
|
121 |
|
122 if (ret < 24*60*60) { |
|
123 log_debug_p("/dtn/route/graph", |
|
124 "weight of edge %s=%u (downtime %u + 5)", |
|
125 edge->info().id_.c_str(), ret, downtime); |
|
126 } else { |
|
127 ret = 24*60*60; |
|
128 log_warn_p("/dtn/route/graph", |
|
129 "weight of edge %s=%u: (downtime %u exceeded max)", |
|
130 edge->info().id_.c_str(), ret, downtime); |
|
131 } |
|
132 |
|
133 return ret; |
|
134 } |
|
135 |
|
136 u_int32_t qlen, qsize; |
|
137 // XXX/demmer use the link queue length if it's a local link |
|
138 qlen = edge->info().params_.qcount_; |
|
139 qsize = edge->info().params_.qsize_; |
|
140 |
|
141 // based on the queue length and link estimates, calculate the |
|
142 // forwarding delay |
|
143 u_int32_t qdelay = (qlen + 1) * edge->info().params_.delay_ / 1000; |
|
144 u_int32_t bwdelay = 0; |
|
145 if (edge->info().params_.bw_ != 0) { |
|
146 // XXX/demmer is bw bits or bytes? |
|
147 bwdelay = qsize / edge->info().params_.bw_; |
|
148 } |
|
149 |
|
150 u_int32_t delay = qdelay + bwdelay; |
|
151 log_debug_p("/dtn/route/graph", "weight of edge %s=%u " |
|
152 "(qlen %u delay %u qdelay %u qsize %u bw %u bwdelay %u)", |
|
153 edge->info().id_.c_str(), delay, |
|
154 qlen, edge->info().params_.delay_, qdelay, |
|
155 qsize, edge->info().params_.bw_, bwdelay); |
|
156 |
|
157 // XXX/demmer we might want to have an estimator for bundles |
|
158 // that we've sent out en route to the destination along with |
|
159 // where and when those bundles will be at different routers. |
|
160 // then when LSAs come in, we adjust our estimates accordingly |
|
161 |
|
162 return delay; |
|
163 } |
|
164 |
|
165 DTLSRRouter* router_; |
|
166 }; |
|
167 |
|
168 //---------------------------------------------------------------------- |
|
169 DTLSRRouter::DTLSRRouter() |
|
170 : TableBasedRouter("DTLSRRouter", "dtlsr"), |
|
171 announce_tag_("dtlsr"), |
|
172 announce_eid_("dtn://*/dtlsr?*"), |
|
173 current_lsas_("DTLSRRouter::current_lsas"), |
|
174 periodic_lsa_timer_(this), |
|
175 delayed_lsa_timer_(this) |
|
176 { |
|
177 // override add_nexthop_routes since it just confuses things |
|
178 config_.add_nexthop_routes_ = false; |
|
179 } |
|
180 |
|
181 //---------------------------------------------------------------------- |
|
182 void |
|
183 DTLSRRouter::initialize() |
|
184 { |
|
185 TableBasedRouter::initialize(); |
|
186 |
|
187 const EndpointID& local_eid = BundleDaemon::instance()->local_eid(); |
|
188 |
|
189 if (local_eid.scheme_str() != "dtn") { |
|
190 log_crit("cannot use DTLSR with a local eid not in the 'dtn' scheme"); |
|
191 exit(1); |
|
192 } |
|
193 |
|
194 local_node_ = graph_.add_node(local_eid.str(), NodeInfo()); |
|
195 |
|
196 EndpointIDPattern admin_eid = local_eid; |
|
197 admin_eid.append_service_tag(announce_tag_); |
|
198 reg_ = new Reg(this, admin_eid); |
|
199 |
|
200 log_info("initializing: local_eid %s weight_fn %s", |
|
201 local_eid.c_str(), |
|
202 DTLSRConfig::weight_fn_to_str(config()->weight_fn_)); |
|
203 |
|
204 switch (config()->weight_fn_) { |
|
205 case DTLSRConfig::COST: |
|
206 weight_fn_ = new CostWeightFn(); |
|
207 break; |
|
208 |
|
209 case DTLSRConfig::DELAY: |
|
210 weight_fn_ = new DelayWeightFn(); |
|
211 break; |
|
212 |
|
213 case DTLSRConfig::ESTIMATED_DELAY: |
|
214 weight_fn_ = new EstimatedDelayWeightFn(this); |
|
215 break; |
|
216 } |
|
217 |
|
218 if (config()->lsa_interval_ != 0) { |
|
219 periodic_lsa_timer_.set_interval(config()->lsa_interval_ * 1000); |
|
220 periodic_lsa_timer_.schedule_in(config()->lsa_interval_ * 1000); |
|
221 } |
|
222 } |
|
223 |
|
224 //---------------------------------------------------------------------- |
|
225 void |
|
226 DTLSRRouter::get_routing_state(oasys::StringBuffer* buf) |
|
227 { |
|
228 // invalidate_routes(); |
|
229 // recompute_routes(); |
|
230 |
|
231 buf->appendf("DTLSR Routing Graph:\n*%p", &graph_); |
|
232 buf->appendf("Current routing table:\n"); |
|
233 TableBasedRouter::get_routing_state(buf); |
|
234 } |
|
235 |
|
236 //---------------------------------------------------------------------- |
|
237 bool |
|
238 DTLSRRouter::can_delete_bundle(const BundleRef& bundle) |
|
239 { |
|
240 if (! TableBasedRouter::can_delete_bundle(bundle)) { |
|
241 return false; |
|
242 } |
|
243 |
|
244 if (current_lsas_.contains(bundle)) { |
|
245 log_debug("can_delete_bundle(%u): current lsa", bundle->bundleid()); |
|
246 return false; |
|
247 } |
|
248 |
|
249 return true; |
|
250 } |
|
251 |
|
252 //---------------------------------------------------------------------- |
|
253 void |
|
254 DTLSRRouter::delete_bundle(const BundleRef& bundle) |
|
255 { |
|
256 TableBasedRouter::delete_bundle(bundle); |
|
257 |
|
258 if (current_lsas_.contains(bundle)) |
|
259 { |
|
260 log_crit("deleting bundle id %u whilea still on current lsas list", |
|
261 bundle->bundleid()); |
|
262 current_lsas_.erase(bundle); |
|
263 } |
|
264 } |
|
265 |
|
266 //---------------------------------------------------------------------- |
|
267 void |
|
268 DTLSRRouter::handle_link_created(LinkCreatedEvent* e) |
|
269 { |
|
270 // XXX/demmer TODO: add the edge and initialize downtime_pct based |
|
271 // on the link type. |
|
272 |
|
273 invalidate_routes(); |
|
274 recompute_routes(); |
|
275 TableBasedRouter::handle_link_created(e); |
|
276 } |
|
277 |
|
278 //---------------------------------------------------------------------- |
|
279 void |
|
280 DTLSRRouter::handle_bundle_received(BundleReceivedEvent* e) |
|
281 { |
|
282 // if (time_to_age_routes()) { |
|
283 // recompute_routes(); |
|
284 // } |
|
285 |
|
286 TableBasedRouter::handle_bundle_received(e); |
|
287 } |
|
288 |
|
289 //---------------------------------------------------------------------- |
|
290 void |
|
291 DTLSRRouter::handle_bundle_expired(BundleExpiredEvent* e) |
|
292 { |
|
293 Bundle* bundle = e->bundleref_.object(); |
|
294 |
|
295 // check if this is one of the current LSAs if so, we |
|
296 // need to drop our retention constraint and let it expire |
|
297 // |
|
298 // XXX/demmer perhaps we should do something more drastic like |
|
299 // remove the node from the routing graph? |
|
300 oasys::ScopeLock l(current_lsas_.lock(), |
|
301 "DTLSRRouter::handle_bundle_expired"); |
|
302 |
|
303 if (current_lsas_.contains(bundle)) |
|
304 { |
|
305 log_notice("current lsa for %s expired... kicking links", |
|
306 bundle->source().c_str()); |
|
307 handle_lsa_expired(bundle); |
|
308 current_lsas_.erase(bundle); |
|
309 } |
|
310 |
|
311 TableBasedRouter::handle_bundle_expired(e); |
|
312 } |
|
313 |
|
314 //---------------------------------------------------------------------- |
|
315 void |
|
316 DTLSRRouter::handle_contact_up(ContactUpEvent* e) |
|
317 { |
|
318 const LinkRef& link = e->contact_->link(); |
|
319 if (link->remote_eid() == EndpointID::NULL_EID()) { |
|
320 log_warn("can't handle link %s: no remote endpoint id", |
|
321 link->name()); |
|
322 return; |
|
323 } |
|
324 |
|
325 EdgeInfo ei(link->name()); |
|
326 // XXX/demmer what about bw/delay/capacity? |
|
327 ei.params_.cost_ = link->params().cost_; |
|
328 |
|
329 RoutingGraph::Node* remote_node = |
|
330 graph_.find_node(link->remote_eid().str()); |
|
331 |
|
332 if (remote_node == NULL) { |
|
333 remote_node = graph_.add_node(link->remote_eid().str(), NodeInfo()); |
|
334 } |
|
335 |
|
336 RoutingGraph::Edge* edge = graph_.find_edge(local_node_, remote_node, ei); |
|
337 if (edge == NULL) { |
|
338 edge = graph_.add_edge(local_node_, remote_node, ei); |
|
339 } else { |
|
340 edge->mutable_info().params_.state_ = LinkParams::LINK_UP; |
|
341 } |
|
342 |
|
343 // always calculate the new routing graph now |
|
344 invalidate_routes(); |
|
345 recompute_routes(); |
|
346 |
|
347 // schedule a new lsa to include the new link |
|
348 schedule_lsa(); |
|
349 |
|
350 // now let the superclass have a turn at the event |
|
351 TableBasedRouter::handle_contact_up(e); |
|
352 } |
|
353 |
|
354 //---------------------------------------------------------------------- |
|
355 void |
|
356 DTLSRRouter::handle_contact_down(ContactDownEvent* e) |
|
357 { |
|
358 const LinkRef& link = e->contact_->link(); |
|
359 |
|
360 RoutingGraph::Node* remote_node = |
|
361 graph_.find_node(link->remote_eid().str()); |
|
362 |
|
363 if (remote_node == NULL) { |
|
364 log_err("contact down event for link %s: node not in routing graph", |
|
365 link->name()); |
|
366 return; |
|
367 } |
|
368 |
|
369 RoutingGraph::Edge* edge = NULL; |
|
370 RoutingGraph::EdgeVector::const_iterator iter; |
|
371 for (iter = local_node_->out_edges().begin(); |
|
372 iter != local_node_->out_edges().end(); ++iter) |
|
373 { |
|
374 if ((*iter)->info().id_ == link->name_str()) { |
|
375 edge = *iter; |
|
376 break; |
|
377 } |
|
378 } |
|
379 |
|
380 if (edge == NULL) { |
|
381 log_err("handle_contact_down: can't find link *%p", link.object()); |
|
382 return; |
|
383 } |
|
384 |
|
385 edge->mutable_info().params_.state_ = LinkParams::LINK_DOWN; |
|
386 |
|
387 // calculate the new routing graph |
|
388 invalidate_routes(); |
|
389 recompute_routes(); |
|
390 |
|
391 // inform peers |
|
392 schedule_lsa(); |
|
393 |
|
394 if (! config()->keep_down_links_) { |
|
395 remove_edge(edge); |
|
396 } |
|
397 |
|
398 TableBasedRouter::handle_contact_down(e); |
|
399 } |
|
400 |
|
401 //---------------------------------------------------------------------- |
|
402 void |
|
403 DTLSRRouter::handle_link_deleted(LinkDeletedEvent* e) |
|
404 { |
|
405 TableBasedRouter::handle_link_deleted(e); |
|
406 NOTREACHED; |
|
407 |
|
408 // XXX/demmer fixme |
|
409 |
|
410 RoutingGraph::EdgeVector::const_iterator iter; |
|
411 for (iter = local_node_->out_edges().begin(); |
|
412 iter != local_node_->out_edges().end(); ++iter) |
|
413 { |
|
414 if ((*iter)->info().id_ == e->link_->name()) { |
|
415 remove_edge(*iter); |
|
416 return; |
|
417 } |
|
418 } |
|
419 |
|
420 log_err("handle_link_deleted: can't find link *%p", e->link_.object()); |
|
421 } |
|
422 |
|
423 //---------------------------------------------------------------------- |
|
424 void |
|
425 DTLSRRouter::handle_registration_added(RegistrationAddedEvent* event) |
|
426 { |
|
427 Registration* reg = event->registration_; |
|
428 const EndpointID& local_eid = BundleDaemon::instance()->local_eid(); |
|
429 |
|
430 TableBasedRouter::handle_registration_added(event); |
|
431 |
|
432 // to handle registration for endpoint identifiers that aren't |
|
433 // covered by our local_eid pattern, we add a node to the graph |
|
434 // with an infinite-bandwidth edge from our local node to it. |
|
435 // |
|
436 // session registrations don't get announced, except for custody |
|
437 // ones which get the 'dtn-session:' prefix added on |
|
438 if (reg->endpoint().subsume(local_eid)) { |
|
439 return; // nothing to do |
|
440 } |
|
441 |
|
442 std::string eid; |
|
443 if (reg->session_flags() == 0) |
|
444 { |
|
445 eid = reg->endpoint().str(); |
|
446 } |
|
447 else if (reg->session_flags() & Session::CUSTODY) |
|
448 { |
|
449 eid = std::string("dtn-session:") + reg->endpoint().str(); |
|
450 } |
|
451 else |
|
452 { |
|
453 return; // ignore non-custody registrations |
|
454 } |
|
455 |
|
456 RoutingGraph::Node* node = graph_.find_node(eid); |
|
457 if (node == NULL) { |
|
458 log_debug("handle_registration added: adding new graph node for %s", |
|
459 eid.c_str()); |
|
460 node = graph_.add_node(eid, NodeInfo()); |
|
461 } |
|
462 |
|
463 EdgeInfo ei(std::string("reg-") + eid); |
|
464 ei.is_registration_ = true; |
|
465 ei.params_.bw_ = 0xffffffff; |
|
466 |
|
467 RoutingGraph::Edge* edge = graph_.find_edge(local_node_, node, ei); |
|
468 if (edge == NULL) { |
|
469 log_debug("handle_registration added: adding new edge node for %s", |
|
470 ei.id_.c_str()); |
|
471 edge = graph_.add_edge(local_node_, node, ei); |
|
472 |
|
473 // send a new lsa to announce the new edge |
|
474 schedule_lsa(); |
|
475 } |
|
476 } |
|
477 |
|
478 //---------------------------------------------------------------------- |
|
479 void |
|
480 DTLSRRouter::remove_edge(RoutingGraph::Edge* edge) |
|
481 { |
|
482 log_debug("remove_edge %s", edge->info().id_.c_str()); |
|
483 bool ok = graph_.del_edge(local_node_, edge); |
|
484 ASSERT(ok); |
|
485 } |
|
486 |
|
487 //---------------------------------------------------------------------- |
|
488 bool |
|
489 DTLSRRouter::time_to_age_routes() |
|
490 { |
|
491 u_int32_t elapsed = last_update_.elapsed_ms() / 1000; |
|
492 if (elapsed > config()->aging_interval_) { |
|
493 return true; |
|
494 } |
|
495 return false; |
|
496 } |
|
497 |
|
498 //---------------------------------------------------------------------- |
|
499 void |
|
500 DTLSRRouter::invalidate_routes() |
|
501 { |
|
502 log_debug("invalidating routes"); |
|
503 last_update_.sec_ = 0; |
|
504 } |
|
505 |
|
506 //---------------------------------------------------------------------- |
|
507 bool |
|
508 DTLSRRouter::is_dynamic_route(RouteEntry* entry) |
|
509 { |
|
510 if (entry->info() == NULL) { |
|
511 return false; |
|
512 } |
|
513 |
|
514 RouteInfo* info = dynamic_cast<RouteInfo*>(entry->info()); |
|
515 if (info != NULL) { |
|
516 return true; |
|
517 } |
|
518 |
|
519 return false; |
|
520 } |
|
521 |
|
522 //---------------------------------------------------------------------- |
|
523 void |
|
524 DTLSRRouter::recompute_routes() |
|
525 { |
|
526 // // XXX/demmer make this a parameter |
|
527 // u_int32_t elapsed = last_update_.elapsed_ms() / 1000; |
|
528 // if (elapsed < config()->recompute_delay_) { |
|
529 // log_debug("not recomputing routes since %u ms elapsed since last update", |
|
530 // elapsed); |
|
531 // return; |
|
532 // } |
|
533 |
|
534 log_debug("recomputing all routes"); |
|
535 last_update_.get_time(); |
|
536 |
|
537 route_table_->del_matching_entries(is_dynamic_route); |
|
538 |
|
539 // loop through all the nodes in the graph, finding the right |
|
540 // route and re-adding it |
|
541 RoutingGraph::NodeVector::const_iterator iter; |
|
542 for (iter = graph_.nodes().begin(); iter != graph_.nodes().end(); ++iter) { |
|
543 const RoutingGraph::Node* dest = *iter; |
|
544 |
|
545 if (dest == local_node_) { |
|
546 continue; |
|
547 } |
|
548 |
|
549 EndpointIDPattern dest_eid(dest->id()); |
|
550 dest_eid.append_service_tag("*"); |
|
551 ASSERT(dest_eid.valid()); |
|
552 |
|
553 // XXX/demmer this should include more criteria for |
|
554 // classification, i.e. the priority class, perhaps the size |
|
555 // limit, etc |
|
556 RoutingGraph::Edge* edge = graph_.best_next_hop(local_node_, dest, |
|
557 weight_fn_); |
|
558 if (edge == NULL) { |
|
559 // log_warn("no route to destination %s", dest->id().c_str()); |
|
560 continue; |
|
561 } |
|
562 |
|
563 ContactManager* cm = BundleDaemon::instance()->contactmgr(); |
|
564 LinkRef link = cm->find_link(edge->info().id_.c_str()); |
|
565 if (link == NULL) { |
|
566 |
|
567 // it's possible that this is a local registration link, |
|
568 // so just ignore it |
|
569 log_debug("link %s not in local link table... ignoring it", |
|
570 edge->info().id_.c_str()); |
|
571 continue; |
|
572 } |
|
573 |
|
574 log_debug("recompute_routes: adding route for %s to link %s", |
|
575 dest_eid.c_str(), link->name()); |
|
576 |
|
577 // XXX/demmer shouldn't this call check_next_hop?? |
|
578 RouteEntry* e = new RouteEntry(dest_eid, link); |
|
579 e->set_info(new RouteInfo(edge)); |
|
580 route_table_->add_entry(e); |
|
581 } |
|
582 |
|
583 // go through all bundles and re-route them |
|
584 handle_changed_routes(); |
|
585 } |
|
586 |
|
587 //---------------------------------------------------------------------- |
|
588 DTLSRRouter::Reg::Reg(DTLSRRouter* router, |
|
589 const EndpointIDPattern& eid) |
|
590 : Registration(DTLSR_REGID, eid, Registration::DEFER, |
|
591 /* XXX/demmer session_flags */ 0, 0), |
|
592 |
|
593 router_(router) |
|
594 { |
|
595 logpathf("%s/reg", router->logpath()), |
|
596 BundleDaemon::post(new RegistrationAddedEvent(this, EVENTSRC_ADMIN)); |
|
597 } |
|
598 |
|
599 //---------------------------------------------------------------------- |
|
600 void |
|
601 DTLSRRouter::Reg::deliver_bundle(Bundle* bundle) |
|
602 { |
|
603 // XXX/demmer yuck |
|
604 if (bundle->source() == BundleDaemon::instance()->local_eid()) { |
|
605 log_info("ignoring bundle sent by self"); |
|
606 return; |
|
607 } |
|
608 |
|
609 u_char type; |
|
610 bundle->payload().read_data(0, 1, &type); |
|
611 |
|
612 if (type == DTLSR::MSG_LSA) { |
|
613 log_info("got LSA bundle *%p",bundle); |
|
614 |
|
615 LSA lsa; |
|
616 if (!DTLSR::parse_lsa_bundle(bundle, &lsa)) { |
|
617 log_warn("error parsing LSA"); |
|
618 goto bail; |
|
619 } |
|
620 |
|
621 router_->handle_lsa(bundle, &lsa); |
|
622 } else { |
|
623 log_err("unknown message type %d", type); |
|
624 } |
|
625 |
|
626 bail: |
|
627 BundleDaemon::post_at_head(new BundleDeliveredEvent(bundle, this)); |
|
628 } |
|
629 |
|
630 //---------------------------------------------------------------------- |
|
631 bool |
|
632 DTLSRRouter::update_current_lsa(RoutingGraph::Node* node, |
|
633 Bundle* bundle, u_int64_t seqno) |
|
634 { |
|
635 bool found_stale_lsa = false; |
|
636 if (seqno <= node->info().last_lsa_seqno_ && |
|
637 bundle->creation_ts().seconds_ < node->info().last_lsa_creation_ts_) |
|
638 { |
|
639 log_info("update_current_lsa: " |
|
640 "ignoring stale LSA (seqno %llu <= last %llu, " |
|
641 "creation_ts %llu <= last %llu)", |
|
642 seqno, node->info().last_lsa_seqno_, |
|
643 bundle->creation_ts().seconds_, |
|
644 node->info().last_lsa_creation_ts_); |
|
645 |
|
646 // suppress forwarding of the stale LSA |
|
647 bundle->fwdlog()->add_entry(EndpointIDPattern::WILDCARD_EID(), |
|
648 ForwardingInfo::FORWARD_ACTION, |
|
649 ForwardingInfo::SUPPRESSED); |
|
650 |
|
651 return false; |
|
652 } |
|
653 else |
|
654 { |
|
655 log_info("update_current_lsa: " |
|
656 "got new LSA (seqno %llu > last %llu || " |
|
657 "creation_ts %llu > last %llu)", |
|
658 seqno, node->info().last_lsa_seqno_, |
|
659 bundle->creation_ts().seconds_, |
|
660 node->info().last_lsa_creation_ts_); |
|
661 } |
|
662 |
|
663 oasys::ScopeLock l(current_lsas_.lock(), |
|
664 "DTLSRRouter::update_current_lsa"); |
|
665 for (BundleList::iterator iter = current_lsas_.begin(); |
|
666 iter != current_lsas_.end(); ++iter) |
|
667 { |
|
668 if ((*iter)->source() == node->id()) { |
|
669 // be careful not to let the bundle reference count drop |
|
670 // to zero by keeping a local reference until we can make |
|
671 // sure it's at least in the NotNeededEvent |
|
672 BundleRef stale_lsa("DTLSRRouter::update_current_lsa"); |
|
673 stale_lsa = *iter; |
|
674 |
|
675 current_lsas_.erase(iter); |
|
676 |
|
677 log_debug("cancelling pending transmissions for *%p", |
|
678 stale_lsa.object()); |
|
679 |
|
680 stale_lsa->fwdlog()->add_entry(EndpointIDPattern::WILDCARD_EID(), |
|
681 ForwardingInfo::FORWARD_ACTION, |
|
682 ForwardingInfo::SUPPRESSED); |
|
683 |
|
684 BundleDaemon::post_at_head( |
|
685 new BundleDeleteRequest(stale_lsa.object(), |
|
686 BundleProtocol::REASON_NO_ADDTL_INFO)); |
|
687 found_stale_lsa = true; |
|
688 |
|
689 // oasys::StringBuffer buf("Stale LSA: "); |
|
690 // bundle->format_verbose(&buf); |
|
691 // log_multiline(oasys::LOG_INFO, buf.c_str()); |
|
692 |
|
693 if (node->info().last_lsa_seqno_ == 0) { |
|
694 NOTREACHED; |
|
695 } |
|
696 break; |
|
697 } |
|
698 } |
|
699 |
|
700 if (node->info().last_lsa_seqno_ == 0) { |
|
701 ASSERT(!found_stale_lsa); |
|
702 log_info("update_current_lsa: " |
|
703 "first LSA from %s (seqno %llu)", |
|
704 node->id().c_str(), seqno); |
|
705 } else { |
|
706 log_info("update_current_lsa: " |
|
707 "replaced %s LSA from %s (seqno %llu) with latest (%llu)", |
|
708 found_stale_lsa ? "stale" : "expired", |
|
709 node->id().c_str(), node->info().last_lsa_seqno_, seqno); |
|
710 } |
|
711 |
|
712 node->mutable_info().last_lsa_seqno_ = seqno; |
|
713 node->mutable_info().last_lsa_creation_ts_ = bundle->creation_ts().seconds_; |
|
714 current_lsas_.push_back(bundle); |
|
715 return true; |
|
716 } |
|
717 |
|
718 //---------------------------------------------------------------------- |
|
719 void |
|
720 DTLSRRouter::handle_lsa(Bundle* bundle, LSA* lsa) |
|
721 { |
|
722 // First check the LSA bundle destination to see if the sender is |
|
723 // in a different area |
|
724 std::string lsa_area = bundle->dest().uri().query_value("area"); |
|
725 if (config()->area_ != lsa_area) { |
|
726 log_debug("handle_lsa: ignoring LSA since area %s != local area %s", |
|
727 lsa_area.c_str(), config()->area_.c_str()); |
|
728 |
|
729 bundle->fwdlog()->add_entry(EndpointIDPattern::WILDCARD_EID(), |
|
730 ForwardingInfo::FORWARD_ACTION, |
|
731 ForwardingInfo::SUPPRESSED); |
|
732 return; |
|
733 } |
|
734 |
|
735 const EndpointID& source = bundle->source(); |
|
736 |
|
737 RoutingGraph::Node* a = graph_.find_node(source.str()); |
|
738 if (a == NULL) { |
|
739 log_debug("handle_lsa: adding new source node %s", |
|
740 source.c_str()); |
|
741 a = graph_.add_node(source.str(), NodeInfo()); |
|
742 } |
|
743 |
|
744 if (! update_current_lsa(a, bundle, lsa->seqno_)) { |
|
745 return; // stale lsa |
|
746 } |
|
747 |
|
748 // don't send the LSA back where we got it from |
|
749 ForwardingInfo info; |
|
750 if (bundle->fwdlog()->get_latest_entry(ForwardingInfo::RECEIVED, &info)) |
|
751 { |
|
752 log_debug("handle_lsa: suppressing transmission back to %s", |
|
753 info.remote_eid().c_str()); |
|
754 bundle->fwdlog()->add_entry(info.remote_eid(), |
|
755 ForwardingInfo::FORWARD_ACTION, |
|
756 ForwardingInfo::SUPPRESSED); |
|
757 } |
|
758 |
|
759 // XXX/demmer here we should drop all the links that aren't |
|
760 // present in the LSA... |
|
761 |
|
762 // Handle all the link announcements |
|
763 for (LinkStateVec::iterator iter = lsa->links_.begin(); |
|
764 iter != lsa->links_.end(); ++iter) |
|
765 { |
|
766 LinkState* ls = &(*iter); |
|
767 |
|
768 // check for the case where we're re-receiving an LSA that was |
|
769 // sent previously, but our link configuration has changed so |
|
770 // we no longer have the link |
|
771 if (a == local_node_) { |
|
772 LinkRef link("DTLSRRouter::handle_lsa"); |
|
773 link = BundleDaemon::instance()->contactmgr()-> |
|
774 find_link(ls->id_.c_str()); |
|
775 if (link == NULL) { |
|
776 log_warn("local link %s in LSA but no longer exists, ignoring...", |
|
777 ls->id_.c_str()); |
|
778 continue; |
|
779 } |
|
780 } |
|
781 |
|
782 // find or add the node |
|
783 RoutingGraph::Node* b = graph_.find_node(ls->dest_.str()); |
|
784 if (!b) { |
|
785 log_debug("handle_lsa: %s adding new dest node %s", |
|
786 source.c_str(), ls->dest_.c_str()); |
|
787 b = graph_.add_node(ls->dest_.str(), NodeInfo()); |
|
788 } |
|
789 |
|
790 // try to find and update the edge in the graph, otherwise add it |
|
791 RoutingGraph::Edge *e = NULL; |
|
792 RoutingGraph::EdgeVector::const_iterator ei; |
|
793 for (ei = a->out_edges().begin(); ei != a->out_edges().end(); ++ei) { |
|
794 if ((*ei)->info().id_ == ls->id_) { |
|
795 e = *ei; |
|
796 break; |
|
797 } |
|
798 } |
|
799 |
|
800 if (e == NULL) { |
|
801 e = graph_.add_edge(a, b, EdgeInfo(ls->id_, ls->params_)); |
|
802 |
|
803 log_debug("handle_lsa: added new edge %s from %s -> %s", |
|
804 oasys::InlineFormatter<EdgeInfo>().format(e->info()), |
|
805 a->id().c_str(), b->id().c_str()); |
|
806 } else { |
|
807 e->mutable_info().params_ = ls->params_; |
|
808 |
|
809 log_debug("handle_lsa: updated edge %s from %s -> %s", |
|
810 oasys::InlineFormatter<EdgeInfo>().format(e->info()), |
|
811 a->id().c_str(), b->id().c_str()); |
|
812 } |
|
813 |
|
814 // XXX/demmer fold this into a parameter update method |
|
815 e->mutable_info().last_update_.get_time(); |
|
816 } |
|
817 |
|
818 recompute_routes(); |
|
819 } |
|
820 |
|
821 //---------------------------------------------------------------------- |
|
822 void |
|
823 DTLSRRouter::handle_lsa_expired(Bundle* bundle) |
|
824 { |
|
825 // XXX/demmer this is bogus -- we don't want to drop all routes |
|
826 // just b/c we lost an LSA in all cases, only when we're using the |
|
827 // long-lived LSA bundles |
|
828 (void)bundle; |
|
829 |
|
830 /* |
|
831 const EndpointID& source = bundle->source_; |
|
832 |
|
833 RoutingGraph::Node* a = graph_.find_node(source.str()); |
|
834 ASSERT(a != NULL); |
|
835 |
|
836 RoutingGraph::EdgeVector::const_iterator ei; |
|
837 for (ei = a->out_edges().begin(); ei != a->out_edges().end(); ++ei) { |
|
838 (*ei)->mutable_info().params_.state_ = LinkParams::LINK_DOWN; |
|
839 } |
|
840 |
|
841 recompute_routes(); |
|
842 */ |
|
843 } |
|
844 |
|
845 //---------------------------------------------------------------------- |
|
846 void |
|
847 DTLSRRouter::generate_link_state(LinkState* ls, |
|
848 RoutingGraph::Edge* edge, |
|
849 const LinkRef& link) |
|
850 { |
|
851 ls->dest_.assign(edge->dest()->id()); |
|
852 ls->id_.assign(edge->info().id_); |
|
853 ls->params_ = edge->info().params_; |
|
854 |
|
855 // XXX/demmer maybe the edge info should be tied to the link |
|
856 // itself somehow? |
|
857 if (link != NULL) { |
|
858 ls->params_.qcount_ = link->bundles_queued(); |
|
859 ls->params_.qsize_ = link->bytes_queued(); |
|
860 } |
|
861 |
|
862 if (edge->info().last_update_.sec_ != 0) { |
|
863 ls->elapsed_ = edge->info().last_update_.elapsed_ms(); |
|
864 } else { |
|
865 ls->elapsed_ = 0; |
|
866 } |
|
867 edge->mutable_info().last_update_.get_time(); |
|
868 } |
|
869 |
|
870 //---------------------------------------------------------------------- |
|
871 void |
|
872 DTLSRRouter::TransmitLSATimer::timeout(const struct timeval& now) |
|
873 { |
|
874 (void)now; |
|
875 router_->send_lsa(); |
|
876 if (interval_ != 0) { |
|
877 schedule_in(interval_); |
|
878 } |
|
879 } |
|
880 |
|
881 //---------------------------------------------------------------------- |
|
882 void |
|
883 DTLSRRouter::schedule_lsa() |
|
884 { |
|
885 if (delayed_lsa_timer_.pending()) { |
|
886 log_debug("schedule_lsa: delayed LSA transmission already pending"); |
|
887 return; |
|
888 } |
|
889 |
|
890 u_int elapsed = (oasys::Time::now() - last_lsa_transmit_).sec_; |
|
891 if (elapsed > config()->min_lsa_interval_) |
|
892 { |
|
893 log_debug("schedule_lsa: %u seconds since last LSA transmission, " |
|
894 "sending one immediately", elapsed); |
|
895 send_lsa(); |
|
896 } |
|
897 else |
|
898 { |
|
899 u_int delay = std::min(config()->min_lsa_interval_, |
|
900 config()->min_lsa_interval_ - elapsed); |
|
901 log_debug("schedule_lsa: %u seconds since last LSA transmission, " |
|
902 "min interval %u, delaying LSA for %u seconds", |
|
903 elapsed, config()->min_lsa_interval_, delay); |
|
904 delayed_lsa_timer_.schedule_in(delay * 1000); |
|
905 } |
|
906 } |
|
907 |
|
908 //---------------------------------------------------------------------- |
|
909 void |
|
910 DTLSRRouter::send_lsa() |
|
911 { |
|
912 char tmp[64]; (void)tmp; |
|
913 LSA lsa; |
|
914 |
|
915 // the contents of last_lsa_seqno get updated in the call to |
|
916 // update_current_lsa below |
|
917 lsa.seqno_ = local_node_->info().last_lsa_seqno_ + 1; |
|
918 |
|
919 RoutingGraph::EdgeVector::iterator ei; |
|
920 for (ei = local_node_->out_edges().begin(); |
|
921 ei != local_node_->out_edges().end(); |
|
922 ++ei) |
|
923 { |
|
924 LinkState ls; |
|
925 LinkRef link("DTLSRRouter::send_lsa"); |
|
926 |
|
927 // if the edge is a local registration, there's no link, we |
|
928 // just pretend there's a zero-delay, infinite-bandwidth link |
|
929 // to the other endpoint |
|
930 if (! (*ei)->info().is_registration_) { |
|
931 link = BundleDaemon::instance()->contactmgr()-> |
|
932 find_link((*ei)->info().id_.c_str()); |
|
933 ASSERT(link != NULL); |
|
934 } |
|
935 |
|
936 generate_link_state(&ls, *ei, link); |
|
937 lsa.links_.push_back(ls); |
|
938 } |
|
939 |
|
940 log_debug("send_lsa: generated %zu link states for local node", |
|
941 lsa.links_.size()); |
|
942 |
|
943 Bundle* bundle = new TempBundle(); |
|
944 |
|
945 if (config()->area_ != "") { |
|
946 snprintf(tmp, sizeof(tmp), "dtn://*/%s?area=%s;lsa_seqno=%llu", |
|
947 announce_tag_, config()->area_.c_str(), lsa.seqno_); |
|
948 } else { |
|
949 snprintf(tmp, sizeof(tmp), "dtn://*/%s?lsa_seqno=%llu", |
|
950 announce_tag_, lsa.seqno_); |
|
951 } |
|
952 bundle->mutable_dest()->assign(tmp); |
|
953 |
|
954 bundle->mutable_source()->assign(BundleDaemon::instance()->local_eid()); |
|
955 bundle->mutable_replyto()->assign(EndpointID::NULL_EID()); |
|
956 bundle->mutable_custodian()->assign(EndpointID::NULL_EID()); |
|
957 bundle->set_expiration(config()->lsa_lifetime_); |
|
958 bundle->set_singleton_dest(false); |
|
959 DTLSR::format_lsa_bundle(bundle, &lsa); |
|
960 |
|
961 update_current_lsa(local_node_, bundle, lsa.seqno_); |
|
962 |
|
963 log_debug("send_lsa: formatted LSA bundle *%p", bundle); |
|
964 |
|
965 // XXX/demmer this would really be better done with |
|
966 // BundleActions::inject_bundle |
|
967 BundleDaemon::post_at_head(new BundleReceivedEvent(bundle, EVENTSRC_ROUTER)); |
|
968 |
|
969 last_lsa_transmit_.get_time(); |
|
970 } |
|
971 |
|
972 } // namespace dtn |
|
973 |