ud3tn/components/bundle7/serializer.c
Maximilian Nitsch 611b67fb0e refactor: cleanup includes
Removes unused includes detected by the clangd LSP.

Signed-off-by: Maximilian Nitsch <maximilian.nitsch@d3tn.com>
2026-05-20 16:16:35 +02:00

275 lines
6.9 KiB
C

// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
#include "bundle7/eid.h"
#include "bundle7/serializer.h"
#include "cbor.h"
#include "compilersupport_p.h" // Private TinyCBOR header, used for endianness
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* CBOR output buffer size. This buffer will be allocated during the
* serialization process and released afterwards
*/
#define BUFFER_SIZE 128
static inline size_t primary_block_get_item_count(const struct bundle *bundle)
{
size_t length = 8;
if (bundle->crc_type != BUNDLE_CRC_TYPE_NONE)
length++;
if (bundle_is_fragmented(bundle))
length += 2;
return length;
}
static inline size_t block_get_item_count(const struct bundle_block *block)
{
size_t length = 5;
if (block->crc_type != BUNDLE_CRC_TYPE_NONE)
length++;
return length;
}
static CborError write_crc(
struct CborEncoder *encoder,
enum bundle_crc_type crc_type, struct crc_stream *crc)
{
// CRC-32
if (crc_type == BUNDLE_CRC_TYPE_32) {
// Feed the "zero" CRC checksum
crc->feed(crc, 0x44);
crc->feed(crc, 0x00);
crc->feed(crc, 0x00);
crc->feed(crc, 0x00);
crc->feed(crc, 0x00);
crc->feed_eof(crc);
// Swap to network byte order
// cppcheck-suppress selfAssignment
crc->checksum = cbor_htonl(crc->checksum);
return cbor_encode_byte_string(encoder, crc->bytes, 4);
// CRC-16
} else {
// Feed the "zero" CRC checksum
crc->feed(crc, 0x42);
crc->feed(crc, 0x00);
crc->feed(crc, 0x00);
crc->feed_eof(crc);
// Swap to network byte order
// cppcheck-suppress selfAssignment
crc->checksum = cbor_htons(crc->checksum);
// We skip the first two (empty) bytes of the checksum here
return cbor_encode_byte_string(encoder, crc->bytes, 2);
}
}
/*
* Number of bytes required for CBOR-encoded max. value:
*
* Primary Block
* -------------
*
*
* Extension Block (without payload):
* ----------------------------------
*
* Array header: 1 Byte
* Block type code (255): 2 Byte
* Block number (255): 2 Byte
* Block processing control flags: 1 Byte
* CRC (32 bit): 5 Byte
* Block data length: 5 Byte
*/
static inline void init_crc(struct crc_stream *crc,
enum bundle_crc_type type)
{
if (type == BUNDLE_CRC_TYPE_32)
crc_init(crc, CRC32);
else if (type == BUNDLE_CRC_TYPE_16)
crc_init(crc, CRC16_X25);
}
static inline void feed_crc(struct crc_stream *crc,
enum bundle_crc_type type, const uint8_t *buffer, size_t length)
{
if (type != BUNDLE_CRC_TYPE_NONE)
crc_feed_bytes(crc, buffer, length);
}
enum ud3tn_result bundle7_serialize(
struct bundle *bundle,
enum ud3tn_result (*write)(void *cla_obj, const void *, const size_t),
void *cla_obj)
{
// Assert that the bundle has correct version
if (bundle->protocol_version != 7)
return UD3TN_FAIL;
uint8_t *buffer;
CborEncoder encoder;
struct crc_stream crc;
int written;
buffer = malloc(BUFFER_SIZE);
if (buffer == NULL)
return UD3TN_FAIL;
// Bundle start (CBOR indefinite array)
buffer[0] = 0x9f;
// -------------
// Primary Block
// -------------
buffer[1] = 0x80 + primary_block_get_item_count(bundle);
init_crc(&crc, bundle->crc_type);
cbor_encoder_init(&encoder, buffer + 2, BUFFER_SIZE - 2, 0);
cbor_encode_uint(&encoder, bundle->protocol_version);
cbor_encode_uint(&encoder, bundle->proc_flags);
cbor_encode_uint(&encoder, bundle->crc_type);
if (write(cla_obj, buffer, cbor_encoder_get_buffer_size(&encoder, buffer)) != UD3TN_OK)
goto fail;
feed_crc(&crc, bundle->crc_type, buffer + 1,
cbor_encoder_get_buffer_size(&encoder, buffer + 1));
// Destination EID
written = bundle7_eid_serialize(bundle->destination,
buffer, BUFFER_SIZE);
if (written <= 0)
goto fail;
if (write(cla_obj, buffer, written) != UD3TN_OK)
goto fail;
feed_crc(&crc, bundle->crc_type, buffer, written);
// Source EID
written = bundle7_eid_serialize(bundle->source,
buffer, BUFFER_SIZE);
if (written <= 0)
goto fail;
if (write(cla_obj, buffer, written) != UD3TN_OK)
goto fail;
feed_crc(&crc, bundle->crc_type, buffer, written);
// Report-To EID
written = bundle7_eid_serialize(bundle->report_to,
buffer, BUFFER_SIZE);
if (written <= 0)
goto fail;
if (write(cla_obj, buffer, written) != UD3TN_OK)
goto fail;
feed_crc(&crc, bundle->crc_type, buffer, written);
// Creation Timestamp
buffer[0] = 0x82;
cbor_encoder_init(&encoder, buffer + 1, BUFFER_SIZE - 1, 0);
cbor_encode_uint(&encoder, bundle->creation_timestamp_ms);
cbor_encode_uint(&encoder, bundle->sequence_number);
cbor_encode_uint(&encoder, bundle->lifetime_ms);
if (bundle_is_fragmented(bundle)) {
cbor_encode_uint(&encoder, bundle->fragment_offset);
cbor_encode_uint(&encoder, bundle->total_adu_length);
}
// CRC checksum for primary block
if (bundle->crc_type != BUNDLE_CRC_TYPE_NONE) {
feed_crc(&crc, bundle->crc_type, buffer,
cbor_encoder_get_buffer_size(&encoder, buffer));
// Calculate and encode CRC checksum for primary block.
write_crc(&encoder, bundle->crc_type, &crc);
}
if (write(cla_obj, buffer, cbor_encoder_get_buffer_size(&encoder, buffer)) != UD3TN_OK)
goto fail;
// ----------------
// Extension Blocks
// ----------------
struct bundle_block_list *cur_block = bundle->blocks;
while (cur_block != NULL) {
const struct bundle_block *block = cur_block->data;
init_crc(&crc, block->crc_type);
// CBOR array header with embedded number of items
buffer[0] = 0x80 + block_get_item_count(block);
cbor_encoder_init(&encoder, buffer + 1, BUFFER_SIZE - 1, 0);
cbor_encode_uint(&encoder, block->type);
cbor_encode_uint(&encoder, block->number);
cbor_encode_uint(&encoder, block->flags);
cbor_encode_uint(&encoder, block->crc_type);
const size_t bytes_before_length = cbor_encoder_get_buffer_size(
&encoder, buffer
);
// As the byte string length is represented in the same manner
// as a uint in CBOR, we can write it like that and afterwards
// change the type code to byte string.
cbor_encode_uint(&encoder, block->length);
buffer[bytes_before_length] |= 0x40; // uint -> bytestring
if (write(cla_obj, buffer,
cbor_encoder_get_buffer_size(&encoder,
buffer)) != UD3TN_OK)
goto fail;
feed_crc(&crc, block->crc_type, buffer,
cbor_encoder_get_buffer_size(&encoder, buffer));
if (write(cla_obj, block->data, block->length) != UD3TN_OK)
goto fail;
feed_crc(&crc, block->crc_type,
block->data, block->length);
if (block->crc_type != BUNDLE_CRC_TYPE_NONE) {
// Reset CBOR encoder
cbor_encoder_init(&encoder, buffer, BUFFER_SIZE, 0);
// Calculate and CRC checksum for extension block
write_crc(&encoder, block->crc_type, &crc);
if (write(cla_obj, buffer,
cbor_encoder_get_buffer_size(&encoder, buffer)) != UD3TN_OK)
goto fail;
}
cur_block = cur_block->next;
}
// CBOR "break"
buffer[0] = 0xff;
if (write(cla_obj, buffer, 1) != UD3TN_OK)
goto fail;
free(buffer);
return UD3TN_OK;
fail:
free(buffer);
return UD3TN_FAIL;
}