mirror of
https://gitlab.com/d3tn/ud3tn.git
synced 2026-08-15 12:50:49 +02:00
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>
479 lines
14 KiB
C
479 lines
14 KiB
C
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
|
#include "bundle6/create.h"
|
|
#include "bundle6/reports.h"
|
|
#include "bundle6/sdnv.h"
|
|
|
|
#include "ud3tn/common.h"
|
|
#include "ud3tn/parser.h"
|
|
#include "ud3tn/report_manager.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define fallthrough_ok __attribute__ ((fallthrough))
|
|
|
|
// -------------------------------
|
|
// Administrative Record Generator
|
|
// -------------------------------
|
|
|
|
static struct bundle *encapsulate_record(
|
|
const struct bundle * const bundle,
|
|
const struct eid source_eid, const struct eid dest_eid,
|
|
uint8_t *payload, const int payload_len, const uint64_t timestamp_ms)
|
|
{
|
|
// Lifetime
|
|
const uint64_t exp_time_ms = bundle_get_expiration_time_ms(bundle);
|
|
|
|
if (exp_time_ms <= timestamp_ms) {
|
|
// NOTE: payload is freed by the create function
|
|
return NULL;
|
|
}
|
|
|
|
struct bundle *result = bundle6_create_local(
|
|
payload, payload_len,
|
|
source_eid, dest_eid, eid_get_null(),
|
|
timestamp_ms, 1,
|
|
exp_time_ms - timestamp_ms, BUNDLE_FLAG_ADMINISTRATIVE_RECORD);
|
|
|
|
if (result == NULL) {
|
|
// NOTE: payload is freed by the create function
|
|
return NULL;
|
|
}
|
|
|
|
result->ret_constraints = BUNDLE_RET_CONSTRAINT_FLAG_OWN;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
#define LENGTH_MAX_SIZE 4
|
|
#define DTN_TIME_MAX_SIZE 9
|
|
#define SEQ_NUM_MAX_SIZE 4
|
|
#define EID_LENGTH_MAX_SIZE 2
|
|
#define EID_DEFAULT_LENGTH 40
|
|
|
|
#define ADMINISTRATIVE_HEADER_SIZE 1
|
|
#define ADMINISTRATVE_RECORD_MAX_SIZE \
|
|
(ADMINISTRATIVE_HEADER_SIZE \
|
|
+ 2 + (LENGTH_MAX_SIZE * 2) + (DTN_TIME_MAX_SIZE * 2) \
|
|
+ SEQ_NUM_MAX_SIZE + EID_LENGTH_MAX_SIZE + EID_DEFAULT_LENGTH)
|
|
|
|
|
|
static struct bundle *generate_record(
|
|
const struct bundle * const bundle, const struct eid source_eid,
|
|
const struct eid dest_eid,
|
|
const uint8_t status_flags, const uint8_t reason_code,
|
|
const uint64_t timestamp_ms)
|
|
{
|
|
if (eid_is_error(source_eid) || eid_is_error(dest_eid))
|
|
return NULL;
|
|
|
|
uint8_t *buffer = (uint8_t *)malloc(ADMINISTRATVE_RECORD_MAX_SIZE);
|
|
uint8_t *cur = buffer;
|
|
bool fragment;
|
|
uint16_t eid_length, cur_length;
|
|
struct bundle *ret;
|
|
|
|
if (buffer == NULL)
|
|
return NULL;
|
|
|
|
char *const bundle_source_eid = eid_to_string2(bundle->source, true);
|
|
|
|
if (bundle_source_eid == NULL) {
|
|
free(buffer);
|
|
return NULL;
|
|
}
|
|
|
|
/* Write record type, flags, prefixes (status flags, reason, ...) */
|
|
fragment = bundle_is_fragmented(bundle);
|
|
(*cur++) = 0x10 | (fragment ? 0x01 : 0x00); // 0b0001 (status report) | 0b000x (fragment?)
|
|
(*cur++) = status_flags;
|
|
(*cur++) = reason_code;
|
|
/* Write fragment info if present */
|
|
if (fragment) {
|
|
cur += sdnv_write_u64(cur, bundle->fragment_offset);
|
|
cur += sdnv_write_u64(cur, bundle->payload_block->length);
|
|
}
|
|
/* Add a "DTN time": 1) TS, 2) Nanoseconds since start of cur. second */
|
|
/* NOTE this is the only timestamp as we only send a SR with a single status flag */
|
|
cur += sdnv_write_u64(cur, (timestamp_ms / 1000));
|
|
cur += sdnv_write_u32(cur, (timestamp_ms % 1000) * 1000); /* "ns" */
|
|
/* Copy bundle data */
|
|
cur += sdnv_write_u64(cur, bundle->creation_timestamp_ms / 1000);
|
|
cur += sdnv_write_u64(cur, bundle->sequence_number);
|
|
/* Bundle source EID (length + data) */
|
|
eid_length = strlen(bundle_source_eid);
|
|
cur += sdnv_write_u32(cur, eid_length);
|
|
cur_length = cur - buffer;
|
|
buffer = realloc(buffer, cur_length + eid_length);
|
|
cur = buffer + cur_length;
|
|
memcpy(cur, bundle_source_eid, eid_length);
|
|
cur += eid_length;
|
|
/* Build the bundle around our generated payload */
|
|
ret = encapsulate_record(
|
|
bundle,
|
|
source_eid,
|
|
dest_eid,
|
|
buffer,
|
|
cur - buffer,
|
|
timestamp_ms
|
|
);
|
|
if (ret == NULL)
|
|
free(buffer);
|
|
free(bundle_source_eid);
|
|
return ret;
|
|
}
|
|
|
|
|
|
struct bundle *bundle6_generate_status_report(
|
|
const struct bundle *const bundle,
|
|
const enum bundle_status_report_status_flags status,
|
|
const enum bundle_status_report_reason reason,
|
|
const struct eid source,
|
|
const uint64_t status_timestamp_ms)
|
|
{
|
|
// Enforce that exactly one bit is set
|
|
if (status != BUNDLE_SR_FLAG_BUNDLE_RECEIVED &&
|
|
status != BUNDLE_SR_FLAG_CUSTODY_TRANSFER &&
|
|
status != BUNDLE_SR_FLAG_BUNDLE_FORWARDED &&
|
|
status != BUNDLE_SR_FLAG_BUNDLE_DELIVERED &&
|
|
status != BUNDLE_SR_FLAG_BUNDLE_DELETED)
|
|
return NULL;
|
|
return generate_record(
|
|
bundle,
|
|
source,
|
|
bundle->report_to,
|
|
(uint8_t)status,
|
|
(uint8_t)reason,
|
|
status_timestamp_ms
|
|
);
|
|
}
|
|
|
|
|
|
// ---------------------------------------
|
|
// Administrative Record Parser (RFC 5050)
|
|
// ---------------------------------------
|
|
|
|
struct record_parser {
|
|
enum parser_status status;
|
|
enum record_parser_stage {
|
|
RP_EXPECT_TYPE,
|
|
RP_EXPECT_REPORT_FLAGS,
|
|
RP_EXPECT_REPORT_REASON,
|
|
RP_EXPECT_FRAGMENT_OFFSET,
|
|
RP_EXPECT_FRAGMENT_LENGTH,
|
|
RP_EXPECT_TIME_RECEPTION_SECONDS,
|
|
RP_EXPECT_TIME_RECEPTION_NANOSECONDS,
|
|
RP_EXPECT_TIME_CUSTODY_SECONDS,
|
|
RP_EXPECT_TIME_CUSTODY_NANOSECONDS,
|
|
RP_EXPECT_TIME_FORWARD_SECONDS,
|
|
RP_EXPECT_TIME_FORWARD_NANOSECONDS,
|
|
RP_EXPECT_TIME_DELIVERY_SECONDS,
|
|
RP_EXPECT_TIME_DELIVERY_NANOSECONDS,
|
|
RP_EXPECT_TIME_DELETION_SECONDS,
|
|
RP_EXPECT_TIME_DELETION_NANOSECONDS,
|
|
RP_EXPECT_BUNDLE_CREATION_TIMESTAMP,
|
|
RP_EXPECT_BUNDLE_CREATION_SEQUENCE,
|
|
RP_EXPECT_BUNDLE_SOURCE_LENGTH,
|
|
RP_EXPECT_BUNDLE_SOURCE_EID
|
|
} stage;
|
|
struct sdnv_state sdnv_state;
|
|
struct bundle_administrative_record *record;
|
|
uint64_t event_timestamp_s;
|
|
uint32_t event_nanoseconds;
|
|
uint64_t *event_target_ms;
|
|
uint16_t record_bundle_source_str_length;
|
|
char *record_bundle_source_str;
|
|
uint16_t current_index, bytes_remaining;
|
|
};
|
|
|
|
static enum ud3tn_result record_parser_init(struct record_parser *parser)
|
|
{
|
|
parser->status = PARSER_STATUS_GOOD;
|
|
parser->stage = RP_EXPECT_TYPE;
|
|
parser->record = init_administrative_record();
|
|
if (parser->record == NULL)
|
|
return UD3TN_FAIL;
|
|
parser->event_timestamp_s = 0;
|
|
parser->event_nanoseconds = 0;
|
|
parser->event_target_ms = NULL;
|
|
parser->record_bundle_source_str = NULL;
|
|
parser->record_bundle_source_str_length = 0;
|
|
return UD3TN_OK;
|
|
}
|
|
|
|
static void record_parser_next(
|
|
struct record_parser *parser, enum record_parser_stage next)
|
|
{
|
|
parser->stage = next;
|
|
sdnv_reset(&parser->sdnv_state);
|
|
}
|
|
|
|
static int record_parser_wait_for_sdnv(
|
|
struct record_parser *parser, enum record_parser_stage next)
|
|
{
|
|
switch (parser->sdnv_state.status) {
|
|
case SDNV_IN_PROGRESS:
|
|
break;
|
|
case SDNV_DONE:
|
|
record_parser_next(parser, next);
|
|
return 1;
|
|
case SDNV_ERROR:
|
|
parser->status = PARSER_STATUS_ERROR;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int record_parser_wait_for_timestamp_ns(
|
|
struct record_parser *parser, enum record_parser_stage next)
|
|
{
|
|
const int res = record_parser_wait_for_sdnv(parser, next);
|
|
|
|
// Finished reading both parts of the timestamp -> transfer to SR as ms and reset.
|
|
if (res) {
|
|
*parser->event_target_ms = (
|
|
parser->event_timestamp_s * 1000 +
|
|
(parser->event_nanoseconds / 1000)
|
|
);
|
|
parser->event_timestamp_s = 0;
|
|
parser->event_nanoseconds = 0;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void record_parser_read_byte(struct record_parser *parser, uint8_t byte)
|
|
{
|
|
switch (parser->stage) {
|
|
case RP_EXPECT_TYPE:
|
|
parser->record->type = (byte >> 4) & 0x0F;
|
|
parser->record->flags = byte & 0x0F;
|
|
if (parser->record->type == BUNDLE_AR_STATUS_REPORT) {
|
|
parser->record->status_report = init_status_report();
|
|
if (!parser->record->status_report)
|
|
parser->status = PARSER_STATUS_ERROR;
|
|
else
|
|
record_parser_next(parser, RP_EXPECT_REPORT_FLAGS);
|
|
} else {
|
|
/* Can't parse other types */
|
|
parser->status = PARSER_STATUS_ERROR;
|
|
}
|
|
break;
|
|
case RP_EXPECT_REPORT_FLAGS:
|
|
parser->record->status_report->status = byte;
|
|
record_parser_next(parser, RP_EXPECT_REPORT_REASON);
|
|
break;
|
|
case RP_EXPECT_REPORT_REASON:
|
|
parser->record->status_report->reason = byte;
|
|
if (HAS_FLAG(parser->record->flags,
|
|
BUNDLE_AR_FLAG_FRAGMENT)
|
|
) {
|
|
record_parser_next(parser,
|
|
RP_EXPECT_FRAGMENT_OFFSET);
|
|
} else {
|
|
record_parser_next(parser,
|
|
RP_EXPECT_TIME_RECEPTION_SECONDS);
|
|
}
|
|
break;
|
|
case RP_EXPECT_FRAGMENT_OFFSET:
|
|
sdnv_read_u64(&parser->sdnv_state,
|
|
&parser->record->fragment_offset, byte);
|
|
record_parser_wait_for_sdnv(
|
|
parser, RP_EXPECT_FRAGMENT_LENGTH);
|
|
break;
|
|
case RP_EXPECT_FRAGMENT_LENGTH:
|
|
sdnv_read_u64(&parser->sdnv_state,
|
|
&parser->record->fragment_length, byte);
|
|
record_parser_wait_for_sdnv(
|
|
parser, RP_EXPECT_TIME_RECEPTION_SECONDS);
|
|
break;
|
|
case RP_EXPECT_TIME_RECEPTION_SECONDS:
|
|
if (HAS_FLAG(parser->record->status_report->status,
|
|
BUNDLE_SR_FLAG_BUNDLE_RECEIVED)) {
|
|
parser->event_target_ms =
|
|
&parser->record->status_report->bundle_received_time;
|
|
sdnv_read_u64(&parser->sdnv_state,
|
|
&parser->event_timestamp_s, byte);
|
|
record_parser_wait_for_sdnv(
|
|
parser, RP_EXPECT_TIME_RECEPTION_NANOSECONDS);
|
|
break;
|
|
}
|
|
fallthrough_ok;
|
|
case RP_EXPECT_TIME_RECEPTION_NANOSECONDS:
|
|
if (HAS_FLAG(parser->record->status_report->status,
|
|
BUNDLE_SR_FLAG_BUNDLE_RECEIVED)) {
|
|
sdnv_read_u32(&parser->sdnv_state,
|
|
&parser->event_nanoseconds, byte);
|
|
record_parser_wait_for_timestamp_ns(
|
|
parser, RP_EXPECT_TIME_CUSTODY_SECONDS);
|
|
break;
|
|
}
|
|
fallthrough_ok;
|
|
case RP_EXPECT_TIME_CUSTODY_SECONDS:
|
|
if (HAS_FLAG(parser->record->status_report->status,
|
|
BUNDLE_SR_FLAG_CUSTODY_TRANSFER)) {
|
|
parser->event_target_ms =
|
|
&parser->record->status_report->bundle_custody_accepted_time;
|
|
sdnv_read_u64(&parser->sdnv_state,
|
|
&parser->event_timestamp_s, byte);
|
|
record_parser_wait_for_sdnv(
|
|
parser, RP_EXPECT_TIME_CUSTODY_NANOSECONDS);
|
|
break;
|
|
}
|
|
fallthrough_ok;
|
|
case RP_EXPECT_TIME_CUSTODY_NANOSECONDS:
|
|
if (HAS_FLAG(parser->record->status_report->status,
|
|
BUNDLE_SR_FLAG_CUSTODY_TRANSFER)) {
|
|
sdnv_read_u32(&parser->sdnv_state,
|
|
&parser->event_nanoseconds, byte);
|
|
record_parser_wait_for_timestamp_ns(
|
|
parser, RP_EXPECT_TIME_FORWARD_SECONDS);
|
|
break;
|
|
}
|
|
fallthrough_ok;
|
|
case RP_EXPECT_TIME_FORWARD_SECONDS:
|
|
if (HAS_FLAG(parser->record->status_report->status,
|
|
BUNDLE_SR_FLAG_BUNDLE_FORWARDED)) {
|
|
parser->event_target_ms =
|
|
&parser->record->status_report->bundle_forwarded_time;
|
|
sdnv_read_u64(&parser->sdnv_state,
|
|
&parser->event_timestamp_s, byte);
|
|
record_parser_wait_for_sdnv(
|
|
parser, RP_EXPECT_TIME_FORWARD_NANOSECONDS);
|
|
break;
|
|
}
|
|
fallthrough_ok;
|
|
case RP_EXPECT_TIME_FORWARD_NANOSECONDS:
|
|
if (HAS_FLAG(parser->record->status_report->status,
|
|
BUNDLE_SR_FLAG_BUNDLE_FORWARDED)) {
|
|
sdnv_read_u32(&parser->sdnv_state,
|
|
&parser->event_nanoseconds, byte);
|
|
record_parser_wait_for_timestamp_ns(
|
|
parser, RP_EXPECT_TIME_DELIVERY_SECONDS);
|
|
break;
|
|
}
|
|
fallthrough_ok;
|
|
case RP_EXPECT_TIME_DELIVERY_SECONDS:
|
|
if (HAS_FLAG(parser->record->status_report->status,
|
|
BUNDLE_SR_FLAG_BUNDLE_DELIVERED)) {
|
|
parser->event_target_ms =
|
|
&parser->record->status_report->bundle_delivered_time;
|
|
sdnv_read_u64(&parser->sdnv_state,
|
|
&parser->event_timestamp_s, byte);
|
|
record_parser_wait_for_sdnv(
|
|
parser, RP_EXPECT_TIME_DELIVERY_NANOSECONDS);
|
|
break;
|
|
}
|
|
fallthrough_ok;
|
|
case RP_EXPECT_TIME_DELIVERY_NANOSECONDS:
|
|
if (HAS_FLAG(parser->record->status_report->status,
|
|
BUNDLE_SR_FLAG_BUNDLE_DELIVERED)) {
|
|
sdnv_read_u32(&parser->sdnv_state,
|
|
&parser->event_nanoseconds, byte);
|
|
record_parser_wait_for_timestamp_ns(
|
|
parser, RP_EXPECT_TIME_DELETION_SECONDS);
|
|
break;
|
|
}
|
|
fallthrough_ok;
|
|
case RP_EXPECT_TIME_DELETION_SECONDS:
|
|
if (HAS_FLAG(parser->record->status_report->status,
|
|
BUNDLE_SR_FLAG_BUNDLE_DELETED)) {
|
|
parser->event_target_ms =
|
|
&parser->record->status_report->bundle_deleted_time;
|
|
sdnv_read_u64(&parser->sdnv_state,
|
|
&parser->event_timestamp_s, byte);
|
|
record_parser_wait_for_sdnv(
|
|
parser, RP_EXPECT_TIME_DELETION_NANOSECONDS);
|
|
break;
|
|
}
|
|
fallthrough_ok;
|
|
case RP_EXPECT_TIME_DELETION_NANOSECONDS:
|
|
if (HAS_FLAG(parser->record->status_report->status,
|
|
BUNDLE_SR_FLAG_BUNDLE_DELETED)) {
|
|
sdnv_read_u32(&parser->sdnv_state,
|
|
&parser->event_nanoseconds, byte);
|
|
record_parser_wait_for_timestamp_ns(
|
|
parser, RP_EXPECT_BUNDLE_CREATION_TIMESTAMP);
|
|
break;
|
|
}
|
|
fallthrough_ok;
|
|
case RP_EXPECT_BUNDLE_CREATION_TIMESTAMP:
|
|
sdnv_read_u64(&parser->sdnv_state,
|
|
&parser->record->bundle_creation_timestamp_ms,
|
|
byte);
|
|
// Parse the current byte and apply time conversion to ms if it
|
|
// is the last byte
|
|
if (record_parser_wait_for_sdnv(
|
|
parser, RP_EXPECT_BUNDLE_CREATION_SEQUENCE)
|
|
)
|
|
parser->record->bundle_creation_timestamp_ms *= 1000;
|
|
break;
|
|
case RP_EXPECT_BUNDLE_CREATION_SEQUENCE:
|
|
sdnv_read_u64(&parser->sdnv_state,
|
|
&parser->record->bundle_sequence_number,
|
|
byte);
|
|
record_parser_wait_for_sdnv(
|
|
parser, RP_EXPECT_BUNDLE_SOURCE_LENGTH);
|
|
break;
|
|
case RP_EXPECT_BUNDLE_SOURCE_LENGTH:
|
|
sdnv_read_u16(&parser->sdnv_state,
|
|
&parser->record_bundle_source_str_length,
|
|
byte);
|
|
if (record_parser_wait_for_sdnv(
|
|
parser, RP_EXPECT_BUNDLE_SOURCE_EID)
|
|
) {
|
|
parser->record_bundle_source_str =
|
|
malloc(parser->record_bundle_source_str_length + 1);
|
|
if (!parser->record_bundle_source_str)
|
|
parser->status = PARSER_STATUS_ERROR;
|
|
parser->current_index = 0;
|
|
parser->bytes_remaining = parser->record_bundle_source_str_length;
|
|
}
|
|
break;
|
|
case RP_EXPECT_BUNDLE_SOURCE_EID:
|
|
parser->bytes_remaining--;
|
|
parser->record_bundle_source_str[parser->current_index++] = (char)byte;
|
|
if (parser->bytes_remaining == 0) {
|
|
parser->record_bundle_source_str[parser->current_index] = '\0';
|
|
parser->record->bundle_source_eid = eid_from_string(
|
|
parser->record_bundle_source_str
|
|
);
|
|
if (!eid_is_error(parser->record->bundle_source_eid))
|
|
parser->status = PARSER_STATUS_DONE;
|
|
else
|
|
parser->status = PARSER_STATUS_ERROR;
|
|
}
|
|
break;
|
|
default:
|
|
parser->status = PARSER_STATUS_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
struct bundle_administrative_record *bundle6_parse_administrative_record(
|
|
const uint8_t *const data, const size_t length)
|
|
{
|
|
struct record_parser parser;
|
|
uint32_t i = 0;
|
|
const uint8_t *cur_byte;
|
|
|
|
if (data == NULL || record_parser_init(&parser) != UD3TN_OK)
|
|
return NULL;
|
|
cur_byte = data;
|
|
parser.record->start_of_record_ptr = data + 1;
|
|
while (parser.status == PARSER_STATUS_GOOD && i < length) {
|
|
record_parser_read_byte(&parser, *cur_byte);
|
|
i++;
|
|
cur_byte++;
|
|
}
|
|
free(parser.record_bundle_source_str);
|
|
parser.record_bundle_source_str = NULL;
|
|
if (parser.status == PARSER_STATUS_DONE)
|
|
return parser.record;
|
|
free_administrative_record(parser.record);
|
|
return NULL;
|
|
}
|