/** * "Helper routine to add a bundle at the indicated position." [DTN2] * * @throws BundleLockNotHeldByCurrentThread * @throws InterruptedException */ private void add_bundle(final Bundle b, final int pos) throws BundleListLockNotHoldByCurrentThread, BundleLockNotHeldByCurrentThread, InterruptedException { if (!lock_.isHeldByCurrentThread()) throw new BundleListLockNotHoldByCurrentThread(); if (!b.get_lock().isHeldByCurrentThread()) throw new BundleLockNotHeldByCurrentThread(); if (b.is_queued_on(this)) { Log.e( TAG, String.format( "ERROR in add bundle: " + "bundle id %d already on list [%s]", b.bundleid(), name_)); return; } list_.add(pos, b); b.mappings().add(this); Log.d( TAG, String.format( "bundle id %d is added to list [%s] , the size become", b.bundleid(), name_, list_.size())); }
/** * Helper routine to remove a bundle from the indicated position. This is called by other public * functions such as pop_front This is the function will actually post the bundle free event to * the Bundle daemon * * @param pos Position to delete a flag indicate whether to free the bundle as well * @param free whether to free the bundle. This freeing will remove Bundle from storage including * its payload * @throws BundleListLockNotHoldByCurrentThread * @returns the bundle that, before this call, was at the position */ private Bundle del_bundle(final int pos, boolean free) throws BundleListLockNotHoldByCurrentThread { Bundle b = list_.get(pos); assert (lock_.isHeldByCurrentThread()); if (!lock_.isHeldByCurrentThread()) throw new BundleListLockNotHoldByCurrentThread(); b.get_lock().lock(); try { Log.d( TAG, String.format("bundle id %d del_bundle: deleting mapping [%s]", b.bundleid(), name_)); if (!b.mappings().contains(this)) { Log.e( TAG, String.format( "ERROR in del bundle: " + "bundle id %d has no mapping for list [%s]", b.bundleid(), name_)); } else { b.mappings().remove(this); } // "remove the bundle from the list" [DTN2] list_.remove(b); if (free) BundleDaemon.getInstance().post(new BundleFreeEvent(b)); return b; } catch (BundleLockNotHeldByCurrentThread e) { Log.e(TAG, e.getMessage()); return null; } finally { b.get_lock().unlock(); } }
/** Delete any fragments that are no longer needed given the incoming (non-fragment) bundle. */ public void delete_obsoleted_fragments(Bundle bundle) { FragmentState state; // cons up the key to do the table lookup and look for reassembly state String[] hash_key = new String[1]; hash_key[0] = ""; get_hash_key(bundle, hash_key); state = fragment_table_.get(hash_key); Logger.getInstance() .debug( TAG, String.format( "checking for obsolete fragments id=%s hash=%s...", bundle.bundleid(), hash_key[0])); if (state == null) { Logger.getInstance().error(TAG, String.format("no reassembly state for key %s", hash_key[0])); return; } Logger.getInstance() .debug( TAG, String.format( "found reassembly state... deleting %d fragments", state.num_fragments())); state.fragment_list().get_lock().lock(); try { while (state.fragment_list().size() > 0) { BundleDaemon.getInstance() .post( new BundleDeleteRequest( state.fragment_list().pop_back(false), status_report_reason_t.REASON_NO_ADDTL_INFO)); } assert (state.fragment_list().size() == 0) : TAG + ": delete_obsoleted_fragments Size not 0"; // moved into // events } finally { state.fragment_list().get_lock().unlock(); } fragment_table_.remove(hash_key); }
/** * "Search the list for a bundle with the given id." [DTN2] * * @return the found bundle otherwise, null. */ public Bundle find(int bundleid) { lock_.lock(); try { Iterator<Bundle> iter = list_.iterator(); while (iter.hasNext()) { Bundle bundle = iter.next(); if (bundle.bundleid() == bundleid) return bundle; } return null; } finally { lock_.unlock(); } }
/** * Given a newly arrived bundle fragment, append it to the table of fragments and see if it allows * us to reassemble the bundle. If it does, a ReassemblyCompletedEvent will be posted. * * @param fragment Newly received fragment */ public void process_for_reassembly(Bundle fragment) { FragmentState state; assert (fragment.is_fragment()) : TAG + ": process_for_reassembly() not a fragment"; String[] hash_key = new String[1]; get_hash_key(fragment, hash_key); FragmentState iter = fragment_table_.get(hash_key); Logger.getInstance() .debug( TAG, String.format( "processing bundle fragment id=%s hash=%s %s", fragment.bundleid(), hash_key, fragment.is_fragment())); if (iter == null) { state = new FragmentState(); fragment.copy_metadata(state.bundle()); state.bundle().set_is_fragment(false); state.bundle().payload().set_length(fragment.orig_length()); fragment_table_.put(hash_key[0], state); } else { state = iter; Logger.getInstance() .debug( TAG, String.format( "found reassembly state for key %s (%s fragments)", hash_key, state.fragment_list().size())); } // stick the fragment on the reassembly list state.add_fragment(fragment); // store the fragment data in the partially reassembled bundle file int fraglen = fragment.payload().length(); Logger.getInstance() .debug( TAG, String.format( "write_data: length_=%s src_offset=%s dst_offset=%s len %s", state.bundle().payload().length(), 0, fragment.frag_offset(), fraglen)); state.bundle().payload().write_data(fragment.payload(), 0, fraglen, fragment.frag_offset()); // reassembled bundle, but eventually reassembly will have to do much // more if (fragment.frag_offset() == 0 && state.bundle().recv_blocks().size() > 0) { BlockInfo block_i; ListIterator<BlockInfo> entry = fragment.recv_blocks().listIterator(); while (entry.hasNext()) { block_i = entry.next(); state.bundle().recv_blocks().add(block_i); } } // check see if we're done if (state.check_completed()) { return; } BundleDaemon.getInstance() .post_at_head(new ReassemblyCompletedEvent(state.bundle(), state.fragment_list())); assert (state.fragment_list().size() == 0) : TAG + ": process_for_reassembly size not 0"; // moved into the event fragment_table_.remove(hash_key); }
/** * "Parse the supplied chunk of arriving data and append it to the rcvd_blocks_ list in the given * bundle, finding the appropriate BlockProcessor element and calling its receive() handler. * * <p>When called repeatedly for arriving chunks of data, this properly fills in the entire * bundle, including the in_blocks_ record of the arriving blocks and the payload (which is stored * externally)." [DTN2] * * @return "the length of data consumed or -1 on protocol error, plus sets last to true if the * bundle is complete." [DTN2] */ public static int consume(Bundle bundle, IByteBuffer data, int len, boolean[] last) { int old_position = data.position(); int origlen = len; last[0] = false; BlockInfoVec recv_blocks = bundle.recv_blocks(); // "special case for first time we get called, since we need to // create a BlockInfo struct for the primary block without knowing // the typecode or the length" [DTN2] if (recv_blocks.isEmpty()) { BPF.getInstance() .getBPFLogger() .debug(TAG, "consume: got first block... " + "creating primary block info"); recv_blocks.append_block(find_processor(bundle_block_type_t.PRIMARY_BLOCK), null); } // "loop as long as there is data left to process" [DTN2] while (len != 0) { BPF.getInstance() .getBPFLogger() .debug(TAG, String.format("consume: %d bytes left to process", len)); BlockInfo info = recv_blocks.back(); // "if the last received block is complete, create a new one // and push it onto the vector. at this stage we consume all // blocks, even if there's no BlockProcessor that understands // how to parse it" [DTN2] if (info.complete()) { data.mark(); byte bundle_block_type_byte = data.get(); bundle_block_type_t type = bundle_block_type_t.get(bundle_block_type_byte); data.reset(); info = recv_blocks.append_block(find_processor(type), null); BPF.getInstance() .getBPFLogger() .debug( TAG, String.format( "consume: previous block complete, " + "created new BlockInfo type %s", info.owner().block_type())); } // "now we know that the block isn't complete, so we tell it to // consume a chunk of data" [DTN2] BPF.getInstance() .getBPFLogger() .debug( TAG, String.format( "consume: block processor %s type %s incomplete, " + "calling consume (%d bytes already buffered)", info.owner().block_type(), info.type(), info.contents().position())); int cc = info.owner().consume(bundle, info, data, len); if (cc < 0) { BPF.getInstance() .getBPFLogger() .error(TAG, String.format("consume: protocol error handling block %s", info.type())); return -1; } // "decrement the amount that was just handled from the overall // total. verify that the block was either completed or // consumed all the data that was passed in." [DTN2] len -= cc; data.position(data.position() + cc); BPF.getInstance() .getBPFLogger() .debug( TAG, String.format( "consume: consumed %d bytes of block type %s (%s)", cc, info.type(), info.complete() ? "complete" : "not complete")); if (info.complete()) { // check if we're done with the bundle if ((info.flags() & block_flag_t.BLOCK_FLAG_LAST_BLOCK.getCode()) > 0) { last[0] = true; break; } } else { assert (len == 0); } } BPF.getInstance() .getBPFLogger() .debug( TAG, String.format( "bundle id %d consume completed, %d/%d bytes consumed %s", bundle.bundleid(), origlen - len, origlen, last[0] ? "(completed bundle)" : "")); data.position(old_position); return origlen - len; }
/** * "Copies out a chunk of formatted bundle data at a specified offset from the provided * BlockInfoVec." [DTN2] * * @return "the length of the chunk produced (up to the supplied length) and sets last to true if * the bundle is complete." [DTN2] */ public static int produce( final Bundle bundle, final BlockInfoVec blocks, IByteBuffer data, int offset, int len, boolean[] last) { int old_position = data.position(); int origlen = len; last[0] = false; if (len == 0) return 0; assert (!blocks.isEmpty()); Iterator<BlockInfo> iter = blocks.iterator(); BlockInfo current_block = iter.next(); // "advance past any blocks that are skipped by the given offset."[DTN2] while (offset >= current_block.full_length()) { BPF.getInstance() .getBPFLogger() .debug( TAG, String.format( "BundleProtocol::produce skipping block type %s " + "since offset %d >= block length %d", current_block.type().toString(), offset, current_block.full_length())); offset -= current_block.full_length(); current_block = iter.next(); } // "the offset value now should be within the current block" [DTN2] while (true) { // The first time remainder will be minus from leftover offset above // but later on it will be the full content of the block int remainder = current_block.full_length() - offset; int tocopy = Math.min(len, remainder); BPF.getInstance() .getBPFLogger() .debug( TAG, String.format( "BundleProtocol::produce copying %d/%d bytes from " + "block type %s at offset %d", tocopy, remainder, current_block.type().toString(), offset)); current_block.owner().produce(bundle, current_block, data, offset, tocopy); len -= tocopy; // move the position of IByteBuffer data.position(data.position() + tocopy); // "if we've copied out the full amount the user asked for, // we're done. note that we need to check the corner case // where we completed the block exactly to properly set the // last bit" [DTN2] if (len == 0) { if ((tocopy == remainder) && ((current_block.flags() & BundleProtocol.block_flag_t.BLOCK_FLAG_LAST_BLOCK.getCode()) > 0)) { last[0] = true; } break; } // "we completed the current block, so we're done if this // is the last block, even if there's space in the user buffer" // [DTN2] assert (tocopy == remainder); if ((current_block.flags() & BundleProtocol.block_flag_t.BLOCK_FLAG_LAST_BLOCK.getCode()) > 0) { last[0] = true; break; } // advance to next block current_block = iter.next(); offset = 0; } BPF.getInstance() .getBPFLogger() .debug( TAG, String.format( "BundleProtocol::produce complete: " + "produced %d bytes, bundle id %d, status is %s", origlen - len, bundle.bundleid(), last[0] ? "complete" : "not complete")); data.position(old_position); return origlen - len; }
/** "Constructor-like function to create a new custody signal bundle." [DTN2] */ public static Bundle create_custody_signal( final Bundle orig_bundle, final EndpointID source_eid, boolean succeeded, BundleProtocol.custody_signal_reason_t reason) { Bundle bundle = new Bundle(location_t.MEMORY); bundle.source().assign(source_eid); if (orig_bundle.custodian().equals(EndpointID.NULL_EID())) { Logger.getInstance() .error( TAG, String.format( "create_custody_signal(for bundle id %d): " + "custody signal cannot be generated due to custodian is null eid", orig_bundle.bundleid())); } bundle.dest().assign(orig_bundle.custodian()); bundle.replyto().assign(EndpointID.NULL_EID()); bundle.custodian().assign(EndpointID.NULL_EID()); bundle.set_is_admin(true); bundle.set_expiration(orig_bundle.expiration()); int sdnv_encoding_len = 0; int signal_len = 0; // "format of custody signals: // // 1 byte admin payload type and flags // 1 byte status code // SDNV [Fragment Offset (if present)] // SDNV [Fragment Length (if present)] // SDNVx2 Time of custody signal // SDNVx2 Copy of bundle X's Creation Timestamp // SDNV Length of X's source endpoint ID // vari Source endpoint ID of bundle X // // first calculate the length // // the non-optional, fixed-length fields above:" [DTN2] signal_len = 1 + 1; // "the 2 SDNV fragment fields:" [DTN2] if (orig_bundle.is_fragment()) { signal_len += SDNV.encoding_len(orig_bundle.frag_offset()); signal_len += SDNV.encoding_len(orig_bundle.orig_length()); } // "Time field, set to the current time:" [DTN2] DTNTime now = new DTNTime(); signal_len += DTNTime.SDNV_encoding_len(now); // "The bundle's creation timestamp:" [DTN2] signal_len += BundleTimestamp.SDNV_encoding_len(orig_bundle.creation_ts()); // the Source Endpoint ID length and value signal_len += SDNV.encoding_len(orig_bundle.source().length()) + orig_bundle.source().length(); // // "We got all the data ready, now format the buffer" [DTN2] // IByteBuffer bp = new SerializableByteBuffer(signal_len); int len = signal_len; bp.rewind(); // "Admin Payload Type and flags" [DTN2] byte type_and_flags_byte = (byte) (BundleProtocol.admin_record_type_t.ADMIN_CUSTODY_SIGNAL.getCode() << 4); if (orig_bundle.is_fragment()) { type_and_flags_byte |= BundleProtocol.admin_record_flags_t.ADMIN_IS_FRAGMENT.getCode(); } bp.put(type_and_flags_byte); len--; // Status_Flag_Byte consists of Success flag and reason code byte status_flag_byte = (byte) ((succeeded ? 1 : 0) << 7 | (reason.getCode() & 0x7f)); bp.put(status_flag_byte); len--; // "The 2 Fragment Fields" [DTN2] if (orig_bundle.is_fragment()) { sdnv_encoding_len = SDNV.encode(orig_bundle.frag_offset(), bp, len); assert (sdnv_encoding_len > 0); len -= sdnv_encoding_len; sdnv_encoding_len = SDNV.encode(orig_bundle.orig_length(), bp, len); assert (sdnv_encoding_len > 0); len -= sdnv_encoding_len; } // DTNTime which is a time of signal field sdnv_encoding_len = DTNTime.SDNV_encoding_len(now); assert (sdnv_encoding_len > 0); DTNTime.encodeSDNV(now, bp); len -= sdnv_encoding_len; // "Copy of bundle X's Creation Timestamp" [DTN2] sdnv_encoding_len = BundleTimestamp.SDNV_encoding_len(orig_bundle.creation_ts()); assert (sdnv_encoding_len > 0); BundleTimestamp.encodeSDNV(orig_bundle.creation_ts(), bp); len -= sdnv_encoding_len; // "The Endpoint ID length and data" [DTN2] sdnv_encoding_len = SDNV.encode(orig_bundle.source().length(), bp, len); assert (sdnv_encoding_len > 0); len -= sdnv_encoding_len; assert (len == orig_bundle.source().length()); bp.put(orig_bundle.source().byte_array()); // // "Finished generating the payload" [DTN2] // bp.rewind(); bundle.payload().set_data(bp, signal_len); return bundle; }