// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0 #include "bundle6/create.h" #include "bundle6/reports.h" #include "bundle6/serializer.h" #include "bundle6/parser.h" #include "ud3tn/bundle.h" #include "ud3tn/eid.h" #include "ud3tn/result.h" #include "ud3tn/report_manager.h" #include "platform/hal_time.h" #include "testud3tn_unity.h" #include "testud3tn_eid_helpers.h" #include #include #include static const char test_payload[] = "PAYLOAD"; static struct bundle *some_bundle; static bool verify_ok; static const uint64_t creation_timestamp_s = 658489863; // 2020-11-12T09:51:03+00:00 static const uint64_t lifetime_s = 42; TEST_GROUP(bundle6ParserSerializer); TEST_SETUP(bundle6ParserSerializer) { char *payload = malloc(sizeof(test_payload)); memcpy(payload, test_payload, sizeof(test_payload)); some_bundle = bundle6_create_local( payload, sizeof(test_payload), EID_DTN_UD3TN_AGENT, EID_DTN_UD3TN_AGENT2, EID_DTN_UD3TN_AGENT, creation_timestamp_s * 1000, 1, lifetime_s * 1000, BUNDLE_FLAG_REPORT_DELIVERY | BUNDLE_V6_FLAG_CUSTODY_TRANSFER_REQUESTED ); some_bundle->current_custodian = eid_dup(EID_NULL_DTN); struct bundle_block *block = bundle_block_create( BUNDLE_BLOCK_TYPE_HOP_COUNT ); block->length = 1; block->data = malloc(1); block->flags = ( BUNDLE_V6_BLOCK_FLAG_HAS_EID_REF_FIELD | BUNDLE_BLOCK_FLAG_MUST_BE_REPLICATED | BUNDLE_V6_BLOCK_FLAG_LAST_BLOCK ); struct eid_list *eid_list = malloc(sizeof(struct eid_list)); eid_list->eid = eid_dup(EID_IPN_11); eid_list->next = NULL; block->eid_refs = eid_list; some_bundle->blocks->next = bundle_block_entry_create(block); some_bundle->payload_block->flags = BUNDLE_BLOCK_FLAG_NONE; } TEST_TEAR_DOWN(bundle6ParserSerializer) { bundle_free(some_bundle); } static void verify_bundle(struct bundle *b) { TEST_ASSERT_NOT_NULL(b); // Check header TEST_ASSERT_EQUAL(6, b->protocol_version); TEST_ASSERT_EQUAL( BUNDLE_V6_FLAG_SINGLETON_ENDPOINT | BUNDLE_FLAG_REPORT_DELIVERY | BUNDLE_V6_FLAG_CUSTODY_TRANSFER_REQUESTED, b->proc_flags ); TEST_ASSERT_EQUAL(1, b->sequence_number); TEST_ASSERT_EQUAL(658489863000, b->creation_timestamp_ms); TEST_ASSERT_EQUAL(42000, b->lifetime_ms); TEST_ASSERT_NOT_EQUAL(0, b->primary_block_length); TEST_ASSERT(eid_eq(EID_DTN_UD3TN_AGENT, b->source)); TEST_ASSERT(eid_eq(EID_DTN_UD3TN_AGENT2, b->destination)); TEST_ASSERT(eid_eq(EID_DTN_UD3TN_AGENT, b->report_to)); TEST_ASSERT(eid_eq(EID_NULL_DTN, b->current_custodian)); // Check blocks TEST_ASSERT_NOT_NULL(b->blocks); TEST_ASSERT_NOT_NULL(b->blocks->data); TEST_ASSERT_NOT_NULL(b->blocks->next); TEST_ASSERT_NOT_NULL(b->blocks->next->data); TEST_ASSERT_NULL(b->blocks->next->next); TEST_ASSERT_NOT_NULL(b->payload_block); TEST_ASSERT_EQUAL_PTR(b->payload_block, b->blocks->data); // Check payload block TEST_ASSERT_EQUAL( BUNDLE_BLOCK_FLAG_NONE, b->payload_block->flags ); TEST_ASSERT_EQUAL( sizeof(test_payload), b->payload_block->length ); TEST_ASSERT_EQUAL_UINT8_ARRAY( test_payload, b->payload_block->data, sizeof(test_payload) ); // Check next block with EID refs TEST_ASSERT_EQUAL( BUNDLE_V6_BLOCK_FLAG_HAS_EID_REF_FIELD | BUNDLE_BLOCK_FLAG_MUST_BE_REPLICATED | BUNDLE_V6_BLOCK_FLAG_LAST_BLOCK, b->blocks->next->data->flags ); TEST_ASSERT_EQUAL(1, b->blocks->next->data->length); TEST_ASSERT_NOT_NULL(b->blocks->next->data->eid_refs); TEST_ASSERT_NULL(b->blocks->next->data->eid_refs->next); TEST_ASSERT(eid_eq( EID_IPN_11, b->blocks->next->data->eid_refs->eid )); verify_ok = true; } static void verify_and_free_bundle(struct bundle *b, void *param) { (void)param; verify_bundle(b); bundle_free(b); } struct buf_info { uint8_t *buf; size_t pos; }; static enum ud3tn_result _write(void *buf_info, const void *data, const size_t len) { struct buf_info *bi = buf_info; memcpy(&bi->buf[bi->pos], data, len); bi->pos += len; return UD3TN_OK; } TEST(bundle6ParserSerializer, parse_and_serialize) { verify_bundle(some_bundle); TEST_ASSERT_TRUE(verify_ok); const size_t serialized_size_before = bundle_get_serialized_size(some_bundle); uint8_t *serializebuffer = malloc(serialized_size_before); struct buf_info bi = { .buf = serializebuffer, .pos = 0, }; TEST_ASSERT_NOT_NULL(serializebuffer); TEST_ASSERT_NOT_EQUAL(0, serialized_size_before); TEST_ASSERT_EQUAL(UD3TN_OK, bundle6_serialize(some_bundle, _write, &bi)); const size_t serialized_size_after = bi.pos; TEST_ASSERT_EQUAL(serialized_size_before, serialized_size_after); struct bundle6_parser p; bundle6_parser_init(&p, verify_and_free_bundle, NULL); verify_ok = false; bundle6_parser_read(&p, serializebuffer, serialized_size_after); TEST_ASSERT_EQUAL(PARSER_STATUS_DONE, p.basedata->status); TEST_ASSERT_TRUE(verify_ok); bundle6_parser_reset(&p); TEST_ASSERT_EQUAL(PARSER_STATUS_GOOD, p.basedata->status); free(serializebuffer); bundle_free(p.bundle); free(p.basedata); } // Bundle sourced and captured from ION v3.7.4, see #7 for details static uint8_t ION_TEST_BUNDLE_IPN_CBHE[] = { // Primary Block // Version 0x06, // Processing Flags 0x81, 0x14, // Length 0x11, // Destination: ipn:3.1 (CBHE) 0x03, 0x01, // Source: dtn:none (CBHE) 0x00, 0x00, // Report-to: dtn:none (CBHE) 0x00, 0x00, // Custodian: dtn:none (CBHE) 0x00, 0x00, // Timestamp 0x82, 0xDC, 0xAE, 0xC7, 0x2A, // Seqnum 0x01, // Lifetime 0x82, 0x2C, // Dict. Length 0x00, // 2nd Block (type 5) 0x05, // Flags: discard if unproc 0x10, // Length: 8 bytes 0x08, // Data 0x69, 0x70, 0x6E, 0x00, 0x31, 0x2E, 0x30, 0x00, // 3rd block (type 20) 0x14, // Flags: replicate in every fragment 0x01, // Length: 1 byte 0x01, // Data 0x00, // Payload Block (type 1) 0x01, // Flags: "last block", "must be replicated" 0x09, // Length: 10 bytes 0x0A, // "hallo welt" 0x68, 0x61, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x65, 0x6C, 0x74 }; static void verify_and_free_ipn_cbhe_bundle(struct bundle *b, void *param) { (void)param; TEST_ASSERT_NOT_NULL(b); TEST_ASSERT_NOT_NULL(b->payload_block); TEST_ASSERT_NOT_NULL(b->blocks); TEST_ASSERT_NOT_NULL(b->blocks->data); TEST_ASSERT_NOT_NULL(b->blocks->next); TEST_ASSERT_NOT_NULL(b->blocks->next->data); TEST_ASSERT_NOT_NULL(b->blocks->next->next); TEST_ASSERT_NOT_NULL(b->blocks->next->next->data); TEST_ASSERT_NULL(b->blocks->next->next->next); TEST_ASSERT_EQUAL(6, b->protocol_version); TEST_ASSERT_EQUAL( BUNDLE_V6_FLAG_SINGLETON_ENDPOINT | BUNDLE_FLAG_MUST_NOT_BE_FRAGMENTED | BUNDLE_V6_FLAG_NORMAL_PRIORITY, b->proc_flags ); TEST_ASSERT_EQUAL(1, b->sequence_number); TEST_ASSERT_EQUAL(300000, b->lifetime_ms); TEST_ASSERT_NOT_EQUAL(10, b->primary_block_length); TEST_ASSERT(eid_eq( (struct eid){ .scheme = EID_SCHEME_IPN, .ipn_eid = (struct ipn_eid){ .fully_qualified_node_number = 3, .service_number = 1, }, }, b->destination )); TEST_ASSERT(eid_is_null(b->source)); TEST_ASSERT(eid_is_null(b->report_to)); TEST_ASSERT(eid_is_null(b->current_custodian)); TEST_ASSERT_EQUAL_PTR(b->payload_block, b->blocks->next->next->data); TEST_ASSERT_EQUAL(BUNDLE_BLOCK_TYPE_PAYLOAD, b->payload_block->type); TEST_ASSERT_EQUAL( BUNDLE_BLOCK_FLAG_MUST_BE_REPLICATED | BUNDLE_V6_BLOCK_FLAG_LAST_BLOCK, b->payload_block->flags ); TEST_ASSERT_EQUAL_UINT8_ARRAY("hallo welt", b->payload_block->data, 10); // other blocks TEST_ASSERT_EQUAL( BUNDLE_BLOCK_FLAG_DISCARD_IF_UNPROC, b->blocks->data->flags ); bundle_free(b); } TEST(bundle6ParserSerializer, parse_cbhe) { struct bundle6_parser p; bundle6_parser_init(&p, verify_and_free_ipn_cbhe_bundle, NULL); bundle6_parser_read( &p, ION_TEST_BUNDLE_IPN_CBHE, sizeof(ION_TEST_BUNDLE_IPN_CBHE) ); TEST_ASSERT_EQUAL(PARSER_STATUS_DONE, p.basedata->status); bundle6_parser_reset(&p); TEST_ASSERT_EQUAL(PARSER_STATUS_GOOD, p.basedata->status); bundle_free(p.bundle); free(p.basedata); } static enum ud3tn_result write_fail(void *cla_obj, const void *data, const size_t len) { int *counter = cla_obj; (*counter)++; return UD3TN_FAIL; } TEST(bundle6ParserSerializer, serialize_write_fail) { int counter = 0; TEST_ASSERT_EQUAL(UD3TN_FAIL, bundle6_serialize(some_bundle, write_fail, &counter)); TEST_ASSERT_EQUAL(1, counter); } TEST(bundle6ParserSerializer, serialize_and_parse_status_report) { const uint64_t TIMESTAMP = creation_timestamp_s * 1000 + 5000; struct bundle *const sr_bundle = bundle6_generate_status_report( some_bundle, BUNDLE_SR_FLAG_BUNDLE_DELETED, BUNDLE_SR_REASON_NO_KNOWN_ROUTE, EID_DTN_UD3TN, TIMESTAMP ); TEST_ASSERT_NOT_NULL(sr_bundle); TEST_ASSERT_EQUAL(6, sr_bundle->protocol_version); TEST_ASSERT(eid_eq(EID_DTN_UD3TN, sr_bundle->source)); TEST_ASSERT(eid_eq(some_bundle->report_to, sr_bundle->destination)); TEST_ASSERT_EQUAL_UINT64(TIMESTAMP, sr_bundle->creation_timestamp_ms); TEST_ASSERT(HAS_FLAG(sr_bundle->proc_flags, BUNDLE_FLAG_ADMINISTRATIVE_RECORD)); TEST_ASSERT_EQUAL_UINT64( (creation_timestamp_s + lifetime_s) * 1000 - TIMESTAMP, sr_bundle->lifetime_ms ); struct bundle_administrative_record *const ar = bundle6_parse_administrative_record( sr_bundle->payload_block->data, sr_bundle->payload_block->length ); TEST_ASSERT_NOT_NULL(ar); TEST_ASSERT_NOT_NULL(ar->status_report); TEST_ASSERT_EQUAL(BUNDLE_SR_FLAG_BUNDLE_DELETED, ar->status_report->status); TEST_ASSERT_EQUAL(BUNDLE_SR_REASON_NO_KNOWN_ROUTE, ar->status_report->reason); TEST_ASSERT_EQUAL_UINT64(TIMESTAMP, ar->status_report->bundle_deleted_time); TEST_ASSERT_EQUAL_UINT64(0, ar->status_report->bundle_delivered_time); TEST_ASSERT_EQUAL_UINT64(0, ar->status_report->bundle_forwarded_time); TEST_ASSERT_EQUAL_UINT64(0, ar->status_report->bundle_received_time); TEST_ASSERT_EQUAL_UINT64(some_bundle->creation_timestamp_ms, ar->bundle_creation_timestamp_ms); TEST_ASSERT_EQUAL_UINT64(some_bundle->sequence_number, ar->bundle_sequence_number); TEST_ASSERT(eid_eq(some_bundle->source, ar->bundle_source_eid)); free_administrative_record(ar); bundle_free(sr_bundle); } TEST_GROUP_RUNNER(bundle6ParserSerializer) { RUN_TEST_CASE(bundle6ParserSerializer, parse_and_serialize); RUN_TEST_CASE(bundle6ParserSerializer, parse_cbhe); RUN_TEST_CASE(bundle6ParserSerializer, serialize_write_fail); RUN_TEST_CASE(bundle6ParserSerializer, serialize_and_parse_status_report); }