--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/servlib/bundling/PrimaryBlockProcessor.cc Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,739 @@
+/*
+ * Copyright 2006 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <dtn-config.h>
+#endif
+
+#include <string>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "Bundle.h"
+#include "BundleProtocol.h"
+#include "PrimaryBlockProcessor.h"
+#include "naming/EndpointID.h"
+#include "SDNV.h"
+
+namespace dtn {
+
+//----------------------------------------------------------------------
+PrimaryBlockProcessor::PrimaryBlockProcessor()
+ : BlockProcessor(BundleProtocol::PRIMARY_BLOCK)
+{
+}
+
+//----------------------------------------------------------------------
+u_int64_t
+PrimaryBlockProcessor::format_bundle_flags(const Bundle* bundle)
+{
+ u_int64_t flags = 0;
+
+ if (bundle->is_fragment()) {
+ flags |= BUNDLE_IS_FRAGMENT;
+ }
+
+ if (bundle->is_admin()) {
+ flags |= BUNDLE_IS_ADMIN;
+ }
+
+ if (bundle->do_not_fragment()) {
+ flags |= BUNDLE_DO_NOT_FRAGMENT;
+ }
+
+ if (bundle->custody_requested()) {
+ flags |= BUNDLE_CUSTODY_XFER_REQUESTED;
+ }
+
+ if (bundle->singleton_dest()) {
+ flags |= BUNDLE_SINGLETON_DESTINATION;
+ }
+
+ if (bundle->app_acked_rcpt()) {
+ flags |= BUNDLE_ACK_BY_APP;
+ }
+
+ return flags;
+}
+
+//----------------------------------------------------------------------
+void
+PrimaryBlockProcessor::parse_bundle_flags(Bundle* bundle, u_int64_t flags)
+{
+ if (flags & BUNDLE_IS_FRAGMENT) {
+ bundle->set_is_fragment(true);
+ } else {
+ bundle->set_is_fragment(false);
+ }
+
+ if (flags & BUNDLE_IS_ADMIN) {
+ bundle->set_is_admin(true);
+ } else {
+ bundle->set_is_admin(false);
+ }
+
+ if (flags & BUNDLE_DO_NOT_FRAGMENT) {
+ bundle->set_do_not_fragment(true);
+ } else {
+ bundle->set_do_not_fragment(false);
+ }
+
+ if (flags & BUNDLE_CUSTODY_XFER_REQUESTED) {
+ bundle->set_custody_requested(true);
+ } else {
+ bundle->set_custody_requested(false);
+ }
+
+ if (flags & BUNDLE_SINGLETON_DESTINATION) {
+ bundle->set_singleton_dest(true);
+ } else {
+ bundle->set_singleton_dest(false);
+ }
+
+ if (flags & BUNDLE_ACK_BY_APP) {
+ bundle->set_app_acked_rcpt(true);
+ } else {
+ bundle->set_app_acked_rcpt(false);
+ }
+}
+
+//----------------------------------------------------------------------
+u_int64_t
+PrimaryBlockProcessor::format_cos_flags(const Bundle* b)
+{
+ u_int64_t cos_flags = 0;
+
+ cos_flags = ((b->priority() & 0x3) << 7);
+
+ return cos_flags;
+}
+
+//----------------------------------------------------------------------
+void
+PrimaryBlockProcessor::parse_cos_flags(Bundle* b, u_int64_t cos_flags)
+{
+ b->set_priority((cos_flags >> 7) & 0x3);
+}
+
+//----------------------------------------------------------------------
+u_int64_t
+PrimaryBlockProcessor::format_srr_flags(const Bundle* b)
+{
+ u_int64_t srr_flags = 0;
+
+ if (b->receive_rcpt())
+ srr_flags |= BundleProtocol::STATUS_RECEIVED;
+
+ if (b->custody_rcpt())
+ srr_flags |= BundleProtocol::STATUS_CUSTODY_ACCEPTED;
+
+ if (b->forward_rcpt())
+ srr_flags |= BundleProtocol::STATUS_FORWARDED;
+
+ if (b->delivery_rcpt())
+ srr_flags |= BundleProtocol::STATUS_DELIVERED;
+
+ if (b->deletion_rcpt())
+ srr_flags |= BundleProtocol::STATUS_DELETED;
+
+ return srr_flags;
+}
+
+//----------------------------------------------------------------------
+void
+PrimaryBlockProcessor::parse_srr_flags(Bundle* b, u_int64_t srr_flags)
+{
+ if (srr_flags & BundleProtocol::STATUS_RECEIVED)
+ b->set_receive_rcpt(true);
+
+ if (srr_flags & BundleProtocol::STATUS_CUSTODY_ACCEPTED)
+ b->set_custody_rcpt(true);
+
+ if (srr_flags & BundleProtocol::STATUS_FORWARDED)
+ b->set_forward_rcpt(true);
+
+ if (srr_flags & BundleProtocol::STATUS_DELIVERED)
+ b->set_delivery_rcpt(true);
+
+ if (srr_flags & BundleProtocol::STATUS_DELETED)
+ b->set_deletion_rcpt(true);
+}
+
+//----------------------------------------------------------------------
+size_t
+PrimaryBlockProcessor::get_primary_len(const Bundle* bundle,
+ Dictionary* dict,
+ PrimaryBlock* primary)
+{
+ static const char* log = "/dtn/bundle/protocol";
+ size_t primary_len = 0;
+ primary->dictionary_length = 0;
+ primary->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).
+ */
+ if (strncmp(bundle->dest().c_str(),"ipn", 3) == 0)
+ {
+ if(!get_ipn(bundle->dest(), &primary->dest_scheme_offset, &primary->dest_ssp_offset))
+ return 0;
+ get_ipn(bundle->source(), &primary->source_scheme_offset, &primary->source_ssp_offset);
+
+ get_ipn(bundle->replyto(), &primary->replyto_scheme_offset, &primary->replyto_ssp_offset);
+ get_ipn(bundle->custodian(), &primary->custodian_scheme_offset, &primary->custodian_ssp_offset);
+ primary->block_length += SDNV::encoding_len(primary->dest_scheme_offset);
+ primary->block_length += SDNV::encoding_len(primary->dest_ssp_offset);
+ primary->block_length += SDNV::encoding_len(primary->source_scheme_offset);
+ primary->block_length += SDNV::encoding_len(primary->source_ssp_offset);
+ primary->block_length += SDNV::encoding_len(primary->replyto_scheme_offset);
+ primary->block_length += SDNV::encoding_len(primary->replyto_ssp_offset);
+ primary->block_length += SDNV::encoding_len(primary->custodian_scheme_offset);
+ primary->block_length += SDNV::encoding_len(primary->custodian_ssp_offset);
+
+ primary->dictionary_length = 0;
+ }
+ else
+ {
+
+ dict->get_offsets(bundle->dest(),
+ &primary->dest_scheme_offset,
+ &primary->dest_ssp_offset);
+ primary->block_length += SDNV::encoding_len(primary->dest_scheme_offset);
+ primary->block_length += SDNV::encoding_len(primary->dest_ssp_offset);
+
+ dict->get_offsets(bundle->source(),
+ &primary->source_scheme_offset,
+ &primary->source_ssp_offset);
+ primary->block_length += SDNV::encoding_len(primary->source_scheme_offset);
+ primary->block_length += SDNV::encoding_len(primary->source_ssp_offset);
+
+ dict->get_offsets(bundle->replyto(),
+ &primary->replyto_scheme_offset,
+ &primary->replyto_ssp_offset);
+ primary->block_length += SDNV::encoding_len(primary->replyto_scheme_offset);
+ primary->block_length += SDNV::encoding_len(primary->replyto_ssp_offset);
+
+ dict->get_offsets(bundle->custodian(),
+ &primary->custodian_scheme_offset,
+ &primary->custodian_ssp_offset);
+ primary->block_length += SDNV::encoding_len(primary->custodian_scheme_offset);
+ primary->block_length += SDNV::encoding_len(primary->custodian_ssp_offset);
+
+ primary->dictionary_length = dict->length();
+}
+ (void)log; // in case NDEBUG is defined
+ log_debug_p(log, "generated dictionary length %llu",
+ U64FMT(primary->dictionary_length));
+
+ primary->block_length += SDNV::encoding_len(bundle->creation_ts().seconds_);
+ primary->block_length += SDNV::encoding_len(bundle->creation_ts().seqno_);
+ primary->block_length += SDNV::encoding_len(bundle->expiration());
+
+ primary->block_length += SDNV::encoding_len(primary->dictionary_length);
+ primary->block_length += primary->dictionary_length;
+
+ /*
+ * 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()) {
+ primary->block_length += SDNV::encoding_len(bundle->frag_offset());
+ primary->block_length += SDNV::encoding_len(bundle->orig_length());
+ }
+
+ // Format the processing flags.
+ primary->processing_flags = format_bundle_flags(bundle);
+ primary->processing_flags |= format_cos_flags(bundle);
+ primary->processing_flags |= format_srr_flags(bundle);
+
+ /*
+ * Finally, add up the initial preamble and the variable
+ * length part.
+ */
+ primary_len = 1 + SDNV::encoding_len(primary->processing_flags) +
+ SDNV::encoding_len(primary->block_length) +
+ primary->block_length;
+
+ log_debug_p(log, "get_primary_len(bundle %d): %zu",
+ bundle->bundleid(), primary_len);
+
+ // Fill in the remaining values of 'primary' just for the sake of returning
+ // a complete data structure.
+ primary->version = BundleProtocol::CURRENT_VERSION;
+ primary->creation_time = bundle->creation_ts().seconds_;
+ primary->creation_sequence = bundle->creation_ts().seqno_;
+ primary->lifetime = bundle->expiration();
+
+ return primary_len;
+}
+
+//----------------------------------------------------------------------
+int
+PrimaryBlockProcessor::prepare(const Bundle* bundle,
+ BlockInfoVec* xmit_blocks,
+ const BlockInfo* source,
+ const LinkRef& link,
+ list_owner_t list)
+{
+ (void)bundle;
+ (void)link;
+ (void)list;
+
+ // There shouldn't already be anything in the xmit_blocks
+ ASSERT(xmit_blocks->size() == 0);
+ // Add EIDs to start off the dictionary
+ xmit_blocks->dict()->add_eid(bundle->dest());
+ xmit_blocks->dict()->add_eid(bundle->source());
+ xmit_blocks->dict()->add_eid(bundle->replyto());
+ xmit_blocks->dict()->add_eid(bundle->custodian());
+
+ // make sure to add the primary to the front
+ xmit_blocks->insert(xmit_blocks->begin(), BlockInfo(this, source));
+
+ return BP_SUCCESS;
+}
+
+//----------------------------------------------------------------------
+
+void PrimaryBlockProcessor::make_ipn(char *eidbuf, u_int64_t eid, u_int64_t path)
+{
+ if(!eid)
+ {
+ sprintf(eidbuf, "dtn:none");
+ }
+ else
+ {
+ if(path>0)
+ {
+ sprintf(eidbuf, "ipn://%" PRIu64 "/%" PRIu64, eid, path);
+ }
+ else
+ {
+ sprintf(eidbuf, "ipn://%" PRIu64, eid);
+ }
+ }
+}
+
+//----------------------------------------------------------------------
+
+bool
+PrimaryBlockProcessor::get_ipn(const EndpointID& eid, u_int64_t* ieid, u_int64_t* itag)
+ {
+ const char* temp = eid.c_str();
+ if(!strcmp(temp, "dtn:none"))
+ {
+ *ieid = 0;
+ *itag = 0;
+ return true;
+ }
+ if(sscanf(temp, "ipn://%" PRIu64 "/%" PRIu64, ieid, itag) < 1)
+ return false;
+ return true;
+
+ }
+
+//----------------------------------------------------------------------
+
+int
+PrimaryBlockProcessor::consume(Bundle* bundle,
+ BlockInfo* block,
+ u_char* buf,
+ size_t len)
+{
+ static const char* log = "/dtn/bundle/protocol";
+ size_t consumed = 0;
+ PrimaryBlock primary;
+
+ ASSERT(! block->complete());
+
+ Dictionary* dict = bundle->mutable_recv_blocks()->dict();
+ memset(&primary, 0, sizeof(primary));
+
+ /*
+ * Now see if this completes the primary by calling into
+ * BlockProcessor::consume() to get the default behavior (now that
+ * we've found the data length above).
+ */
+ int cc = BlockProcessor::consume(bundle, block, buf, len);
+
+ if (cc == -1) {
+ return -1; // protocol error
+ }
+
+ if (! block->complete()) {
+ ASSERT(cc == (int)len);
+ return consumed + cc;
+ }
+
+ /*
+ * Ok, now the block is complete so we can parse it. We'll return
+ * the total amount consumed (or -1 for protocol error) when
+ * finished.
+ */
+ consumed += cc;
+
+ size_t primary_len = block->full_length();
+
+ buf = block->writable_contents()->buf();
+ len = block->writable_contents()->len();
+
+ ASSERT(primary_len == len);
+
+ primary.version = *(u_int8_t*)buf;
+ buf += 1;
+ len -= 1;
+
+ if (primary.version != BundleProtocol::CURRENT_VERSION) {
+ log_warn_p(log, "protocol version mismatch %d != %d",
+ primary.version, BundleProtocol::CURRENT_VERSION);
+ return -1;
+ }
+
+#define PBP_READ_SDNV(location) { \
+ int sdnv_len = SDNV::decode(buf, len, location); \
+ if (sdnv_len < 0) \
+ goto tooshort; \
+ buf += sdnv_len; \
+ len -= sdnv_len; }
+
+ // Grab the SDNVs representing the flags and the block length.
+ PBP_READ_SDNV(&primary.processing_flags);
+ PBP_READ_SDNV(&primary.block_length);
+
+ log_debug_p(log, "parsed primary block: version %d length %u",
+ primary.version, block->data_length());
+
+ // Parse the flags.
+ parse_bundle_flags(bundle, primary.processing_flags);
+ parse_cos_flags(bundle, primary.processing_flags);
+ parse_srr_flags(bundle, primary.processing_flags);
+
+ // What remains in the buffer should now be equal to what the block-length
+ // field advertised.
+ ASSERT(len == block->data_length());
+
+ // Read the various SDNVs up to the start of the dictionary.
+ PBP_READ_SDNV(&primary.dest_scheme_offset);
+ PBP_READ_SDNV(&primary.dest_ssp_offset);
+ PBP_READ_SDNV(&primary.source_scheme_offset);
+ PBP_READ_SDNV(&primary.source_ssp_offset);
+ PBP_READ_SDNV(&primary.replyto_scheme_offset);
+ PBP_READ_SDNV(&primary.replyto_ssp_offset);
+ PBP_READ_SDNV(&primary.custodian_scheme_offset);
+ PBP_READ_SDNV(&primary.custodian_ssp_offset);
+ PBP_READ_SDNV(&primary.creation_time);
+ PBP_READ_SDNV(&primary.creation_sequence);
+ PBP_READ_SDNV(&primary.lifetime);
+ PBP_READ_SDNV(&primary.dictionary_length);
+
+ bundle->set_creation_ts(BundleTimestamp(primary.creation_time,
+ primary.creation_sequence));
+ bundle->set_expiration(primary.lifetime);
+
+ /*
+ * Verify that we have the whole dictionary.
+ */
+ if (len < primary.dictionary_length) {
+ tooshort:
+ log_err_p(log, "primary block advertised incorrect length %u",
+ block->data_length());
+ return -1;
+ }
+
+ /*
+ * Make sure that the dictionary ends with a null byte.
+ */
+ if (buf[primary.dictionary_length - 1] != '\0') {
+ log_err_p(log, "dictionary does not end with a NULL character!");
+ return -1;
+ }
+
+ /*
+ * Now use the dictionary buffer to parse out the various endpoint
+ * identifiers, making sure that none of them peeks past the end
+ * of the dictionary block.
+ */
+ u_char* dictionary = buf;
+ buf += primary.dictionary_length;
+ len -= primary.dictionary_length;
+
+ if(primary.dictionary_length == 0)
+ {
+ char eid[52] = {0};
+ make_ipn(eid, primary.source_scheme_offset, primary.source_ssp_offset);
+ bundle->mutable_source()->assign(eid);
+
+ make_ipn(eid, primary.dest_scheme_offset, primary.dest_ssp_offset);
+ bundle->mutable_dest()->assign(eid);
+
+ make_ipn(eid, primary.replyto_scheme_offset, primary.replyto_ssp_offset);
+ bundle->mutable_replyto()->assign(eid);
+
+ make_ipn(eid, primary.custodian_scheme_offset, primary.custodian_ssp_offset);
+ bundle->mutable_custodian()->assign(eid);
+ }
+ else
+ {
+
+ dict->set_dict(dictionary, primary.dictionary_length);
+ dict->extract_eid(bundle->mutable_source(),
+ primary.source_scheme_offset,
+ primary.source_ssp_offset);
+
+ dict->extract_eid(bundle->mutable_dest(),
+ primary.dest_scheme_offset,
+ primary.dest_ssp_offset);
+
+ dict->extract_eid(bundle->mutable_replyto(),
+ primary.replyto_scheme_offset,
+ primary.replyto_ssp_offset);
+
+ dict->extract_eid(bundle->mutable_custodian(),
+ primary.custodian_scheme_offset,
+ primary.custodian_ssp_offset);
+}
+ // If the bundle is a fragment, grab the fragment offset and original
+ // bundle size (and make sure they fit in a 32 bit integer).
+ if (bundle->is_fragment()) {
+ u_int64_t sdnv_buf = 0;
+ PBP_READ_SDNV(&sdnv_buf);
+ if (sdnv_buf > 0xffffffff) {
+ log_err_p(log, "fragment offset is too large: %llu",
+ U64FMT(sdnv_buf));
+ return -1;
+ }
+
+ bundle->set_frag_offset(sdnv_buf);
+ sdnv_buf = 0;
+
+ PBP_READ_SDNV(&sdnv_buf);
+ if (sdnv_buf > 0xffffffff) {
+ log_err_p(log, "fragment original length is too large: %llu",
+ U64FMT(sdnv_buf));
+ return -1;
+ }
+
+ bundle->set_orig_length(sdnv_buf);
+
+ log_debug_p(log, "parsed fragmentation info: offset %u orig_len %u",
+ bundle->frag_offset(), bundle->orig_length());
+ }
+
+#undef PBP_READ_SDNV
+
+ return consumed;
+}
+
+//----------------------------------------------------------------------
+bool
+PrimaryBlockProcessor::validate(const Bundle* bundle,
+ BlockInfoVec* block_list,
+ BlockInfo* block,
+ status_report_reason_t* reception_reason,
+ status_report_reason_t* deletion_reason)
+{
+ (void)block_list;
+ (void)block;
+ (void)reception_reason;
+ static const char* log = "/dtn/bundle/protocol";
+
+ // Make sure all four EIDs are valid.
+ bool eids_valid = true;
+ eids_valid &= bundle->source().valid();
+ eids_valid &= bundle->dest().valid();
+ eids_valid &= bundle->custodian().valid();
+ eids_valid &= bundle->replyto().valid();
+
+ if (!eids_valid) {
+ log_err_p(log, "bad value for one or more EIDs");
+ *deletion_reason = BundleProtocol::REASON_BLOCK_UNINTELLIGIBLE;
+ return false;
+ }
+
+ // According to BP section 3.3, there are certain things that a bundle
+ // with a null source EID should not try to do. Check for these cases
+ // and reject the bundle if any is true.
+ if (bundle->source() == EndpointID::NULL_EID()) {
+ if (bundle->receipt_requested() || bundle->app_acked_rcpt()) {
+ log_err_p(log,
+ "bundle with null source eid has requested a "
+ "report; reject it");
+ *deletion_reason = BundleProtocol::REASON_BLOCK_UNINTELLIGIBLE;
+ return false;
+ }
+
+ if (bundle->custody_requested()) {
+ log_err_p(log,
+ "bundle with null source eid has requested custody "
+ "transfer; reject it");
+ *deletion_reason = BundleProtocol::REASON_BLOCK_UNINTELLIGIBLE;
+ return false;
+ }
+
+ if (!bundle->do_not_fragment()) {
+ log_err_p(log,
+ "bundle with null source eid has not set "
+ "'do-not-fragment' flag; reject it");
+ *deletion_reason = BundleProtocol::REASON_BLOCK_UNINTELLIGIBLE;
+ return false;
+ }
+ }
+
+ // Admin bundles cannot request custody transfer.
+ if (bundle->is_admin()) {
+ if (bundle->custody_requested()) {
+ log_err_p(log,
+ "admin bundle requested custody transfer; "
+ "reject it");
+ *deletion_reason = BundleProtocol::REASON_BLOCK_UNINTELLIGIBLE;
+ return false;
+ }
+
+ if ( bundle->receive_rcpt() ||
+ bundle->custody_rcpt() ||
+ bundle->forward_rcpt() ||
+ bundle->delivery_rcpt() ||
+ bundle->deletion_rcpt() ||
+ bundle->app_acked_rcpt() )
+ {
+ log_err_p(log, "admin bundle has requested a report; reject it");
+ *deletion_reason = BundleProtocol::REASON_BLOCK_UNINTELLIGIBLE;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------
+int
+PrimaryBlockProcessor::generate(const Bundle* bundle,
+ BlockInfoVec* xmit_blocks,
+ BlockInfo* block,
+ const LinkRef& link,
+ bool last)
+{
+ (void)bundle;
+ (void)link;
+ (void)xmit_blocks;
+ (void)block;
+
+ /*
+ * The primary can't be last since there must be a payload block
+ */
+ ASSERT(!last);
+
+ return BP_SUCCESS;
+}
+
+//----------------------------------------------------------------------
+void
+PrimaryBlockProcessor::generate_primary(const Bundle* bundle,
+ BlockInfoVec* xmit_blocks,
+ BlockInfo* block)
+{
+ static const char* log = "/dtn/bundle/protocol";
+ // point at the local dictionary
+ Dictionary* dict = xmit_blocks->dict();
+ size_t primary_len = 0; // total length of the primary block
+ PrimaryBlock primary;
+
+ memset(&primary, 0, sizeof(primary));
+
+ /*
+ * Calculate the primary block length and initialize the buffer.
+ */
+ primary_len = get_primary_len(bundle, dict, &primary);
+ block->writable_contents()->reserve(primary_len);
+ block->writable_contents()->set_len(primary_len);
+ block->set_data_length(primary_len);
+
+ /*
+ * Advance buf and decrement len as we go through the process.
+ */
+ u_char* buf = block->writable_contents()->buf();
+ int len = primary_len;
+
+ (void)log; // in case NDEBUG is defined
+ log_debug_p(log, "generating primary: length %zu", primary_len);
+
+ // Stick the version number in the first byte.
+ *buf = BundleProtocol::CURRENT_VERSION;
+ ++buf;
+ --len;
+
+#define PBP_WRITE_SDNV(value) { \
+ int sdnv_len = SDNV::encode(value, buf, len); \
+ ASSERT(sdnv_len > 0); \
+ buf += sdnv_len; \
+ len -= sdnv_len; }
+
+ // Write out all of the SDNVs
+ PBP_WRITE_SDNV(primary.processing_flags);
+ PBP_WRITE_SDNV(primary.block_length);
+ PBP_WRITE_SDNV(primary.dest_scheme_offset);
+ PBP_WRITE_SDNV(primary.dest_ssp_offset);
+ PBP_WRITE_SDNV(primary.source_scheme_offset);
+ PBP_WRITE_SDNV(primary.source_ssp_offset);
+ PBP_WRITE_SDNV(primary.replyto_scheme_offset);
+ PBP_WRITE_SDNV(primary.replyto_ssp_offset);
+ PBP_WRITE_SDNV(primary.custodian_scheme_offset);
+ PBP_WRITE_SDNV(primary.custodian_ssp_offset);
+ PBP_WRITE_SDNV(bundle->creation_ts().seconds_);
+ PBP_WRITE_SDNV(bundle->creation_ts().seqno_);
+ PBP_WRITE_SDNV(bundle->expiration());
+ PBP_WRITE_SDNV(primary.dictionary_length);
+
+ // Add the dictionary.
+// memcpy(buf, dict->dict(), dict->length());
+ memcpy(buf, dict->dict(), primary.dictionary_length);
+ // buf += dict->length();
+// len -= dict->length();
+ buf += primary.dictionary_length;
+ len -= primary.dictionary_length;
+
+ /*
+ * If the bundle is a fragment, stuff in SDNVs for the fragment
+ * offset and original length.
+ */
+ if (bundle->is_fragment()) {
+ PBP_WRITE_SDNV(bundle->frag_offset());
+ PBP_WRITE_SDNV(bundle->orig_length());
+ }
+
+#undef PBP_WRITE_SDNV
+
+ /*
+ * Asuming that get_primary_len is written correctly, len should
+ * now be zero since we initialized it to primary_len at the
+ * beginning of the function.
+ */
+ ASSERT(len == 0);
+}
+
+
+} // namespace dtn