/** "Hook to ask the router if the bundle can be deleted." [DTN2] */ @Override public boolean can_delete_bundle(final Bundle bundle) { Log.d( TAG, String.format("TableBasedRouter::can_delete_bundle: checking if we can delete %s", bundle)); // "check if we haven't yet done anything with this bundle" [DTN2] if (bundle .fwdlog() .get_count( ForwardingInfo.state_t.TRANSMITTED.getCode() | ForwardingInfo.state_t.DELIVERED.getCode(), ForwardingInfo.ANY_ACTION) == 0) { Log.d( TAG, String.format( "TableBasedRouter::can_delete_bundle(%d): " + "not yet transmitted or delivered", bundle.bundleid())); return false; } // check if we have local custody if (bundle.local_custody()) { Log.d( TAG, String.format( "TableBasedRouter::can_delete_bundle(%d): " + "not deleting because we have custody", bundle.bundleid())); return false; } return true; }
@Override protected void handle_bundle_received(BundleReceivedEvent event) { boolean should_route = true; Bundle bundle = event.bundle(); Log.d(TAG, String.format("handle bundle received: bundle %d", bundle.bundleid())); EndpointID remote_eid = EndpointID.NULL_EID(); if (event.link() != null) { remote_eid = event.link().remote_eid(); } if (!reception_cache_.add_entry(bundle, remote_eid)) { Log.i(TAG, String.format("ignoring duplicate bundle: bundle %d", bundle.bundleid())); BundleDaemon.getInstance() .post_at_head( new BundleDeleteRequest( bundle, BundleProtocol.status_report_reason_t.REASON_NO_ADDTL_INFO)); return; } if (should_route) { route_bundle(bundle); } else { BundleDaemon.getInstance() .post_at_head( new BundleDeleteRequest( bundle, BundleProtocol.status_report_reason_t.REASON_NO_ADDTL_INFO)); } }
/** * "Add a new bundle/info pair to the deferred list" [DTN2] * * @param bundle * @param info * @return */ boolean add(final Bundle bundle, final ForwardingInfo info) { if (list_.contains(bundle)) { Log.e(TAG, String.format("bundle %d already in deferred list!", bundle.bundleid())); return false; } Log.d( TAG, String.format( "adding bundle %d to deferred (list length %d)", bundle.bundleid(), count_)); count_++; list_.push_back(bundle); info_.put(bundle, info); return true; }
@Override protected void handle_bundle_cancelled(BundleSendCancelledEvent event) { Bundle bundle = event.bundle(); Log.d(TAG, String.format("handle bundle cancelled: bundle %d", bundle.bundleid())); // "if the bundle has expired, we don't want to reroute it." [DTN2] if (!bundle.expired()) { route_bundle(bundle); } }
@Override protected void handle_bundle_transmitted(BundleTransmittedEvent event) { Bundle bundle = event.bundle(); Log.d(TAG, String.format("handle bundle transmitted: bundle %d", bundle.bundleid())); // "if the bundle has a deferred single-copy transmission for // forwarding on any links, then remove the forwarding log entries" [DTN2] remove_from_deferred(bundle, ForwardingInfo.action_t.FORWARD_ACTION.getCode()); // "check if the transmission means that we can send another bundle // on the link" [DTN2] Link link = event.contact().link(); check_next_hop(link); }
/** * "Remove the bundle and its associated forwarding info from the list" [DTN2] * * @param bundle * @return */ boolean del(final Bundle bundle) { if (!list_.erase(bundle, false)) { return false; } assert (count_ > 0) : "DeferredList::del count <= 0"; count_--; Log.d( TAG, String.format("removed bundle %d from deferred (length %d)", bundle.bundleid(), count_)); ForwardingInfo removed_result = info_.remove(bundle); assert removed_result != null : "DeferredList::del failed to remove info map"; return true; }
/** * "Check whether the Bundle should be forwarded to the give route or not" [DTN2] * * @param bundle the Bundle to check * @param route the route to check */ protected boolean should_fwd(final Bundle bundle, RouteEntry route) { if (route == null) return false; EndpointID prevhop = reception_cache_.lookup(bundle); if (prevhop != null) { if (prevhop.equals(route.link().remote_eid()) && !prevhop.equals(EndpointID.NULL_EID())) { Log.d( TAG, String.format( "should_fwd bundle %d: " + "skip %s since bundle arrived from the same node", bundle.bundleid(), route.link().name())); return false; } } return super.should_fwd(bundle, route.link(), route.action()); }
/** "Hook to tell the router that the bundle should be deleted." [DTN2] */ @Override public void delete_bundle(final Bundle bundle) { Log.d(TAG, String.format("delete bundleid %d", bundle.bundleid())); remove_from_deferred(bundle, ForwardingInfo.ANY_ACTION); }
/** * "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(); } }
/** * "Check the route table entries that match the given bundle and have not already been found in * the bundle history. If a match is found, call fwd_to_nexthop on it." [DTN2] * * @param bundle the bundle to forward * @return "the number of links on which the bundle was queued (i.e. the number of matching route * entries.)" [DTN2] */ protected int route_bundle(Bundle bundle) { RouteEntryVec matches = new RouteEntryVec(); Log.d(TAG, String.format("route_bundle: checking bundle %d", bundle.bundleid())); // "check to see if forwarding is suppressed to all nodes" [DTN2] if (bundle .fwdlog() .get_count( EndpointIDPattern.WILDCARD_EID(), ForwardingInfo.state_t.SUPPRESSED.getCode(), ForwardingInfo.ANY_ACTION) > 0) { Log.i( TAG, String.format( "route_bundle: " + "ignoring bundle %d since forwarding is suppressed", bundle.bundleid())); return 0; } Link null_link = null; route_table_.get_matching(bundle.dest(), null_link, matches); // "sort the matching routes by priority, allowing subclasses to // override the way in which the sorting occurs" [DTN2] sort_routes(matches); Log.d( TAG, String.format( "route_bundle bundle id %d: checking %d route entry matches", bundle.bundleid(), matches.size())); int count = 0; Iterator<RouteEntry> itr = matches.iterator(); while (itr.hasNext()) { RouteEntry route = itr.next(); Log.d( TAG, String.format( "checking route entry %s link %s (%s)", route.toString(), route.link().name(), route.link())); if (!should_fwd(bundle, route)) { continue; } if (deferred_list(route.link()).list().contains(bundle)) { Log.d( TAG, String.format( "route_bundle bundle %d: " + "ignoring link %s since already deferred", bundle.bundleid(), route.link().name())); continue; } // "because there may be bundles that already have deferred // transmission on the link, we first call check_next_hop to // get them into the queue before trying to route the new // arrival, otherwise it might leapfrog the other deferred // bundles" [DTN2] check_next_hop(route.link()); if (!fwd_to_nexthop(bundle, route)) { continue; } ++count; } Log.d( TAG, String.format( "route_bundle bundle id %d: forwarded on %d links", bundle.bundleid(), count)); return count; }
/** "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; }
/** * Internal function get the total length of primary block to write on the buffer * * @param bundle Bundle to generate * @param dict Dictionary to get the offsets of the endpoint eids * @param primary PrimaryBlock data strucre object * @return Total numbers of Bytes required to write primary block */ protected static int get_primary_len(final Bundle bundle, Dictionary dict, PrimaryBlock primary) { int primary_len = 0; int block_len = 0; primary.set_dictionary_length(0); primary.set_block_length(0); /* * We need to figure out the total length of the primary block, * except for the SDNVs used to encode flags and the length itself and * the one byte version field. * * First, we determine the size of the dictionary by first * figuring out all the unique strings, and in the process, * remembering their offsets and summing up their lengths * (including the null terminator for each). */ dict.get_offsets(bundle.dest(), primary.dest_scheme_offset(), primary.dest_ssp_offset()); block_len += SDNV.encoding_len(primary.dest_scheme_offset()); block_len += SDNV.encoding_len(primary.dest_ssp_offset()); dict.get_offsets(bundle.source(), primary.source_scheme_offset(), primary.source_ssp_offset()); block_len += SDNV.encoding_len(primary.source_scheme_offset()); block_len += SDNV.encoding_len(primary.source_ssp_offset()); dict.get_offsets( bundle.replyto(), primary.replyto_scheme_offset(), primary.replyto_ssp_offset()); block_len += SDNV.encoding_len(primary.replyto_scheme_offset()); block_len += SDNV.encoding_len(primary.replyto_ssp_offset()); dict.get_offsets( bundle.custodian(), primary.custodian_scheme_offset(), primary.custodian_ssp_offset()); block_len += SDNV.encoding_len(primary.custodian_scheme_offset()); block_len += SDNV.encoding_len(primary.custodian_ssp_offset()); primary.set_dictionary_length(dict.dict_length()); block_len += SDNV.encoding_len(bundle.creation_ts().seconds()); block_len += SDNV.encoding_len(bundle.creation_ts().seqno()); block_len += SDNV.encoding_len(bundle.expiration()); block_len += SDNV.encoding_len(primary.dictionary_length_value()); block_len += primary.dictionary_length_value(); /* * If the bundle is a fragment, we need to include space for the * fragment offset and the original payload length. * * Note: Any changes to this protocol must be reflected into the * FragmentManager since it depends on this length when * calculating fragment sizes. */ if (bundle.is_fragment()) { block_len += SDNV.encoding_len(bundle.frag_offset()); block_len += SDNV.encoding_len(bundle.orig_length()); } // Format the processing flags. primary.set_processing_flags(format_bundle_flags(bundle)); primary.set_processing_flags(format_bundle_flags(bundle)); primary.set_processing_flags(primary.processing_flags_value() | format_cos_flags(bundle)); primary.set_processing_flags(primary.processing_flags_value() | format_srr_flags(bundle)); /* * Finally, add up the initial preamble and the variable * length part. */ primary.set_block_length(block_len); primary_len = (int) (1 + SDNV.encoding_len(primary.processing_flags) + SDNV.encoding_len(primary.block_length()) + primary.block_length_value()); Log.d(TAG, "get_primary_len: for bundleid = " + bundle.bundleid() + ": " + primary_len); // Fill in the remaining values of 'primary' just for the sake of returning // a complete data structure. primary.set_version(BundleProtocol.CURRENT_VERSION); primary.set_creation_time(bundle.creation_ts().seconds()); primary.set_creation_sequence(bundle.creation_ts().seqno()); primary.set_lifetime(bundle.expiration()); return primary_len; }