diff -r 000000000000 -r 2b3e5ec03512 servlib/routing/DTLSRRouter.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlib/routing/DTLSRRouter.cc Thu Apr 21 14:57:45 2011 +0100 @@ -0,0 +1,973 @@ +/* + * Copyright 2007 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "DTLSRRouter.h" +#include "bundling/BundleDaemon.h" +#include "bundling/SDNV.h" +#include "bundling/TempBundle.h" +#include "contacts/ContactManager.h" +#include "routing/RouteTable.h" +#include "session/Session.h" + +namespace oasys { + +//---------------------------------------------------------------------- +template<> +const char* +InlineFormatter + ::format(const dtn::DTLSRRouter::EdgeInfo& ei) +{ + const char* state_str; + if (ei.params_.state_ == dtn::DTLSRRouter::LinkParams::LINK_UP) { + state_str = "UP"; + } else if (ei.params_.state_ == dtn::DTLSRRouter::LinkParams::LINK_DOWN) { + state_str = "DOWN"; + } else { + state_str = "__UNKNOWN__"; + } + + buf_.appendf("%s: state=%s cost=%u delay=%u bw=%u", + ei.id_.c_str(), state_str, + ei.params_.cost_, ei.params_.delay_, ei.params_.bw_); + return buf_.c_str(); +} + +} // namespace oasys + +namespace dtn { + +//---------------------------------------------------------------------- +class DTLSRRouter::CostWeightFn : public RoutingGraph::WeightFn { +public: + u_int32_t operator()(const RoutingGraph::SearchInfo& info, + const RoutingGraph::Edge* edge) + { + (void)info; + if (edge->info().params_.state_ == LinkParams::LINK_DOWN) { + return 0xffffffff; + } + return edge->info().params_.cost_; + } +}; + +//---------------------------------------------------------------------- +class DTLSRRouter::DelayWeightFn : public RoutingGraph::WeightFn { +public: + u_int32_t operator()(const RoutingGraph::SearchInfo& info, + const RoutingGraph::Edge* edge) + { + (void)info; + + u_int32_t downtime = (info.now_ - edge->info().last_update_).sec_; + if (DTLSRConfig::instance()->lsa_interval_ != 0 && + downtime > (2 * DTLSRConfig::instance()->lsa_interval_)) + { + // don't know anything implies the link is "down" + return 0xffffffff; + } + + if (edge->info().params_.state_ == LinkParams::LINK_DOWN) { + return 0xffffffff; + } + return edge->info().params_.delay_; + } +}; + +//---------------------------------------------------------------------- +class DTLSRRouter::EstimatedDelayWeightFn : public RoutingGraph::WeightFn { +public: + EstimatedDelayWeightFn(DTLSRRouter* rtr) + : router_(rtr) {} + + u_int32_t operator()(const RoutingGraph::SearchInfo& info, + const RoutingGraph::Edge* edge) + { + if (edge->info().params_.state_ == LinkParams::LINK_DOWN) { + + // adjust the delay based on the uptime / downtime + u_int32_t downtime = (info.now_ - edge->info().last_update_).sec_; + + // XXX/demmer for now make the bogus silly assumption that + // it will take just as long to come back up as it's been + // down for, plus a constant factor of 5 seconds to + // establish the link, capped at 1 day + // + // really, we need a good way of succinctly characterizing + // delay in a way that can be decayed but also take into + // account historical data, i.e. a number that + // grows/shrinks as the link gets worse but where a router + // can take into account that some outages are expected + u_int32_t ret = (downtime + 5) >> DTLSRConfig::instance()->weight_shift_; + + if (ret < 24*60*60) { + log_debug_p("/dtn/route/graph", + "weight of edge %s=%u (downtime %u + 5)", + edge->info().id_.c_str(), ret, downtime); + } else { + ret = 24*60*60; + log_warn_p("/dtn/route/graph", + "weight of edge %s=%u: (downtime %u exceeded max)", + edge->info().id_.c_str(), ret, downtime); + } + + return ret; + } + + u_int32_t qlen, qsize; + // XXX/demmer use the link queue length if it's a local link + qlen = edge->info().params_.qcount_; + qsize = edge->info().params_.qsize_; + + // based on the queue length and link estimates, calculate the + // forwarding delay + u_int32_t qdelay = (qlen + 1) * edge->info().params_.delay_ / 1000; + u_int32_t bwdelay = 0; + if (edge->info().params_.bw_ != 0) { + // XXX/demmer is bw bits or bytes? + bwdelay = qsize / edge->info().params_.bw_; + } + + u_int32_t delay = qdelay + bwdelay; + log_debug_p("/dtn/route/graph", "weight of edge %s=%u " + "(qlen %u delay %u qdelay %u qsize %u bw %u bwdelay %u)", + edge->info().id_.c_str(), delay, + qlen, edge->info().params_.delay_, qdelay, + qsize, edge->info().params_.bw_, bwdelay); + + // XXX/demmer we might want to have an estimator for bundles + // that we've sent out en route to the destination along with + // where and when those bundles will be at different routers. + // then when LSAs come in, we adjust our estimates accordingly + + return delay; + } + + DTLSRRouter* router_; +}; + +//---------------------------------------------------------------------- +DTLSRRouter::DTLSRRouter() + : TableBasedRouter("DTLSRRouter", "dtlsr"), + announce_tag_("dtlsr"), + announce_eid_("dtn://*/dtlsr?*"), + current_lsas_("DTLSRRouter::current_lsas"), + periodic_lsa_timer_(this), + delayed_lsa_timer_(this) +{ + // override add_nexthop_routes since it just confuses things + config_.add_nexthop_routes_ = false; +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::initialize() +{ + TableBasedRouter::initialize(); + + const EndpointID& local_eid = BundleDaemon::instance()->local_eid(); + + if (local_eid.scheme_str() != "dtn") { + log_crit("cannot use DTLSR with a local eid not in the 'dtn' scheme"); + exit(1); + } + + local_node_ = graph_.add_node(local_eid.str(), NodeInfo()); + + EndpointIDPattern admin_eid = local_eid; + admin_eid.append_service_tag(announce_tag_); + reg_ = new Reg(this, admin_eid); + + log_info("initializing: local_eid %s weight_fn %s", + local_eid.c_str(), + DTLSRConfig::weight_fn_to_str(config()->weight_fn_)); + + switch (config()->weight_fn_) { + case DTLSRConfig::COST: + weight_fn_ = new CostWeightFn(); + break; + + case DTLSRConfig::DELAY: + weight_fn_ = new DelayWeightFn(); + break; + + case DTLSRConfig::ESTIMATED_DELAY: + weight_fn_ = new EstimatedDelayWeightFn(this); + break; + } + + if (config()->lsa_interval_ != 0) { + periodic_lsa_timer_.set_interval(config()->lsa_interval_ * 1000); + periodic_lsa_timer_.schedule_in(config()->lsa_interval_ * 1000); + } +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::get_routing_state(oasys::StringBuffer* buf) +{ +// invalidate_routes(); +// recompute_routes(); + + buf->appendf("DTLSR Routing Graph:\n*%p", &graph_); + buf->appendf("Current routing table:\n"); + TableBasedRouter::get_routing_state(buf); +} + +//---------------------------------------------------------------------- +bool +DTLSRRouter::can_delete_bundle(const BundleRef& bundle) +{ + if (! TableBasedRouter::can_delete_bundle(bundle)) { + return false; + } + + if (current_lsas_.contains(bundle)) { + log_debug("can_delete_bundle(%u): current lsa", bundle->bundleid()); + return false; + } + + return true; +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::delete_bundle(const BundleRef& bundle) +{ + TableBasedRouter::delete_bundle(bundle); + + if (current_lsas_.contains(bundle)) + { + log_crit("deleting bundle id %u whilea still on current lsas list", + bundle->bundleid()); + current_lsas_.erase(bundle); + } +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::handle_link_created(LinkCreatedEvent* e) +{ + // XXX/demmer TODO: add the edge and initialize downtime_pct based + // on the link type. + + invalidate_routes(); + recompute_routes(); + TableBasedRouter::handle_link_created(e); +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::handle_bundle_received(BundleReceivedEvent* e) +{ +// if (time_to_age_routes()) { +// recompute_routes(); +// } + + TableBasedRouter::handle_bundle_received(e); +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::handle_bundle_expired(BundleExpiredEvent* e) +{ + Bundle* bundle = e->bundleref_.object(); + + // check if this is one of the current LSAs if so, we + // need to drop our retention constraint and let it expire + // + // XXX/demmer perhaps we should do something more drastic like + // remove the node from the routing graph? + oasys::ScopeLock l(current_lsas_.lock(), + "DTLSRRouter::handle_bundle_expired"); + + if (current_lsas_.contains(bundle)) + { + log_notice("current lsa for %s expired... kicking links", + bundle->source().c_str()); + handle_lsa_expired(bundle); + current_lsas_.erase(bundle); + } + + TableBasedRouter::handle_bundle_expired(e); +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::handle_contact_up(ContactUpEvent* e) +{ + const LinkRef& link = e->contact_->link(); + if (link->remote_eid() == EndpointID::NULL_EID()) { + log_warn("can't handle link %s: no remote endpoint id", + link->name()); + return; + } + + EdgeInfo ei(link->name()); + // XXX/demmer what about bw/delay/capacity? + ei.params_.cost_ = link->params().cost_; + + RoutingGraph::Node* remote_node = + graph_.find_node(link->remote_eid().str()); + + if (remote_node == NULL) { + remote_node = graph_.add_node(link->remote_eid().str(), NodeInfo()); + } + + RoutingGraph::Edge* edge = graph_.find_edge(local_node_, remote_node, ei); + if (edge == NULL) { + edge = graph_.add_edge(local_node_, remote_node, ei); + } else { + edge->mutable_info().params_.state_ = LinkParams::LINK_UP; + } + + // always calculate the new routing graph now + invalidate_routes(); + recompute_routes(); + + // schedule a new lsa to include the new link + schedule_lsa(); + + // now let the superclass have a turn at the event + TableBasedRouter::handle_contact_up(e); +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::handle_contact_down(ContactDownEvent* e) +{ + const LinkRef& link = e->contact_->link(); + + RoutingGraph::Node* remote_node = + graph_.find_node(link->remote_eid().str()); + + if (remote_node == NULL) { + log_err("contact down event for link %s: node not in routing graph", + link->name()); + return; + } + + RoutingGraph::Edge* edge = NULL; + RoutingGraph::EdgeVector::const_iterator iter; + for (iter = local_node_->out_edges().begin(); + iter != local_node_->out_edges().end(); ++iter) + { + if ((*iter)->info().id_ == link->name_str()) { + edge = *iter; + break; + } + } + + if (edge == NULL) { + log_err("handle_contact_down: can't find link *%p", link.object()); + return; + } + + edge->mutable_info().params_.state_ = LinkParams::LINK_DOWN; + + // calculate the new routing graph + invalidate_routes(); + recompute_routes(); + + // inform peers + schedule_lsa(); + + if (! config()->keep_down_links_) { + remove_edge(edge); + } + + TableBasedRouter::handle_contact_down(e); +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::handle_link_deleted(LinkDeletedEvent* e) +{ + TableBasedRouter::handle_link_deleted(e); + NOTREACHED; + + // XXX/demmer fixme + + RoutingGraph::EdgeVector::const_iterator iter; + for (iter = local_node_->out_edges().begin(); + iter != local_node_->out_edges().end(); ++iter) + { + if ((*iter)->info().id_ == e->link_->name()) { + remove_edge(*iter); + return; + } + } + + log_err("handle_link_deleted: can't find link *%p", e->link_.object()); +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::handle_registration_added(RegistrationAddedEvent* event) +{ + Registration* reg = event->registration_; + const EndpointID& local_eid = BundleDaemon::instance()->local_eid(); + + TableBasedRouter::handle_registration_added(event); + + // to handle registration for endpoint identifiers that aren't + // covered by our local_eid pattern, we add a node to the graph + // with an infinite-bandwidth edge from our local node to it. + // + // session registrations don't get announced, except for custody + // ones which get the 'dtn-session:' prefix added on + if (reg->endpoint().subsume(local_eid)) { + return; // nothing to do + } + + std::string eid; + if (reg->session_flags() == 0) + { + eid = reg->endpoint().str(); + } + else if (reg->session_flags() & Session::CUSTODY) + { + eid = std::string("dtn-session:") + reg->endpoint().str(); + } + else + { + return; // ignore non-custody registrations + } + + RoutingGraph::Node* node = graph_.find_node(eid); + if (node == NULL) { + log_debug("handle_registration added: adding new graph node for %s", + eid.c_str()); + node = graph_.add_node(eid, NodeInfo()); + } + + EdgeInfo ei(std::string("reg-") + eid); + ei.is_registration_ = true; + ei.params_.bw_ = 0xffffffff; + + RoutingGraph::Edge* edge = graph_.find_edge(local_node_, node, ei); + if (edge == NULL) { + log_debug("handle_registration added: adding new edge node for %s", + ei.id_.c_str()); + edge = graph_.add_edge(local_node_, node, ei); + + // send a new lsa to announce the new edge + schedule_lsa(); + } +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::remove_edge(RoutingGraph::Edge* edge) +{ + log_debug("remove_edge %s", edge->info().id_.c_str()); + bool ok = graph_.del_edge(local_node_, edge); + ASSERT(ok); +} + +//---------------------------------------------------------------------- +bool +DTLSRRouter::time_to_age_routes() +{ + u_int32_t elapsed = last_update_.elapsed_ms() / 1000; + if (elapsed > config()->aging_interval_) { + return true; + } + return false; +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::invalidate_routes() +{ + log_debug("invalidating routes"); + last_update_.sec_ = 0; +} + +//---------------------------------------------------------------------- +bool +DTLSRRouter::is_dynamic_route(RouteEntry* entry) +{ + if (entry->info() == NULL) { + return false; + } + + RouteInfo* info = dynamic_cast(entry->info()); + if (info != NULL) { + return true; + } + + return false; +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::recompute_routes() +{ +// // XXX/demmer make this a parameter +// u_int32_t elapsed = last_update_.elapsed_ms() / 1000; +// if (elapsed < config()->recompute_delay_) { +// log_debug("not recomputing routes since %u ms elapsed since last update", +// elapsed); +// return; +// } + + log_debug("recomputing all routes"); + last_update_.get_time(); + + route_table_->del_matching_entries(is_dynamic_route); + + // loop through all the nodes in the graph, finding the right + // route and re-adding it + RoutingGraph::NodeVector::const_iterator iter; + for (iter = graph_.nodes().begin(); iter != graph_.nodes().end(); ++iter) { + const RoutingGraph::Node* dest = *iter; + + if (dest == local_node_) { + continue; + } + + EndpointIDPattern dest_eid(dest->id()); + dest_eid.append_service_tag("*"); + ASSERT(dest_eid.valid()); + + // XXX/demmer this should include more criteria for + // classification, i.e. the priority class, perhaps the size + // limit, etc + RoutingGraph::Edge* edge = graph_.best_next_hop(local_node_, dest, + weight_fn_); + if (edge == NULL) { +// log_warn("no route to destination %s", dest->id().c_str()); + continue; + } + + ContactManager* cm = BundleDaemon::instance()->contactmgr(); + LinkRef link = cm->find_link(edge->info().id_.c_str()); + if (link == NULL) { + + // it's possible that this is a local registration link, + // so just ignore it + log_debug("link %s not in local link table... ignoring it", + edge->info().id_.c_str()); + continue; + } + + log_debug("recompute_routes: adding route for %s to link %s", + dest_eid.c_str(), link->name()); + + // XXX/demmer shouldn't this call check_next_hop?? + RouteEntry* e = new RouteEntry(dest_eid, link); + e->set_info(new RouteInfo(edge)); + route_table_->add_entry(e); + } + + // go through all bundles and re-route them + handle_changed_routes(); +} + +//---------------------------------------------------------------------- +DTLSRRouter::Reg::Reg(DTLSRRouter* router, + const EndpointIDPattern& eid) + : Registration(DTLSR_REGID, eid, Registration::DEFER, + /* XXX/demmer session_flags */ 0, 0), + + router_(router) +{ + logpathf("%s/reg", router->logpath()), + BundleDaemon::post(new RegistrationAddedEvent(this, EVENTSRC_ADMIN)); +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::Reg::deliver_bundle(Bundle* bundle) +{ + // XXX/demmer yuck + if (bundle->source() == BundleDaemon::instance()->local_eid()) { + log_info("ignoring bundle sent by self"); + return; + } + + u_char type; + bundle->payload().read_data(0, 1, &type); + + if (type == DTLSR::MSG_LSA) { + log_info("got LSA bundle *%p",bundle); + + LSA lsa; + if (!DTLSR::parse_lsa_bundle(bundle, &lsa)) { + log_warn("error parsing LSA"); + goto bail; + } + + router_->handle_lsa(bundle, &lsa); + } else { + log_err("unknown message type %d", type); + } + +bail: + BundleDaemon::post_at_head(new BundleDeliveredEvent(bundle, this)); +} + +//---------------------------------------------------------------------- +bool +DTLSRRouter::update_current_lsa(RoutingGraph::Node* node, + Bundle* bundle, u_int64_t seqno) +{ + bool found_stale_lsa = false; + if (seqno <= node->info().last_lsa_seqno_ && + bundle->creation_ts().seconds_ < node->info().last_lsa_creation_ts_) + { + log_info("update_current_lsa: " + "ignoring stale LSA (seqno %llu <= last %llu, " + "creation_ts %llu <= last %llu)", + seqno, node->info().last_lsa_seqno_, + bundle->creation_ts().seconds_, + node->info().last_lsa_creation_ts_); + + // suppress forwarding of the stale LSA + bundle->fwdlog()->add_entry(EndpointIDPattern::WILDCARD_EID(), + ForwardingInfo::FORWARD_ACTION, + ForwardingInfo::SUPPRESSED); + + return false; + } + else + { + log_info("update_current_lsa: " + "got new LSA (seqno %llu > last %llu || " + "creation_ts %llu > last %llu)", + seqno, node->info().last_lsa_seqno_, + bundle->creation_ts().seconds_, + node->info().last_lsa_creation_ts_); + } + + oasys::ScopeLock l(current_lsas_.lock(), + "DTLSRRouter::update_current_lsa"); + for (BundleList::iterator iter = current_lsas_.begin(); + iter != current_lsas_.end(); ++iter) + { + if ((*iter)->source() == node->id()) { + // be careful not to let the bundle reference count drop + // to zero by keeping a local reference until we can make + // sure it's at least in the NotNeededEvent + BundleRef stale_lsa("DTLSRRouter::update_current_lsa"); + stale_lsa = *iter; + + current_lsas_.erase(iter); + + log_debug("cancelling pending transmissions for *%p", + stale_lsa.object()); + + stale_lsa->fwdlog()->add_entry(EndpointIDPattern::WILDCARD_EID(), + ForwardingInfo::FORWARD_ACTION, + ForwardingInfo::SUPPRESSED); + + BundleDaemon::post_at_head( + new BundleDeleteRequest(stale_lsa.object(), + BundleProtocol::REASON_NO_ADDTL_INFO)); + found_stale_lsa = true; + +// oasys::StringBuffer buf("Stale LSA: "); +// bundle->format_verbose(&buf); +// log_multiline(oasys::LOG_INFO, buf.c_str()); + + if (node->info().last_lsa_seqno_ == 0) { + NOTREACHED; + } + break; + } + } + + if (node->info().last_lsa_seqno_ == 0) { + ASSERT(!found_stale_lsa); + log_info("update_current_lsa: " + "first LSA from %s (seqno %llu)", + node->id().c_str(), seqno); + } else { + log_info("update_current_lsa: " + "replaced %s LSA from %s (seqno %llu) with latest (%llu)", + found_stale_lsa ? "stale" : "expired", + node->id().c_str(), node->info().last_lsa_seqno_, seqno); + } + + node->mutable_info().last_lsa_seqno_ = seqno; + node->mutable_info().last_lsa_creation_ts_ = bundle->creation_ts().seconds_; + current_lsas_.push_back(bundle); + return true; +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::handle_lsa(Bundle* bundle, LSA* lsa) +{ + // First check the LSA bundle destination to see if the sender is + // in a different area + std::string lsa_area = bundle->dest().uri().query_value("area"); + if (config()->area_ != lsa_area) { + log_debug("handle_lsa: ignoring LSA since area %s != local area %s", + lsa_area.c_str(), config()->area_.c_str()); + + bundle->fwdlog()->add_entry(EndpointIDPattern::WILDCARD_EID(), + ForwardingInfo::FORWARD_ACTION, + ForwardingInfo::SUPPRESSED); + return; + } + + const EndpointID& source = bundle->source(); + + RoutingGraph::Node* a = graph_.find_node(source.str()); + if (a == NULL) { + log_debug("handle_lsa: adding new source node %s", + source.c_str()); + a = graph_.add_node(source.str(), NodeInfo()); + } + + if (! update_current_lsa(a, bundle, lsa->seqno_)) { + return; // stale lsa + } + + // don't send the LSA back where we got it from + ForwardingInfo info; + if (bundle->fwdlog()->get_latest_entry(ForwardingInfo::RECEIVED, &info)) + { + log_debug("handle_lsa: suppressing transmission back to %s", + info.remote_eid().c_str()); + bundle->fwdlog()->add_entry(info.remote_eid(), + ForwardingInfo::FORWARD_ACTION, + ForwardingInfo::SUPPRESSED); + } + + // XXX/demmer here we should drop all the links that aren't + // present in the LSA... + + // Handle all the link announcements + for (LinkStateVec::iterator iter = lsa->links_.begin(); + iter != lsa->links_.end(); ++iter) + { + LinkState* ls = &(*iter); + + // check for the case where we're re-receiving an LSA that was + // sent previously, but our link configuration has changed so + // we no longer have the link + if (a == local_node_) { + LinkRef link("DTLSRRouter::handle_lsa"); + link = BundleDaemon::instance()->contactmgr()-> + find_link(ls->id_.c_str()); + if (link == NULL) { + log_warn("local link %s in LSA but no longer exists, ignoring...", + ls->id_.c_str()); + continue; + } + } + + // find or add the node + RoutingGraph::Node* b = graph_.find_node(ls->dest_.str()); + if (!b) { + log_debug("handle_lsa: %s adding new dest node %s", + source.c_str(), ls->dest_.c_str()); + b = graph_.add_node(ls->dest_.str(), NodeInfo()); + } + + // try to find and update the edge in the graph, otherwise add it + RoutingGraph::Edge *e = NULL; + RoutingGraph::EdgeVector::const_iterator ei; + for (ei = a->out_edges().begin(); ei != a->out_edges().end(); ++ei) { + if ((*ei)->info().id_ == ls->id_) { + e = *ei; + break; + } + } + + if (e == NULL) { + e = graph_.add_edge(a, b, EdgeInfo(ls->id_, ls->params_)); + + log_debug("handle_lsa: added new edge %s from %s -> %s", + oasys::InlineFormatter().format(e->info()), + a->id().c_str(), b->id().c_str()); + } else { + e->mutable_info().params_ = ls->params_; + + log_debug("handle_lsa: updated edge %s from %s -> %s", + oasys::InlineFormatter().format(e->info()), + a->id().c_str(), b->id().c_str()); + } + + // XXX/demmer fold this into a parameter update method + e->mutable_info().last_update_.get_time(); + } + + recompute_routes(); +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::handle_lsa_expired(Bundle* bundle) +{ + // XXX/demmer this is bogus -- we don't want to drop all routes + // just b/c we lost an LSA in all cases, only when we're using the + // long-lived LSA bundles + (void)bundle; + + /* + const EndpointID& source = bundle->source_; + + RoutingGraph::Node* a = graph_.find_node(source.str()); + ASSERT(a != NULL); + + RoutingGraph::EdgeVector::const_iterator ei; + for (ei = a->out_edges().begin(); ei != a->out_edges().end(); ++ei) { + (*ei)->mutable_info().params_.state_ = LinkParams::LINK_DOWN; + } + + recompute_routes(); + */ +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::generate_link_state(LinkState* ls, + RoutingGraph::Edge* edge, + const LinkRef& link) +{ + ls->dest_.assign(edge->dest()->id()); + ls->id_.assign(edge->info().id_); + ls->params_ = edge->info().params_; + + // XXX/demmer maybe the edge info should be tied to the link + // itself somehow? + if (link != NULL) { + ls->params_.qcount_ = link->bundles_queued(); + ls->params_.qsize_ = link->bytes_queued(); + } + + if (edge->info().last_update_.sec_ != 0) { + ls->elapsed_ = edge->info().last_update_.elapsed_ms(); + } else { + ls->elapsed_ = 0; + } + edge->mutable_info().last_update_.get_time(); +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::TransmitLSATimer::timeout(const struct timeval& now) +{ + (void)now; + router_->send_lsa(); + if (interval_ != 0) { + schedule_in(interval_); + } +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::schedule_lsa() +{ + if (delayed_lsa_timer_.pending()) { + log_debug("schedule_lsa: delayed LSA transmission already pending"); + return; + } + + u_int elapsed = (oasys::Time::now() - last_lsa_transmit_).sec_; + if (elapsed > config()->min_lsa_interval_) + { + log_debug("schedule_lsa: %u seconds since last LSA transmission, " + "sending one immediately", elapsed); + send_lsa(); + } + else + { + u_int delay = std::min(config()->min_lsa_interval_, + config()->min_lsa_interval_ - elapsed); + log_debug("schedule_lsa: %u seconds since last LSA transmission, " + "min interval %u, delaying LSA for %u seconds", + elapsed, config()->min_lsa_interval_, delay); + delayed_lsa_timer_.schedule_in(delay * 1000); + } +} + +//---------------------------------------------------------------------- +void +DTLSRRouter::send_lsa() +{ + char tmp[64]; (void)tmp; + LSA lsa; + + // the contents of last_lsa_seqno get updated in the call to + // update_current_lsa below + lsa.seqno_ = local_node_->info().last_lsa_seqno_ + 1; + + RoutingGraph::EdgeVector::iterator ei; + for (ei = local_node_->out_edges().begin(); + ei != local_node_->out_edges().end(); + ++ei) + { + LinkState ls; + LinkRef link("DTLSRRouter::send_lsa"); + + // if the edge is a local registration, there's no link, we + // just pretend there's a zero-delay, infinite-bandwidth link + // to the other endpoint + if (! (*ei)->info().is_registration_) { + link = BundleDaemon::instance()->contactmgr()-> + find_link((*ei)->info().id_.c_str()); + ASSERT(link != NULL); + } + + generate_link_state(&ls, *ei, link); + lsa.links_.push_back(ls); + } + + log_debug("send_lsa: generated %zu link states for local node", + lsa.links_.size()); + + Bundle* bundle = new TempBundle(); + + if (config()->area_ != "") { + snprintf(tmp, sizeof(tmp), "dtn://*/%s?area=%s;lsa_seqno=%llu", + announce_tag_, config()->area_.c_str(), lsa.seqno_); + } else { + snprintf(tmp, sizeof(tmp), "dtn://*/%s?lsa_seqno=%llu", + announce_tag_, lsa.seqno_); + } + bundle->mutable_dest()->assign(tmp); + + bundle->mutable_source()->assign(BundleDaemon::instance()->local_eid()); + bundle->mutable_replyto()->assign(EndpointID::NULL_EID()); + bundle->mutable_custodian()->assign(EndpointID::NULL_EID()); + bundle->set_expiration(config()->lsa_lifetime_); + bundle->set_singleton_dest(false); + DTLSR::format_lsa_bundle(bundle, &lsa); + + update_current_lsa(local_node_, bundle, lsa.seqno_); + + log_debug("send_lsa: formatted LSA bundle *%p", bundle); + + // XXX/demmer this would really be better done with + // BundleActions::inject_bundle + BundleDaemon::post_at_head(new BundleReceivedEvent(bundle, EVENTSRC_ROUTER)); + + last_lsa_transmit_.get_time(); +} + +} // namespace dtn +