/** "Give the processors a chance to chew on the bundle after reloading from disk." [DTN2] */ public static void reload_post_process(Bundle bundle) { BlockInfoVec recv_blocks = bundle.recv_blocks(); Iterator<BlockInfo> iter = recv_blocks.iterator(); while (iter.hasNext()) { // "allow BlockProcessors [and Ciphersuites] a chance to re-do // things needed after a load-from-store" [DTN2] BlockInfo block = iter.next(); block.owner().reload_post_process(bundle, recv_blocks, block); } }
/** * "Generate a BlockInfoVec for the outgoing link and put it into xmit_blocks_." [DTN2] * * @return a list of new BLockInfo */ public static BlockInfoVec prepare_blocks(Bundle bundle, final Link link) { // "create a new block list for the outgoing link by first calling // prepare on all the BlockProcessor classes for the blocks that // arrived on the link" [DTN2] BlockInfoVec xmit_blocks = bundle.xmit_link_block_set().create_blocks(link); BlockInfoVec recv_blocks = bundle.recv_blocks(); if (recv_blocks.size() > 0) { // "if there is a received block, the first one better be the primary" // [DTN2] assert (recv_blocks.front().type() == bundle_block_type_t.PRIMARY_BLOCK); Iterator<BlockInfo> iter = recv_blocks.iterator(); while (iter.hasNext()) { BlockInfo block = iter.next(); if (bundle.fragmented_incoming() && xmit_blocks.find_block(BundleProtocol.bundle_block_type_t.PAYLOAD_BLOCK) != null) { continue; } block .owner() .prepare(bundle, xmit_blocks, block, link, BlockInfo.list_owner_t.LIST_RECEIVED); } } else { BPF.getInstance().getBPFLogger().debug(TAG, "adding primary and payload block"); BlockProcessor bp = find_processor(BundleProtocol.bundle_block_type_t.PRIMARY_BLOCK); bp.prepare(bundle, xmit_blocks, null, link, BlockInfo.list_owner_t.LIST_NONE); bp = find_processor(bundle_block_type_t.PAYLOAD_BLOCK); bp.prepare(bundle, xmit_blocks, null, link, BlockInfo.list_owner_t.LIST_NONE); } // "now we also make sure to prepare() on any registered processors // that don't already have a block in the output list. this // handles the case where we have a locally generated block with // nothing in the recv_blocks vector" [DTN2] Iterator<BlockProcessor> itr = processors_.iterator(); while (itr.hasNext()) { BlockProcessor bp = itr.next(); if (!xmit_blocks.has_block(bp.block_type())) { bp.prepare(bundle, xmit_blocks, null, link, BlockInfo.list_owner_t.LIST_NONE); } } // Now prepare security blocks BPF.getInstance().getBPFLogger().debug(TAG, "Now preparing security blocks..."); Security.prepare_out_blocks(bundle, link, xmit_blocks); return xmit_blocks; }
/** * Create a bundle fragment from another bundle. * * @param bundle the source bundle from which we create the fragment. Note: the bundle may itself * be a fragment * @param offset the offset relative to this bundle (not the original) for the for the new * fragment. note that if this bundle is already a fragment, the offset into the original * bundle will be this bundle's frag_offset + offset * @param length the length of the fragment we want * @return Newly created bundle */ public Bundle create_fragment(Bundle bundle, BlockInfoVec blocks, int offset, int length) { Bundle fragment = new Bundle(location_t.MEMORY); bundle.copy_metadata(fragment); fragment.set_is_fragment(true); fragment.set_do_not_fragment(false); // initialize the fragment's orig_length and figure out the offset // into the payload if (!bundle.is_fragment()) { fragment.set_orig_length(bundle.payload().length()); fragment.set_frag_offset(offset); } else { fragment.set_orig_length(bundle.orig_length()); fragment.set_frag_offset(bundle.frag_offset() + offset); } // check for overallocated length if ((offset + length) > fragment.orig_length()) { Logger.getInstance() .error( TAG, String.format( "fragment length overrun: " + "orig_length %d frag_offset %d requested offset %d length %d", fragment.orig_length(), fragment.frag_offset(), offset, length)); // Panic; return null; } // initialize payload fragment.payload().write_data(bundle.payload(), offset, length, 0); // copy all blocks that follow the payload, and all those before // the payload that are marked with the "must be replicated in every // fragment" bit ListIterator<BlockInfo> iter = blocks.listIterator(); // BlockInfoVec vec; boolean found_payload = false; while (iter.hasNext()) { BlockInfo entry = iter.next(); int type = entry.type().getCode(); if ((type == bundle_block_type_t.PRIMARY_BLOCK.getCode()) || (bundle_block_type_t.PAYLOAD_BLOCK.getCode() > 0) || found_payload || ((entry.flags() & block_flag_t.BLOCK_FLAG_REPLICATE.getCode()) > 0)) { // we need to include this block; copy the BlockInfo into the // fragment fragment.recv_blocks().add(entry); if (type == bundle_block_type_t.PAYLOAD_BLOCK.getCode()) { found_payload = true; } } } return fragment; }
/** * 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); }
/** * "Loop through the bundle's received block list to validate each entry." [DTN2] * * @return "true if the bundle is valid, false if it should be deleted." [DTN2] */ public static boolean validate( Bundle bundle, status_report_reason_t[] reception_reason, status_report_reason_t[] deletion_reason) { int primary_blocks = 0, payload_blocks = 0; BlockInfoVec recv_blocks = bundle.recv_blocks(); // "a bundle must include at least two blocks (primary and payload)" // [DTN2] if (recv_blocks.size() < 2) { BPF.getInstance().getBPFLogger().error(TAG, "bundle fails to contain at least two blocks"); deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE; return false; } // "the first block of a bundle must be a primary block" [DTN2] if (recv_blocks.front().type() != BundleProtocol.bundle_block_type_t.PRIMARY_BLOCK) { BPF.getInstance().getBPFLogger().error(TAG, "bundle fails to contain a primary block"); deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE; return false; } // "validate each individual block" [DTN2] int last_block_index = recv_blocks.size() - 1; for (int i = 0; i < recv_blocks.size(); i++) { BlockInfo current_block = recv_blocks.get(i); // "a block may not have enough data for the preamble" [DTN2] if (current_block.data_offset() == 0) { // "either the block is not the last one and something went // badly wrong, or it is the last block present" [DTN2] if (i != last_block_index) { BPF.getInstance().getBPFLogger().error(TAG, "bundle block too short for the preamble"); deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE; return false; } // "this is the last block, so drop it" [DTN2] BPF.getInstance().getBPFLogger().debug(TAG, "forgetting preamble-starved last block"); recv_blocks.remove(current_block); if (recv_blocks.size() < 2) { BPF.getInstance() .getBPFLogger() .error(TAG, "bundle fails to contain at least two blocks"); deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE; return false; } // "continue with the tests; results may have changed now that // a different block is last" [DTN2] return false; } else { if (current_block.type() == BundleProtocol.bundle_block_type_t.PRIMARY_BLOCK) { primary_blocks++; } if (current_block.type() == BundleProtocol.bundle_block_type_t.PAYLOAD_BLOCK) { payload_blocks++; } } if (!current_block .owner() .validate(bundle, recv_blocks, current_block, reception_reason, deletion_reason)) { return false; } // "a bundle's last block must be flagged as such, // and all other blocks should not be flagged" [DTN2] if (i == last_block_index) { if (!current_block.last_block()) { if (!bundle.fragmented_incoming()) { BPF.getInstance().getBPFLogger().error(TAG, "bundle's last block not flagged"); deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE; return false; } else { BPF.getInstance() .getBPFLogger() .debug(TAG, "bundle's last block not flagged, but " + "it is a reactive fragment"); } } } else { if (current_block.last_block()) { BPF.getInstance().getBPFLogger().error(TAG, "bundle block incorrectly flagged as last"); deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE; return false; } } } // "a bundle must contain one, and only one, primary block" [DTN2] if (primary_blocks != 1) { BPF.getInstance() .getBPFLogger() .error(TAG, String.format("bundle contains %d primary blocks", primary_blocks)); deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE; return false; } // "a bundle must not contain more than one payload block" [DTN2] if (payload_blocks > 1) { BPF.getInstance() .getBPFLogger() .error(TAG, String.format("bundle contains %d payload blocks", payload_blocks)); deletion_reason[0] = BundleProtocol.status_report_reason_t.REASON_BLOCK_UNINTELLIGIBLE; return false; } return true; }
/** * "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; }