/** * "Generate contents for the given BlockInfoVec on the given Link." [DTN2] * * @return "the total length of the formatted blocks for this bundle." [DTN2] */ public static int generate_blocks(Bundle bundle, BlockInfoVec blocks, final Link link) { // "now assert there's at least 2 blocks (primary + payload) and // that the primary is first" [DTN2] assert (blocks.size() >= 2); assert (blocks.front().type() == bundle_block_type_t.PRIMARY_BLOCK); // "now we make a pass through the list and call generate on // each block processor" [DTN2] for (int i = 0; i < blocks.size(); i++) { boolean last = i == blocks.size() - 1; BlockInfo iter = blocks.get(i); iter.owner().generate(bundle, blocks, iter, link, last); BPF.getInstance() .getBPFLogger() .debug( TAG, String.format( "generated block (owner %s type %s) " + "data_offset %d data_length %d , contents_length %d", iter.owner().block_type(), iter.type(), iter.data_offset(), iter.data_length(), iter.contents().position())); if (last) { assert ((iter.flags() & BundleProtocol.block_flag_t.BLOCK_FLAG_LAST_BLOCK.getCode()) != 0); } else { assert ((iter.flags() & BundleProtocol.block_flag_t.BLOCK_FLAG_LAST_BLOCK.getCode()) == 0); } } // "Now that all the EID references are added to the dictionary, // generate the primary block." [DTN2] PrimaryBlockProcessor pbp = (PrimaryBlockProcessor) find_processor(BundleProtocol.bundle_block_type_t.PRIMARY_BLOCK); assert (blocks.front().owner() == pbp); pbp.generate_primary(bundle, blocks, blocks.front()); // "make a final pass through, calling finalize() and extracting // the block length" [DTN2] int total_len = 0; for (int i = blocks.size() - 1; i >= 0; i--) { BlockInfo iter = blocks.get(i); iter.owner().finalize(bundle, blocks, iter, link); total_len += iter.full_length(); } return total_len; }
/** * "Temporary helper function to find the offset of the first byte of the payload in a * BlockInfoVec." [DTN2] */ public static int payload_offset(final BlockInfoVec blocks) { int ret = 0; Iterator<BlockInfo> itr = blocks.iterator(); while (itr.hasNext()) { BlockInfo block = itr.next(); if (block.type() == BundleProtocol.bundle_block_type_t.PAYLOAD_BLOCK) { ret += block.data_offset(); return ret; } ret += block.full_length(); } return ret; }
/** * "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; }
/** * "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; }