servlib/routing/DTLSRRouter.cc
changeset 0 2b3e5ec03512
equal deleted inserted replaced
-1:000000000000 0:2b3e5ec03512
       
     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