mirror of
https://gitlab.com/d3tn/ud3tn.git
synced 2026-08-13 12:33:27 +02:00
Removes unused includes detected by the clangd LSP. Signed-off-by: Maximilian Nitsch <maximilian.nitsch@d3tn.com>
429 lines
13 KiB
C
429 lines
13 KiB
C
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
|
#include "bundle7/bundle7.h"
|
|
|
|
#include "ud3tn/common.h"
|
|
#include "ud3tn/bundle.h"
|
|
#include "ud3tn/eid.h"
|
|
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <limits.h>
|
|
|
|
#define MAX_VALIDATED_BLOCK_NUMBER UINT8_MAX
|
|
|
|
|
|
const uint32_t BUNDLE_V7_FLAG_MASK = BUNDLE_FLAG_IS_FRAGMENT
|
|
| BUNDLE_FLAG_ADMINISTRATIVE_RECORD
|
|
| BUNDLE_FLAG_MUST_NOT_BE_FRAGMENTED
|
|
| BUNDLE_FLAG_ACKNOWLEDGEMENT_REQUESTED
|
|
| BUNDLE_FLAG_REPORT_STATUS_TIME
|
|
| BUNDLE_FLAG_REPORT_RECEPTION
|
|
| BUNDLE_FLAG_REPORT_FORWARDING
|
|
| BUNDLE_FLAG_REPORT_DELIVERY
|
|
| BUNDLE_FLAG_REPORT_DELETION;
|
|
|
|
|
|
bool bundle7_is_valid(const struct bundle *bundle)
|
|
{
|
|
if (bundle->protocol_version != 7)
|
|
return false;
|
|
|
|
if (eid_is_error(bundle->source) || eid_is_error(bundle->destination) ||
|
|
eid_is_error(bundle->report_to) || eid_is_null(bundle->destination))
|
|
return false;
|
|
|
|
const bool is_admin_record = !!(bundle->proc_flags & BUNDLE_FLAG_ADMINISTRATIVE_RECORD);
|
|
const bool has_null_source = eid_is_null(bundle->source);
|
|
const bool has_primary_crc = bundle->crc_type != BUNDLE_CRC_TYPE_NONE;
|
|
|
|
/**
|
|
* From RFC 9171, 4.2.3:
|
|
* > If the bundle processing control flags indicate that the bundle's ADU is an
|
|
* > administrative record, then all status report request flag values MUST be zero.
|
|
*/
|
|
if (is_admin_record) {
|
|
if (bundle->proc_flags & BUNDLE_FLAG_REPORT_ANY)
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* From RFC 9171, 4.2.3:
|
|
* > If the bundle's source node is omitted (i.e., the source node ID is the ID of the null
|
|
* > endpoint, which has no members as discussed below; this option enables anonymous
|
|
* > bundle transmission), then the bundle is not uniquely identifiable and all Bundle
|
|
* > Protocol features that rely on bundle identity must therefore be disabled: the
|
|
* > "Bundle must not be fragmented" flag value MUST be 1, and all status report request
|
|
* > flag values MUST be zero.
|
|
*/
|
|
if (has_null_source) {
|
|
if (!(bundle->proc_flags & BUNDLE_FLAG_MUST_NOT_BE_FRAGMENTED))
|
|
return false;
|
|
if (bundle->proc_flags & BUNDLE_FLAG_REPORT_ANY)
|
|
return false;
|
|
}
|
|
|
|
struct bundle_block_list *cur = bundle->blocks;
|
|
bool has_payload_block = false;
|
|
bool has_previous_node_block = false;
|
|
bool has_bundle_age_block = false;
|
|
bool has_hop_count_block = false;
|
|
bool has_block_integrity_block = false;
|
|
bool any_block_requests_status_reporting = false;
|
|
bool taken_block_numbers[MAX_VALIDATED_BLOCK_NUMBER] = {false};
|
|
|
|
while (cur) {
|
|
if (cur->data->type == BUNDLE_BLOCK_TYPE_PAYLOAD) {
|
|
// From RFC 9171, 4.1: The last such block MUST be a payload block
|
|
if (cur->next)
|
|
return false;
|
|
// From RFC 9171, 4.1: the bundle MUST have exactly one payload block.
|
|
if (has_payload_block)
|
|
return false;
|
|
has_payload_block = true;
|
|
// From RFC 9171, 4.1: The block number of the payload block is always 1.
|
|
if (cur->data->number != 1)
|
|
return false;
|
|
} else if (cur->data->type == BUNDLE_BLOCK_TYPE_PREVIOUS_NODE) {
|
|
// From RFC 9171, 4.4.1: the bundle SHOULD contain one (1) occurrence of
|
|
// this type of block and MUST NOT contain more than one.
|
|
if (has_previous_node_block)
|
|
return false;
|
|
has_previous_node_block = true;
|
|
} else if (cur->data->type == BUNDLE_BLOCK_TYPE_BUNDLE_AGE) {
|
|
// From RFC 9171, 4.4.2: A bundle MUST NOT contain multiple occurrences
|
|
// of the Bundle Age Block
|
|
if (has_bundle_age_block)
|
|
return false;
|
|
has_bundle_age_block = true;
|
|
} else if (cur->data->type == BUNDLE_BLOCK_TYPE_HOP_COUNT) {
|
|
// From RFC 9171, 4.4.3: A bundle MAY contain one occurrence of this type
|
|
// of block but MUST NOT contain more than one.
|
|
if (has_hop_count_block)
|
|
return false;
|
|
has_hop_count_block = true;
|
|
} else if (cur->data->type == BUNDLE_BLOCK_TYPE_BLOCK_INTEGRITY) {
|
|
// TODO: Decode the security targets array to check whether the block
|
|
// actually targets the primary block. As the order in that CBOR array is
|
|
// arbitrary (RFC 9172, 3.6), this is non-trivial.
|
|
has_block_integrity_block = true;
|
|
}
|
|
// Status reporting is not possible in some cases (e.g. Null source)
|
|
if (HAS_FLAG(cur->data->flags, BUNDLE_BLOCK_FLAG_REPORT_IF_UNPROC))
|
|
any_block_requests_status_reporting = true;
|
|
// Reserved for primary block that is not part of the block list
|
|
if (cur->data->number == 0)
|
|
return false;
|
|
if (cur->data->number < MAX_VALIDATED_BLOCK_NUMBER) {
|
|
// Duplicate?
|
|
// From RFC 9171, 4.1: The block number uniquely identifies the block
|
|
// within the bundle
|
|
if (taken_block_numbers[cur->data->number - 1])
|
|
return false;
|
|
taken_block_numbers[cur->data->number - 1] = true;
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
|
|
// From RFC 9171, 4.1: the bundle MUST have exactly one payload block.
|
|
if (!has_payload_block)
|
|
return false;
|
|
// From RFC 9171, 4.4.2: If the bundle's creation time is zero, then the bundle MUST
|
|
// contain exactly one (1) occurrence of this type of block
|
|
if (bundle->creation_timestamp_ms == 0 && !has_bundle_age_block)
|
|
return false;
|
|
// From RFC 9171, 4.3.1: A CRC SHALL be present in the primary block unless the bundle
|
|
// includes a BPSec Block Integrity Block [BPSEC] whose target is the primary block
|
|
if (!has_primary_crc && !has_block_integrity_block)
|
|
// Will be optimized away if BUNDLE7_ALLOW_NO_PRIMARY_CRC_NO_BIB is set
|
|
if (!BUNDLE7_ALLOW_NO_PRIMARY_CRC_NO_BIB)
|
|
return false;
|
|
|
|
/**
|
|
* From RFC 9171, 4.2.4:
|
|
* > For each bundle whose bundle processing control flags indicate that the bundle's
|
|
* > ADU is an administrative record, or whose source node ID is the null endpoint ID
|
|
* > as defined below, the value of the "Transmit status report if block can't be
|
|
* > processed" flag in every canonical block of the bundle MUST be zero.
|
|
*/
|
|
if (any_block_requests_status_reporting && (has_null_source || is_admin_record))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
size_t bundle7_cbor_uint_sizeof(uint64_t num)
|
|
{
|
|
// Binary search
|
|
if (num <= UINT16_MAX) {
|
|
if (num <= UINT8_MAX)
|
|
// Embedded unsigned integer
|
|
if (num <= 23)
|
|
return 1;
|
|
// uint8
|
|
else
|
|
return 2;
|
|
// uint16
|
|
else
|
|
return 3;
|
|
// uint32
|
|
} else if (num <= UINT32_MAX) {
|
|
return 5;
|
|
// uint64
|
|
} else {
|
|
return 9;
|
|
}
|
|
}
|
|
|
|
|
|
size_t bundle7_eid_sizeof(const struct eid eid)
|
|
{
|
|
if (eid_is_error(eid))
|
|
return 0;
|
|
|
|
if (eid.scheme == EID_SCHEME_DTN) {
|
|
// dtn:none -> [1,0] -> 0x82 0x01 0x00
|
|
if (eid.dtn_eid == NULL)
|
|
return 3;
|
|
|
|
// dtn:
|
|
// The CBOR encoded SSP requires an header encoding the string
|
|
// length. Therefore we add the size of this header to the bound.
|
|
// The SSP is "string length - 4" bytes long.
|
|
const size_t dtn_length = strlen(eid.dtn_eid);
|
|
|
|
if (dtn_length < 4) // invalid; probably a bug
|
|
return 0;
|
|
|
|
return (
|
|
1 + // CBOR array header
|
|
1 + // bundle7_cbor_uint_sizeof(BUNDLE_V7_EID_SCHEMA_DTN)
|
|
bundle7_cbor_uint_sizeof(dtn_length - 4) +
|
|
(dtn_length - 4)
|
|
);
|
|
} else if (eid.scheme != EID_SCHEME_IPN) {
|
|
return 0;
|
|
}
|
|
|
|
// ipn
|
|
if (ipn_eid_allocator(eid.ipn_eid) == 0 || EID_IPN_FORCE_2_ELEMENT)
|
|
return (
|
|
1 + // CBOR array header
|
|
1 + // bundle7_cbor_uint_sizeof(BUNDLE_V7_EID_SCHEMA_IPN)
|
|
1 + // CBOR array header
|
|
bundle7_cbor_uint_sizeof(eid.ipn_eid.fully_qualified_node_number) +
|
|
bundle7_cbor_uint_sizeof(eid.ipn_eid.service_number)
|
|
);
|
|
else
|
|
return (
|
|
1 + // CBOR array header
|
|
1 + // bundle7_cbor_uint_sizeof(BUNDLE_V7_EID_SCHEMA_IPN)
|
|
1 + // CBOR array header
|
|
bundle7_cbor_uint_sizeof(ipn_eid_allocator(eid.ipn_eid)) +
|
|
bundle7_cbor_uint_sizeof(ipn_eid_node(eid.ipn_eid)) +
|
|
bundle7_cbor_uint_sizeof(eid.ipn_eid.service_number)
|
|
);
|
|
}
|
|
|
|
|
|
void bundle7_recalculate_primary_block_length(struct bundle *bundle)
|
|
{
|
|
// Primary Block
|
|
const size_t dst_eid_size = bundle7_eid_sizeof(bundle->destination);
|
|
const size_t src_eid_size = bundle7_eid_sizeof(bundle->source);
|
|
const size_t rpt_eid_size = bundle7_eid_sizeof(bundle->report_to);
|
|
|
|
// Bug: Bundles with invalid EID fields must not be processed!
|
|
ASSERT(dst_eid_size != 0);
|
|
ASSERT(src_eid_size != 0);
|
|
ASSERT(rpt_eid_size != 0);
|
|
|
|
size_t size = 1 // CBOR array header
|
|
+ bundle7_cbor_uint_sizeof(bundle->protocol_version)
|
|
+ bundle7_cbor_uint_sizeof(bundle->proc_flags)
|
|
+ bundle7_cbor_uint_sizeof(bundle->crc_type)
|
|
+ dst_eid_size
|
|
+ src_eid_size
|
|
+ rpt_eid_size
|
|
// Creation Timestamp
|
|
+ 1 // CBOR array header
|
|
+ bundle7_cbor_uint_sizeof(bundle->creation_timestamp_ms)
|
|
+ bundle7_cbor_uint_sizeof(bundle->sequence_number)
|
|
+ bundle7_cbor_uint_sizeof(bundle->lifetime_ms);
|
|
|
|
// Fragmented Bundle
|
|
if (bundle_is_fragmented(bundle)) {
|
|
size += bundle7_cbor_uint_sizeof(bundle->fragment_offset);
|
|
size += bundle7_cbor_uint_sizeof(bundle->total_adu_length);
|
|
}
|
|
|
|
if (bundle->crc_type == BUNDLE_CRC_TYPE_32)
|
|
size += 5;
|
|
else if (bundle->crc_type == BUNDLE_CRC_TYPE_16)
|
|
size += 3;
|
|
|
|
bundle->primary_block_length = size;
|
|
}
|
|
|
|
|
|
size_t bundle7_block_get_serialized_size(const struct bundle_block *block)
|
|
{
|
|
size_t size = 1 // CBOR array header
|
|
+ bundle7_cbor_uint_sizeof(block->type)
|
|
+ bundle7_cbor_uint_sizeof(block->number)
|
|
+ bundle7_cbor_uint_sizeof(block->flags)
|
|
+ bundle7_cbor_uint_sizeof(block->crc_type)
|
|
+ bundle7_cbor_uint_sizeof(block->length)
|
|
// Block-specific data
|
|
+ block->length;
|
|
|
|
// CRC field
|
|
if (block->crc_type == BUNDLE_CRC_TYPE_32)
|
|
size += 5;
|
|
else if (block->crc_type == BUNDLE_CRC_TYPE_16)
|
|
size += 3;
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
size_t bundle7_get_serialized_size(struct bundle *bundle)
|
|
{
|
|
size_t size = 0;
|
|
struct bundle_block_list *entry = bundle->blocks;
|
|
|
|
// Extension Blocks
|
|
while (entry != NULL) {
|
|
size += bundle7_block_get_serialized_size(entry->data);
|
|
entry = entry->next;
|
|
}
|
|
|
|
return 1 // CBOR indef-array start
|
|
+ bundle->primary_block_length
|
|
+ size
|
|
+ 1; // CBOR "stop"
|
|
}
|
|
|
|
|
|
size_t bundle7_get_serialized_size_without_payload(struct bundle *bundle)
|
|
{
|
|
size_t size = 0;
|
|
struct bundle_block_list *entry = bundle->blocks;
|
|
|
|
// Extension Blocks
|
|
while (entry->data->type != BUNDLE_BLOCK_TYPE_PAYLOAD) {
|
|
size += bundle7_block_get_serialized_size(entry->data);
|
|
entry = entry->next;
|
|
}
|
|
|
|
return 1 // CBOR indef-array start
|
|
+ bundle->primary_block_length
|
|
+ size
|
|
+ 1; // CBOR "stop"
|
|
}
|
|
|
|
|
|
size_t bundle7_get_first_fragment_min_size(struct bundle *bundle)
|
|
{
|
|
size_t size = 0;
|
|
struct bundle_block_list *entry = bundle->blocks;
|
|
|
|
while (entry != NULL) {
|
|
// Payload header
|
|
if (entry->data->type == BUNDLE_BLOCK_TYPE_PAYLOAD) {
|
|
size += 1 // CBOR array header
|
|
+ bundle7_cbor_uint_sizeof(entry->data->type)
|
|
+ bundle7_cbor_uint_sizeof(entry->data->number)
|
|
+ bundle7_cbor_uint_sizeof(entry->data->flags)
|
|
+ bundle7_cbor_uint_sizeof(entry->data->crc_type)
|
|
+ bundle7_cbor_uint_sizeof(entry->data->length);
|
|
|
|
// CRC field
|
|
if (entry->data->crc_type == BUNDLE_CRC_TYPE_32)
|
|
size += 5;
|
|
else if (entry->data->crc_type == BUNDLE_CRC_TYPE_16)
|
|
size += 3;
|
|
|
|
// Data length zero + empty byte string
|
|
size += 2;
|
|
}
|
|
// For the first fragment (fragment with the offset 0), all
|
|
// extension blocks must be replicated
|
|
else
|
|
size += bundle7_block_get_serialized_size(entry->data);
|
|
|
|
entry = entry->next;
|
|
}
|
|
|
|
// If bundle was not fragmented before, add the "Fragment offset" and
|
|
// "Total Application Data Unit Length" field sizes
|
|
if (!bundle_is_fragmented(bundle)) {
|
|
size += bundle7_cbor_uint_sizeof(bundle->payload_block->length);
|
|
// Conservative estimation for maximum "minimum bundle size",
|
|
// i.e., header length: the fragment offset could have a maximum
|
|
// size corresponding to the fragment offset. All other values
|
|
// in the bundle can only decrease.
|
|
size += bundle7_cbor_uint_sizeof(bundle->payload_block->length);
|
|
} else {
|
|
// Conservative estimation rgd. fragment offset
|
|
size += (
|
|
bundle7_cbor_uint_sizeof(bundle->total_adu_length) -
|
|
bundle7_cbor_uint_sizeof(bundle->fragment_offset)
|
|
);
|
|
}
|
|
|
|
return bundle->primary_block_length + size;
|
|
}
|
|
|
|
|
|
size_t bundle7_get_last_fragment_min_size(struct bundle *bundle)
|
|
{
|
|
size_t size = 0;
|
|
struct bundle_block_list *entry = bundle->blocks;
|
|
|
|
while (entry != NULL) {
|
|
if (entry->data->type == BUNDLE_BLOCK_TYPE_PAYLOAD) {
|
|
size += 1 // CBOR array header
|
|
+ bundle7_cbor_uint_sizeof(entry->data->type)
|
|
+ bundle7_cbor_uint_sizeof(entry->data->number)
|
|
+ bundle7_cbor_uint_sizeof(entry->data->flags)
|
|
+ bundle7_cbor_uint_sizeof(entry->data->crc_type)
|
|
+ bundle7_cbor_uint_sizeof(entry->data->length);
|
|
|
|
// CRC field
|
|
if (entry->data->crc_type == BUNDLE_CRC_TYPE_32)
|
|
size += 5;
|
|
else if (entry->data->crc_type == BUNDLE_CRC_TYPE_16)
|
|
size += 3;
|
|
|
|
// Data length zero + empty byte string
|
|
size += 2;
|
|
}
|
|
// Sum up all blocks that must be replicated in each fragment
|
|
else if (HAS_FLAG(entry->data->flags,
|
|
BUNDLE_BLOCK_FLAG_MUST_BE_REPLICATED))
|
|
size += bundle7_block_get_serialized_size(entry->data);
|
|
|
|
entry = entry->next;
|
|
}
|
|
|
|
// If bundle was not fragmented before, add the "Fragment offset" and
|
|
// "Total Application Data Unit Length" field sizes
|
|
if (!bundle_is_fragmented(bundle)) {
|
|
size += bundle7_cbor_uint_sizeof(bundle->payload_block->length);
|
|
// Conservative estimation for maximum "minimum bundle size",
|
|
// i.e., header length: the fragment offset could have a maximum
|
|
// size corresponding to the fragment offset. All other values
|
|
// in the bundle can only decrease.
|
|
size += bundle7_cbor_uint_sizeof(bundle->payload_block->length);
|
|
} else {
|
|
// Conservative estimation rgd. fragment offset
|
|
size += (
|
|
bundle7_cbor_uint_sizeof(bundle->total_adu_length) -
|
|
bundle7_cbor_uint_sizeof(bundle->fragment_offset)
|
|
);
|
|
}
|
|
|
|
return bundle->primary_block_length + size;
|
|
}
|