/** "Remove matching deferred transmission entries." [DTN2] */ void remove_from_deferred(final Bundle bundle, int actions) { ContactManager cm = BundleDaemon.getInstance().contactmgr(); cm.get_lock().lock(); try { final LinkSet links = cm.links(); Iterator<Link> iter = links.iterator(); while (iter.hasNext()) { Link link = iter.next(); // "a bundle might be deleted immediately after being loaded // from storage, meaning that remove_from_deferred is called // before the deferred list is created (since the link isn't // fully set up yet). so just skip the link if there's no // router info, and therefore no deferred list" [DTN2] if (link.router_info() == null) { continue; } DeferredList deferred = deferred_list(link); ForwardingInfo info = deferred.find(bundle); if (info != null) { if ((info.action().getCode() & actions) > 0) { Log.d( TAG, String.format("removing bundle %s from link %s deferred list", bundle, link)); deferred.del(bundle); } } } } finally { cm.get_lock().unlock(); } }
@Override protected void handle_link_created(LinkCreatedEvent event) { Link link = event.link(); assert (link != null) : "TableBasedRouter: handle_link_created: link is null"; assert (!link.isdeleted()) : "TableBasedRouter: handle_link_available: link is deleted"; link.set_router_info(new DeferredList(link)); add_nexthop_route(link); }
@Override protected void handle_contact_down(ContactDownEvent event) { Link link = event.contact().link(); assert (link != null) : "TableBasedRouter : handle_contact_down : link is null"; assert (!link.isdeleted()) : "TableBasedRouter : handle_contact_down : link is deleted"; // "if there are any bundles queued on the link when it goes down, // schedule a timer to cancel those transmissions and reroute the // bundles in case the link takes too long to come back up" [DTN2] int num_queued = link.queue().size(); if (num_queued != 0) { RerouteTimer reroute_timer = reroute_timers_.get(link.name()); if (reroute_timer == null) { Log.d( TAG, String.format( "link %s went down with %d bundles queued, " + "scheduling reroute timer in %d seconds", link.name(), num_queued, link.params().potential_downtime())); RerouteTimer t = new RerouteTimer(this, link); t.schedule_in(link.params().potential_downtime()); reroute_timers_.put(link.name(), t); } } }
@Override protected void handle_link_deleted(LinkDeletedEvent event) { Link link = event.link(); assert (link != null) : "TableBasedRouter: handle_link_deleted: link is null"; route_table_.del_entries_for_nexthop(link); RerouteTimer t = reroute_timers_.get(link.name()); if (t != null) { Log.d(TAG, String.format("link %s deleted, cancelling reroute timer", link.name())); reroute_timers_.remove(link.name()); t.cancel(); } }
/** * "Helper accessor to return the deferred queue for a link" [DTN2] * * @param link * @return */ public DeferredList deferred_list(final Link link) { DeferredList dq = (DeferredList) link.router_info(); assert dq != null : "TableBasedRouter: deferred_list() deferedList is null"; if (dq == null) dq = new DeferredList(link); return dq; }
@Override protected void handle_link_available(LinkAvailableEvent event) { Link link = event.link(); assert (link != null) : "TableBasedRouter: handle_link_available: link is null"; assert (!link.isdeleted()) : "TableBasedRouter: handle_link_available: link is deleted"; // "if it is a discovered link, we typically open it" [DTN2] if (config_.open_discovered_links() && !link.isopen() && link.type() == Link.link_type_t.OPPORTUNISTIC && event.reason() == ContactEvent.reason_t.DISCOVERY) { actions_.open_link(link); } // "check if there's anything to be forwarded to the link" [DTN2] check_next_hop(link); }
@Override protected void handle_contact_up(ContactUpEvent event) { Link link = event.contact().link(); assert (link != null) : "TabledBasedRouter: handle_contact_up : link is null"; assert (!link.isdeleted()) : "TabledBasedRouter: handle_contact_up : link is deleted"; if (!link.isopen()) { Log.e( TAG, String.format("contact up(link %s): event delivered but link not open", link.name())); } add_nexthop_route(link); check_next_hop(link); // "check if there's a pending reroute timer on the link, and if // so, cancel it. // // note that there's a possibility that a link just bounces // between up and down states but can't ever really send a bundle // (or part of one), which we don't handle here since we can't // distinguish that case from one in which the CL is actually // sending data, just taking a long time to do so." [DTN2] RerouteTimer reroute_timer = reroute_timers_.get(link.name()); if (reroute_timer != null) { Log.d(TAG, String.format("link %s reopened, cancelling reroute timer", link.name())); reroute_timers_.remove(link.name()); reroute_timer.cancel(); } }
/** * "When new links are added or opened, and if we're configured to add nexthop routes, try to add * a new route for the given link." [DTN2] */ public void add_nexthop_route(final Link link) { // "If we're configured to do so, create a route entry for the eid // specified by the link when it connected, using the // scheme-specific code to transform the URI to wildcard // the service part" [DTN2] EndpointID eid = link.remote_eid(); if (config_.add_nexthop_routes() && !eid.equals(EndpointID.NULL_EID())) { EndpointIDPattern eid_pattern = new EndpointIDPattern(link.remote_eid()); // "attempt to build a route pattern from link's remote_eid" [DTN2] if (!eid_pattern.append_service_wildcard()) // "else assign remote_eid as-is" [DTN2] eid_pattern.assign(link.remote_eid()); RouteEntryVec ignored = new RouteEntryVec(); if (route_table_.get_matching(eid_pattern, link, ignored) == 0) { RouteEntry entry = new RouteEntry(eid_pattern, link); entry.set_action(ForwardingInfo.action_t.FORWARD_ACTION); add_route(entry); } } }
public DeferredList(final Link link) { list_ = new BundleList(link.name() + ":deferred"); info_ = new HashMap<Bundle, ForwardingInfo>(); count_ = 0; }
/** * Helper function for rerouting * * @param link */ void reroute_bundles(final Link link) { assert !link.isdeleted() : "TableBasedRouter : reroute_bundles, link is deleted"; // "if the reroute timer fires, the link should be down and there // should be at least one bundle queued on it." [DTN2] if (link.state() != Link.state_t.UNAVAILABLE) { Log.w( TAG, String.format( "reroute timer fired but link %s state is %s, not UNAVAILABLE", link, link.state().toString())); return; } Log.d( TAG, String.format( "reroute timer fired -- cancelling %s bundles on link %s", link.queue().size(), link.toString())); link.queue().get_lock().lock(); try { while (!link.queue().empty()) { Bundle bundle = link.queue().front(); actions_.cancel_bundle(bundle, link); assert !bundle.is_queued_on(link.queue()) : "TableBasedRouter : reroute_bundles, bundle is not queued on link"; } // "there should never have been any in flight since the link is // unavailable" [DTN2] assert link.inflight().empty() : "TableBasedRouter : reroute_bundles, link on flight list is not empty"; } finally { link.queue().get_lock().unlock(); } }
/** * "Called when the next hop link is available for transmission (i.e. either when it first arrives * and the contact is brought up or when a bundle is completed and it's no longer busy). * * <p>Loops through the bundle list and calls fwd_to_matching on all bundles." [DTN2] */ protected void check_next_hop(Link next_hop) { // "if the link isn't open, there's nothing to do now" [DTN2] if (!next_hop.isopen()) { Log.d( TAG, String.format( "check_next_hop %s -> %s: link not open...", next_hop.name(), next_hop.nexthop())); return; } // "if the link queue doesn't have space (based on the low water // mark) don't do anything" [DTN2] if (!next_hop.queue_has_space()) { Log.d( TAG, String.format( "check_next_hop %s -> %s: no space in queue...", next_hop.name(), next_hop.nexthop())); return; } Log.d( TAG, String.format( "check_next_hop %s -> %s: checking deferred bundle list...", next_hop.name(), next_hop.nexthop())); // "because the loop below will remove the current bundle from // the deferred list, invalidating any iterators pointing to its // position, make sure to advance the iterator before processing // the current bundle" [DTN2] DeferredList deferred = deferred_list(next_hop); deferred.list().get_lock().lock(); try { Iterator<Bundle> iter = deferred.list().begin(); while (iter.hasNext()) { if (next_hop.queue_is_full()) { Log.d( TAG, String.format( "check_next_hop %s: link queue is full, stopping loop", next_hop.name())); break; } Bundle bundle = iter.next(); ForwardingInfo info = deferred.find(bundle); assert info != null : "TableBasedRouter: check_next_hop, ForwardingInfo regarding Bundle is null"; // "if should_fwd returns false, then the bundle was either // already transmitted or is in flight on another node. since // it's possible that one of the other transmissions will // fail, we leave it on the deferred list for now, relying on // the transmitted handlers to clean up the state" [DTN2] if (!this.should_fwd(bundle, next_hop, info.action())) { Log.d(TAG, String.format("check_next_hop: not forwarding to link %s", next_hop.name())); continue; } // "if the link is available and not open, open it" [DTN2] if (next_hop.isNotUnavailable() && (!next_hop.isopen()) && (!next_hop.isopening())) { Log.d( TAG, String.format( "check_next_hop: " + "opening %s because a message is intended for it", next_hop.name())); actions_.open_link(next_hop); } // "remove the bundle from the deferred list" [DTN2] Log.d( TAG, String.format( "check_next_hop: sending bundle %d to %s", bundle.bundleid(), next_hop.name())); iter.remove(); actions_.queue_bundle(bundle, next_hop, info.action(), info.custody_spec()); } } catch (BundleListLockNotHoldByCurrentThread e) { Log.e(TAG, "Table Based Router " + e.toString()); } finally { deferred.list().get_lock().unlock(); } }
/** "Try to forward a bundle to a next hop route." [DTN2] */ protected boolean fwd_to_nexthop(Bundle bundle, RouteEntry route) { Link link = route.link(); // "if the link is available and not open, open it" [DTN2] if (link.isNotUnavailable() && (!link.isopen()) && (!link.isopening())) { Log.d(TAG, String.format("opening %s because a message is intended for it", link.name())); actions_.open_link(link); } // "if the link is open and has space in the queue, then queue the // bundle for transmission there" [DTN2] if (link.isopen() && !link.queue_is_full()) { Log.d(TAG, String.format("queuing %d on %s", bundle.bundleid(), link.name())); actions_.queue_bundle(bundle, link, route.action(), route.custody_spec()); return true; } // "otherwise we can't send the bundle now, so put it on the link's // deferred list and log reason why we can't forward it" [DTN2] DeferredList deferred = deferred_list(link); if (!bundle.is_queued_on(deferred.list())) { ForwardingInfo info = new ForwardingInfo( ForwardingInfo.state_t.NONE, route.action(), link.name_str(), 0xffffffff, link.remote_eid(), route.custody_spec()); deferred.add(bundle, info); } else { Log.w( TAG, String.format( "bundle %d already exists on deferred list of link %s", bundle.bundleid(), link.name())); } if (!link.isNotUnavailable()) { Log.d( TAG, String.format( "can't forward bundle %d to %s because link not available", bundle.bundleid(), link.name())); } else if (!link.isopen()) { Log.d( TAG, String.format( TAG, "can't forward bundle %d to %s because link not open", bundle.bundleid(), link.name())); } else if (link.queue_is_full()) { Log.d( TAG, String.format( TAG, "can't forward bundle %d to %s because link queue is full", bundle.bundleid(), link.name())); } else { Log.d(TAG, String.format(TAG, "can't forward %d to %s", bundle.bundleid(), link.name())); } return false; }