ud3tn/test/unit/test_bundle7Parser.c
Felix Walter 17aec0f909 test/unit: Free used heap-allocated data
Closes: #104

Signed-off-by: Felix Walter <felix.walter@d3tn.com>
2026-03-18 09:57:10 +01:00

591 lines
16 KiB
C

// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
/**
* Unit Test for BPbis parser
*
* Raw CBOR data is loaded from "test_bundle7Data.c".
*/
#include "bundle7/bundle_age.h"
#include "bundle7/eid.h"
#include "bundle7/hopcount.h"
#include "bundle7/parser.h"
#include "bundle7/reports.h"
#include "ud3tn/bundle.h"
#include "ud3tn/report_manager.h"
#include "testud3tn_unity.h"
#include "testud3tn_eid_helpers.h"
#include <stdlib.h>
#include <string.h>
TEST_GROUP(bundle7Parser);
extern uint8_t cbor_simple_bundle[];
extern size_t len_simple_bundle;
static struct bundle *bundle;
static void send_callback(struct bundle *_bundle, void *param)
{
(void)param;
bundle = _bundle;
}
TEST_SETUP(bundle7Parser)
{
bundle = NULL;
}
TEST_TEAR_DOWN(bundle7Parser)
{
if (bundle != NULL) {
bundle_free(bundle);
bundle = NULL;
}
}
TEST(bundle7Parser, eid_parser)
{
struct eid eid;
// dtn
const uint8_t sat_1[] = { 0x82, 0x01, 0x64, 0x2f, 0x2f, 0x54, 0x31 };
// ipn 2-element
const uint8_t ipn[] = { 0x82, 0x02, 0x82, 0x18, 0x2a, 0x18, 0x48 };
const uint8_t ipn2[] = { 0x82, 0x02, 0x82, 0x18, 0x2a, 0x00 };
const uint8_t ipn3[] = { 0x82, 0x02, 0x82, 0x00, 0x00 }; // ipn 2-elem null
const uint8_t ipn4[] = {
0x82, 0x02, 0x82,
0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};
// ipn 3-element
const uint8_t ipn_31[] = { 0x82, 0x02, 0x83, 0x00, 0x18, 0x2a, 0x18, 0x48 };
const uint8_t ipn_32[] = { 0x82, 0x02, 0x83, 0x18, 0x2a, 0x00, 0x00 };
const uint8_t ipn_33[] = { 0x82, 0x02, 0x83, 0x00, 0x00, 0x00, 0x00 }; // ipn 3-elem null
const uint8_t ipn_34[] = {
0x82, 0x02, 0x83,
0x1a, 0xff, 0xff, 0xff, 0xff,
0x1a, 0xff, 0xff, 0xff, 0xff,
0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};
// dtn:none
const uint8_t none[] = { 0x82, 0x01, 0x00 };
eid = bundle7_eid_parse(sat_1, sizeof(sat_1));
TEST_ASSERT_FALSE(eid_is_error(eid));
TEST_ASSERT(eid_eq(EID_DTN("dtn://T1"), eid));
eid_free(eid);
eid = bundle7_eid_parse(ipn, sizeof(ipn));
TEST_ASSERT_FALSE(eid_is_error(eid));
TEST_ASSERT(eid_eq(EID_IPN_S("ipn:42.72"), eid));
eid_free(eid);
eid = bundle7_eid_parse(ipn2, sizeof(ipn2));
TEST_ASSERT_FALSE(eid_is_error(eid));
TEST_ASSERT(eid_eq(EID_IPN_S("ipn:42.0"), eid));
eid_free(eid);
eid = bundle7_eid_parse(ipn3, sizeof(ipn3));
TEST_ASSERT_FALSE(eid_is_error(eid));
TEST_ASSERT(eid_is_null(eid));
TEST_ASSERT_EQUAL_AEID(EID_IPN_S("ipn:0.0"), eid);
TEST_ASSERT(eid_eq(EID_NULL_DTN, eid));
TEST_ASSERT(eid_eq(EID_NULL_IPN, eid));
eid_free(eid);
eid = bundle7_eid_parse(ipn4, sizeof(ipn4));
TEST_ASSERT_FALSE(eid_is_error(eid));
TEST_ASSERT(eid_eq(EID_IPN_S("ipn:18446744073709551615.18446744073709551615"), eid));
eid_free(eid);
eid = bundle7_eid_parse(ipn_31, sizeof(ipn_31));
TEST_ASSERT_FALSE(eid_is_error(eid));
TEST_ASSERT_EQUAL_AEID(EID_IPN_3(0, 42, 72), eid);
eid_free(eid);
eid = bundle7_eid_parse(ipn_32, sizeof(ipn_32));
TEST_ASSERT_FALSE(eid_is_error(eid));
TEST_ASSERT_EQUAL_AEID(EID_IPN_3(42, 0, 0), eid);
eid_free(eid);
eid = bundle7_eid_parse(ipn_33, sizeof(ipn_33));
TEST_ASSERT_FALSE(eid_is_error(eid));
TEST_ASSERT_EQUAL_AEID(EID_IPN_S("ipn:0.0.0"), eid);
eid_free(eid);
eid = bundle7_eid_parse(ipn_34, sizeof(ipn_34));
TEST_ASSERT_FALSE(eid_is_error(eid));
TEST_ASSERT(eid_eq(EID_IPN(18446744073709551615ULL, 18446744073709551615ULL), eid));
eid_free(eid);
eid = bundle7_eid_parse(none, sizeof(none));
TEST_ASSERT_FALSE(eid_is_error(eid));
TEST_ASSERT(eid_is_null(eid));
TEST_ASSERT(eid_eq(EID_IPN_S("ipn:0.0"), eid));
TEST_ASSERT(eid_eq(EID_NULL_DTN, eid));
TEST_ASSERT(eid_eq(EID_NULL_IPN, eid));
eid_free(eid);
}
static void parse_bundle_chunked(const size_t chunk_size)
{
struct bundle7_parser state;
struct parser *parser = bundle7_parser_init(
&state,
&send_callback,
NULL
);
TEST_ASSERT_NOT_NULL(parser);
size_t read = 0;
size_t chunk = chunk_size;
while (read < len_simple_bundle && state.basedata->status == PARSER_STATUS_GOOD) {
if (state.basedata->flags & PARSER_FLAG_BULK_READ) {
TEST_ASSERT_TRUE(read + state.basedata->next_bytes
< len_simple_bundle);
// Bulk read operation
memcpy(state.basedata->next_buffer,
cbor_simple_bundle + read,
state.basedata->next_bytes);
// Advance parsing pointer
read += state.basedata->next_bytes;
// Deactivate bulk read mode
state.basedata->flags &= ~PARSER_FLAG_BULK_READ;
} else {
if (chunk + read >= len_simple_bundle)
chunk = len_simple_bundle - read;
size_t parsed = bundle7_parser_read(&state,
cbor_simple_bundle + read,
chunk);
if (parsed == 0) {
chunk *= 2;
} else {
chunk = chunk_size;
read += parsed;
}
}
TEST_ASSERT_NOT_EQUAL(state.basedata->status,
PARSER_STATUS_ERROR);
}
TEST_ASSERT_EQUAL(PARSER_STATUS_DONE, state.basedata->status);
TEST_ASSERT_FALSE(state.basedata->flags & PARSER_FLAG_CRC_INVALID);
TEST_ASSERT_NULL(state.bundle);
TEST_ASSERT_NOT_NULL(bundle);
// Creation Timestamp 2020-11-12T09:51:03
TEST_ASSERT_EQUAL(658489863000, bundle->creation_timestamp_ms);
// Primary block CRC
TEST_ASSERT_EQUAL(BUNDLE_CRC_TYPE_NONE, bundle->crc_type);
// Bundle Processing flags
TEST_ASSERT_TRUE(bundle->proc_flags
& BUNDLE_FLAG_MUST_NOT_BE_FRAGMENTED);
TEST_ASSERT_TRUE(bundle->proc_flags
& BUNDLE_FLAG_REPORT_DELIVERY);
TEST_ASSERT_FALSE(bundle->proc_flags
& BUNDLE_FLAG_IS_FRAGMENT);
TEST_ASSERT_FALSE(bundle->proc_flags
& BUNDLE_FLAG_ADMINISTRATIVE_RECORD);
TEST_ASSERT_FALSE(bundle->proc_flags
& BUNDLE_FLAG_ACKNOWLEDGEMENT_REQUESTED);
TEST_ASSERT_FALSE(bundle->proc_flags
& BUNDLE_V6_FLAG_NORMAL_PRIORITY);
TEST_ASSERT_FALSE(bundle->proc_flags
& BUNDLE_V6_FLAG_EXPEDITED_PRIORITY);
TEST_ASSERT_FALSE(bundle->proc_flags
& BUNDLE_FLAG_REPORT_RECEPTION);
TEST_ASSERT_FALSE(bundle->proc_flags
& BUNDLE_FLAG_REPORT_FORWARDING);
TEST_ASSERT_FALSE(bundle->proc_flags
& BUNDLE_FLAG_REPORT_DELETION);
TEST_ASSERT_FALSE(bundle->proc_flags
& BUNDLE_FLAG_REPORT_STATUS_TIME);
struct bundle_block_list *block_element = bundle->blocks;
// Previous node block
TEST_ASSERT_EQUAL(BUNDLE_BLOCK_TYPE_PREVIOUS_NODE,
block_element->data->type);
TEST_ASSERT_EQUAL(BUNDLE_BLOCK_FLAG_NONE,
block_element->data->flags);
TEST_ASSERT_EQUAL(6, block_element->data->length);
uint8_t previous_node[6] = {
0x82, 0x01, 0x63, 0x47, 0x53, 0x34
};
TEST_ASSERT_EQUAL_INT8_ARRAY(previous_node,
block_element->data->data, 6);
TEST_ASSERT_EQUAL(BUNDLE_CRC_TYPE_NONE, block_element->data->crc_type);
// Hop count block
block_element = block_element->next;
TEST_ASSERT_EQUAL(BUNDLE_BLOCK_TYPE_HOP_COUNT,
block_element->data->type);
TEST_ASSERT_EQUAL(4, block_element->data->length);
uint8_t hop_count[4] = {
0x82, 0x18, 0x1e, 0x00
};
TEST_ASSERT_EQUAL_INT8_ARRAY(hop_count, block_element->data->data, 4);
TEST_ASSERT_EQUAL(BUNDLE_CRC_TYPE_NONE, block_element->data->crc_type);
// Bundle age block
block_element = block_element->next;
TEST_ASSERT_EQUAL(BUNDLE_BLOCK_TYPE_BUNDLE_AGE,
block_element->data->type);
TEST_ASSERT_EQUAL(1, block_element->data->length);
uint8_t bundle_age[1] = { 0x00 };
TEST_ASSERT_EQUAL_INT8_ARRAY(bundle_age, block_element->data->data, 1);
// Payload
block_element = block_element->next;
TEST_ASSERT_EQUAL_PTR(block_element->data,
bundle->payload_block);
// Payload block must be the last block
TEST_ASSERT_NULL(block_element->next);
uint8_t payload[] = {
'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!',
};
TEST_ASSERT_EQUAL(sizeof(payload), bundle->payload_block->length);
TEST_ASSERT_EQUAL_INT8_ARRAY(payload,
bundle->payload_block->data, 12);
TEST_ASSERT_EQUAL(BUNDLE_CRC_TYPE_NONE, block_element->data->crc_type);
TEST_ASSERT_EQUAL(len_simple_bundle,
bundle_get_serialized_size(bundle));
// Assert that serialized length is correct after recalculation
bundle_recalculate_header_length(bundle);
TEST_ASSERT_EQUAL(len_simple_bundle,
bundle_get_serialized_size(bundle));
bundle7_parser_reset(&state);
bundle_free(state.bundle);
free(parser);
}
TEST(bundle7Parser, bundle_parser)
{
for (size_t chunk = 1; chunk < len_simple_bundle; ++chunk) {
parse_bundle_chunked(chunk);
// Clear bundle
bundle_free(bundle);
bundle = NULL;
}
}
// -------------------------
// CRC 16 AX.25 Verification
// -------------------------
//
extern uint8_t cbor_crc16_primary_block[];
extern uint8_t cbor_crc16_payload_block[];
extern size_t len_crc16_primary_block;
extern size_t len_crc16_payload_block;
TEST(bundle7Parser, crc16_verification)
{
struct bundle7_parser state;
struct parser *parser = bundle7_parser_init(
&state,
&send_callback,
NULL
);
TEST_ASSERT_NOT_NULL(parser);
//
// CRC 16 primary block checksum
// -----------------------------
//
size_t parsed = bundle7_parser_read(&state, cbor_crc16_primary_block,
len_crc16_primary_block);
TEST_ASSERT_EQUAL(PARSER_STATUS_DONE, state.basedata->status);
TEST_ASSERT_FALSE(state.basedata->flags & PARSER_FLAG_CRC_INVALID);
TEST_ASSERT_EQUAL(parsed, len_crc16_primary_block);
TEST_ASSERT_NULL(state.bundle);
TEST_ASSERT_NOT_NULL(bundle);
TEST_ASSERT_EQUAL(bundle->crc.checksum, 0xb196);
// Clear bundle
bundle_free(bundle);
bundle = NULL;
//
// CRC 16 payload block checksum
// -----------------------------
//
bundle7_parser_reset(&state);
parsed = bundle7_parser_read(&state, cbor_crc16_payload_block,
len_crc16_payload_block);
TEST_ASSERT_EQUAL(PARSER_STATUS_DONE, state.basedata->status);
TEST_ASSERT_FALSE(state.basedata->flags & PARSER_FLAG_CRC_INVALID);
TEST_ASSERT_EQUAL(parsed, len_crc16_payload_block);
TEST_ASSERT_NULL(state.bundle);
TEST_ASSERT_NOT_NULL(bundle);
TEST_ASSERT_EQUAL(bundle->payload_block->crc.checksum, 0x60d7);
// Clear bundle
bundle_free(bundle);
bundle = NULL;
free(parser);
}
// ------------------------------
// CRC 32 Castagnoli Verification
// ------------------------------
//
extern uint8_t cbor_crc32_primary_block[];
extern uint8_t cbor_crc32_payload_block[];
extern size_t len_crc32_primary_block;
extern size_t len_crc32_payload_block;
TEST(bundle7Parser, crc32_verification)
{
struct bundle7_parser state;
struct parser *parser = bundle7_parser_init(
&state,
&send_callback,
NULL
);
TEST_ASSERT_NOT_NULL(parser);
//
// CRC 32 primary block checksum
// -----------------------------
//
size_t parsed = bundle7_parser_read(&state, cbor_crc32_primary_block,
len_crc32_primary_block);
TEST_ASSERT_EQUAL(PARSER_STATUS_DONE, state.basedata->status);
TEST_ASSERT_FALSE(state.basedata->flags & PARSER_FLAG_CRC_INVALID);
TEST_ASSERT_EQUAL(parsed, len_crc32_primary_block);
TEST_ASSERT_NULL(state.bundle);
TEST_ASSERT_NOT_NULL(bundle);
TEST_ASSERT_EQUAL(bundle->crc.checksum, 0xb05c7c1e);
// Clear bundle
bundle_free(bundle);
bundle = NULL;
//
// CRC 32 payload block checksum
// -----------------------------
//
bundle7_parser_reset(&state);
parsed = bundle7_parser_read(&state, cbor_crc32_payload_block,
len_crc32_payload_block);
TEST_ASSERT_EQUAL(PARSER_STATUS_DONE, state.basedata->status);
TEST_ASSERT_FALSE(state.basedata->flags & PARSER_FLAG_CRC_INVALID);
TEST_ASSERT_EQUAL(parsed, len_crc32_payload_block);
TEST_ASSERT_NULL(state.bundle);
TEST_ASSERT_NOT_NULL(bundle);
TEST_ASSERT_EQUAL(bundle->payload_block->crc.checksum, 0xc3aec552);
// Clear bundle
bundle_free(bundle);
bundle = NULL;
free(parser);
}
// --------------------
// Invalid CRC Handling
// --------------------
extern uint8_t cbor_invalid_crc16[];
extern size_t len_invalid_crc16;
TEST(bundle7Parser, invalid_crc_handling)
{
struct bundle7_parser state;
struct parser *parser = bundle7_parser_init(
&state,
&send_callback,
NULL
);
TEST_ASSERT_NOT_NULL(parser);
size_t parsed = bundle7_parser_read(&state, cbor_invalid_crc16,
len_invalid_crc16);
// The bundle must be parsed completely
TEST_ASSERT_EQUAL(PARSER_STATUS_DONE, state.basedata->status);
TEST_ASSERT_EQUAL(parsed, len_invalid_crc16);
// but the CRC_INVALID flag must be set
TEST_ASSERT_TRUE(state.basedata->flags & PARSER_FLAG_CRC_INVALID);
// and the bundle must not be send callback must not be called
TEST_ASSERT_NULL(state.bundle);
TEST_ASSERT_NULL(bundle);
free(parser);
}
static const uint8_t cbor_status_report[] = {
// [
// 1, // Record type code
// [
// [ // bundle status information
// [true, 10], // Reporting node received bundle
// [false], // Reporting node forwarded the bundle
// [false], // Reporting node delivered the bundle
// [false] // Reporting node deleted the bundle
// ],
// 0, // bundle status report reason code
// [1, 0], // Source node ID of reported bundle
// [2165, 12] // Creation time of the reported bundle
// ]
// ]
// ]
0x82, 0x01, 0x84, 0x84, 0x82, 0xf5, 0x0a, 0x81, 0xf4, 0x81,
0xf4, 0x81, 0xf4, 0x00, 0x82, 0x01, 0x00, 0x82, 0x19, 0x08, 0x75, 0x0c
};
TEST(bundle7Parser, status_report_parser)
{
struct bundle *bundle = bundle_init();
TEST_ASSERT_NOT_NULL(bundle);
// Primary Block
bundle->proc_flags = BUNDLE_FLAG_REPORT_STATUS_TIME
| BUNDLE_FLAG_ADMINISTRATIVE_RECORD;
bundle->creation_timestamp_ms = 4200;
bundle->sequence_number = 0;
bundle->lifetime_ms = 86400;
struct bundle_block *payload;
struct bundle_block_list *entry;
// Payload
payload = bundle_block_create(BUNDLE_BLOCK_TYPE_PAYLOAD);
entry = bundle_block_entry_create(payload);
payload->length = sizeof(cbor_status_report);
payload->data = malloc(payload->length);
memcpy(payload->data, cbor_status_report, sizeof(cbor_status_report));
bundle->blocks = entry;
bundle->payload_block = payload;
// Parser payload
struct bundle_administrative_record *record =
bundle7_parse_administrative_record(
bundle->payload_block->data,
bundle->payload_block->length
);
TEST_ASSERT_NOT_NULL(record);
TEST_ASSERT_NOT_NULL(record->status_report);
TEST_ASSERT_EQUAL(BUNDLE_AR_STATUS_REPORT, record->type);
TEST_ASSERT_EQUAL(0, record->flags);
TEST_ASSERT_EQUAL(2165, record->bundle_creation_timestamp_ms);
TEST_ASSERT_EQUAL(12, record->bundle_sequence_number);
TEST_ASSERT_EQUAL(10, record->status_report->bundle_received_time);
TEST_ASSERT_EQUAL(BUNDLE_SR_FLAG_BUNDLE_RECEIVED,
record->status_report->status);
TEST_ASSERT_EQUAL(BUNDLE_SR_REASON_NO_INFO,
record->status_report->reason);
free_administrative_record(record);
bundle_free(bundle);
}
// [30, 4]
static const uint8_t cbor_hop_count[] = { 0x82, 0x18, 0x1e, 0x04 };
TEST(bundle7Parser, hop_count)
{
struct bundle_hop_count hop_count;
// Try to parse empty buffer
TEST_ASSERT_FALSE(bundle7_hop_count_parse(&hop_count, NULL, 0));
// Try to parse wrong CBOR structure
TEST_ASSERT_FALSE(bundle7_hop_count_parse(&hop_count,
cbor_status_report, sizeof(cbor_status_report)));
// Parse valid CBOR structure
TEST_ASSERT_TRUE(bundle7_hop_count_parse(&hop_count,
cbor_hop_count, sizeof(cbor_hop_count)));
TEST_ASSERT_EQUAL(30, hop_count.limit);
TEST_ASSERT_EQUAL(4, hop_count.count);
}
// 42000
static const uint8_t cbor_bundle_age[] = { 0x19, 0xa4, 0x10 };
TEST(bundle7Parser, bundle_age)
{
uint64_t bundle_age;
// Try to parse empty buffer
TEST_ASSERT_FALSE(bundle_age_parse(&bundle_age, NULL, 0));
// Try to parse wrong CBOR structure
TEST_ASSERT_FALSE(bundle_age_parse(&bundle_age,
cbor_status_report, sizeof(cbor_status_report)));
// Parse valid CBOR structure
TEST_ASSERT_TRUE(bundle_age_parse(&bundle_age,
cbor_bundle_age, sizeof(cbor_bundle_age)));
TEST_ASSERT_EQUAL(42000, bundle_age);
}
TEST_GROUP_RUNNER(bundle7Parser)
{
RUN_TEST_CASE(bundle7Parser, eid_parser);
RUN_TEST_CASE(bundle7Parser, bundle_parser);
RUN_TEST_CASE(bundle7Parser, crc16_verification);
RUN_TEST_CASE(bundle7Parser, crc32_verification);
RUN_TEST_CASE(bundle7Parser, invalid_crc_handling);
RUN_TEST_CASE(bundle7Parser, status_report_parser);
RUN_TEST_CASE(bundle7Parser, hop_count);
RUN_TEST_CASE(bundle7Parser, bundle_age);
}