ud3tn/test/unit/test_bundle6ParserSerializer.c
Felix Walter a231ed5ed3 Pass timestamps in status reports
In outgoing status reports, previously, we did not send timestamps, even
in case they were requested for BPv7 bundles. This refactors the
corresponding functions to generate status reports, so we assign the
proper timestamp depending on the status flag.

Note that we never supported sending a status report capturing multiple
events at once -- only the BPv7 SR serializer supports the generation.

Signed-off-by: Felix Walter <felix.walter@d3tn.com>
2026-03-19 19:22:45 +01:00

394 lines
10 KiB
C

// 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 <string.h>
#include <stdlib.h>
#include <stdbool.h>
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);
}