// 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 // ------------------------------- // Administrative Record Generator // ------------------------------- static struct bundle *encapsulate_record( const struct bundle * const bundle, const char *source_eid, const char *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, 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 char *source_eid, const char *dest_eid, const bool is_custody_signal, const uint8_t prefix_1, const uint8_t prefix_2, const uint64_t timestamp_ms) { 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; /* Write record type, flags, prefixes (status flags, reason, ...) */ fragment = bundle_is_fragmented(bundle); (*cur++) = (is_custody_signal ? 0x20 : 0x10) | (fragment ? 0x01 : 0x00); (*cur++) = prefix_1; if (is_custody_signal) (*cur++) = prefix_2; /* 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 */ 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); 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_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); return ret; } struct bundle *bundle6_generate_status_report( const struct bundle * const bundle, const struct bundle_status_report *report, const char *local_eid, const uint64_t timestamp_s) { return generate_record( bundle, local_eid, bundle->report_to, true, (uint8_t)(report->status), (uint8_t)(report->reason), timestamp_s ); } struct bundle *bundle6_generate_custody_signal( const struct bundle * const bundle, const struct bundle_custody_signal *signal, const char *local_eid, const uint64_t timestamp_ms) { return generate_record( bundle, local_eid, bundle->current_custodian, false, ((uint8_t)(signal->reason) & 0x7F) | ((signal->type == BUNDLE_CS_TYPE_ACCEPTANCE) ? 0x80 : 0x00), 0, 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_CUSTODY_STATUS, RP_EXPECT_FRAGMENT_OFFSET, RP_EXPECT_FRAGMENT_LENGTH, RP_EXPECT_RECORD_DTN_SECONDS, RP_EXPECT_RECORD_DTN_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; 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 = malloc(sizeof(struct bundle_administrative_record)); if (parser->record == NULL) return UD3TN_FAIL; parser->record->status_report = NULL; parser->record->custody_signal = NULL; parser->record->fragment_offset = 0; parser->record->fragment_length = 0; parser->record->event_timestamp = 0; parser->record->event_nanoseconds = 0; parser->record->bundle_creation_timestamp_ms = 0; parser->record->bundle_sequence_number = 0; parser->record->bundle_source_eid_length = 0; parser->record->bundle_source_eid = NULL; 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 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 = (struct bundle_status_report *)malloc( sizeof(struct bundle_status_report)); record_parser_next( parser, RP_EXPECT_REPORT_FLAGS); } else if (parser->record->type == BUNDLE_AR_CUSTODY_SIGNAL ) { parser->record->custody_signal = (struct bundle_custody_signal *)malloc( sizeof(struct bundle_custody_signal)); record_parser_next( parser, RP_EXPECT_CUSTODY_STATUS); } 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_RECORD_DTN_SECONDS); } break; case RP_EXPECT_CUSTODY_STATUS: parser->record->custody_signal->type = (byte >> 7) ? BUNDLE_CS_TYPE_ACCEPTANCE : BUNDLE_CS_TYPE_REFUSAL; parser->record->custody_signal->reason = byte & 0x7F; 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_RECORD_DTN_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_RECORD_DTN_SECONDS); break; case RP_EXPECT_RECORD_DTN_SECONDS: sdnv_read_u64(&parser->sdnv_state, &parser->record->event_timestamp, byte); record_parser_wait_for_sdnv( parser, RP_EXPECT_RECORD_DTN_NANOSECONDS); break; case RP_EXPECT_RECORD_DTN_NANOSECONDS: sdnv_read_u32(&parser->sdnv_state, &parser->record->event_nanoseconds, byte); record_parser_wait_for_sdnv( parser, RP_EXPECT_BUNDLE_CREATION_TIMESTAMP); break; 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_eid_length, byte); if (record_parser_wait_for_sdnv( parser, RP_EXPECT_BUNDLE_SOURCE_EID) ) { parser->record->bundle_source_eid = malloc(parser->record ->bundle_source_eid_length + 1); if (!parser->record->bundle_source_eid) parser->status = PARSER_STATUS_ERROR; parser->current_index = 0; parser->bytes_remaining = parser->record ->bundle_source_eid_length; } break; case RP_EXPECT_BUNDLE_SOURCE_EID: parser->bytes_remaining--; parser->record->bundle_source_eid [parser->current_index++] = (char)byte; if (parser->bytes_remaining == 0) { parser->record->bundle_source_eid [parser->current_index] = '\0'; parser->status = PARSER_STATUS_DONE; } 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++; } if (parser.status == PARSER_STATUS_DONE) return parser.record; free_administrative_record(parser.record); return NULL; }