// 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 #include #include #include #include #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; // Zero EID length is invalid if (parser->bytes_remaining == 0) parser->status = PARSER_STATUS_ERROR; } 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; }