mirror of
https://gitlab.com/d3tn/ud3tn.git
synced 2026-08-13 12:33:27 +02:00
AAP2 uses uint32, so we do, too. This moves the check for a negative value to the UAPI backend implementation. Signed-off-by: Felix Walter <felix.walter@d3tn.com>
502 lines
13 KiB
C
502 lines
13 KiB
C
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
/** \file ud3tn_unified_api_backend.c
|
|
*
|
|
* \brief This file contains the functions that implement a C API on top of the
|
|
* ud3tn AAP2. Destined to be moved to ud3tn official code.
|
|
*
|
|
* \copyright (c) 2024 Alma Mater Studiorum, University of Bologna.
|
|
*
|
|
* \par License
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* \author Fabio Colonna, fabio.colonna3@studio.unibo.it
|
|
* \author Beatrice Barbieri, beatrice.barbieri7@studio.unibo.it
|
|
*/
|
|
|
|
#include "aap2/aap2_client.h"
|
|
#include "aap2/aap2_unified_api_backend.h"
|
|
|
|
#include "platform/hal_io.h"
|
|
|
|
#include "ud3tn/eid.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
/* --------------- PUBLIC FUNCTIONS --------------- */
|
|
|
|
ud3tn_error_t ud3tn_close(ud3tn_handle_t handle)
|
|
{
|
|
LOGF_DEBUG("Entering %s", __func__);
|
|
|
|
aap2_client_close(handle->input_client);
|
|
aap2_client_close(handle->output_client);
|
|
free(handle);
|
|
|
|
return UD3TN_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @note This function creates two connections on two different sockets in order to perform
|
|
* bidirectional communication.
|
|
*/
|
|
ud3tn_error_t ud3tn_open(ud3tn_handle_t *out_handle, char *sock_path)
|
|
{
|
|
LOGF_DEBUG("Entering %s", __func__);
|
|
*out_handle = NULL;
|
|
|
|
LOGF_DEBUG("Using AAP2 UNIX socket path: %s", sock_path);
|
|
|
|
UD3TNClientInfo *const out_client = malloc(sizeof(UD3TNClientInfo));
|
|
|
|
if (!out_client) {
|
|
LOG_ERROR("Could not allocate memory for client");
|
|
return UD3TN_EOPEN;
|
|
}
|
|
|
|
*out_client = (UD3TNClientInfo){
|
|
aap2_client_open_socket(sock_path, 0),
|
|
aap2_client_open_socket(sock_path, 0)
|
|
};
|
|
|
|
if (!out_client->input_client || !out_client->output_client) {
|
|
LOG_WARN("Opening AAP2 client connections failed");
|
|
aap2_client_close(out_client->input_client);
|
|
aap2_client_close(out_client->output_client);
|
|
free(out_client);
|
|
*out_handle = NULL;
|
|
return UD3TN_EOPEN;
|
|
}
|
|
|
|
LOG_DEBUG("Connected to AAP2 through a UNIX socket");
|
|
LOGF_DEBUG("Node ID: %s", aap2_client_get_node_id(out_client->input_client));
|
|
*out_handle = out_client;
|
|
return UD3TN_SUCCESS;
|
|
}
|
|
|
|
ud3tn_error_t ud3tn_open_with_ip(const char *const addr, int port, ud3tn_handle_t *out_handle)
|
|
{
|
|
LOGF_DEBUG("Entering %s", __func__);
|
|
*out_handle = NULL;
|
|
|
|
// Params check
|
|
if (port < 0 || port > (int)UINT16_MAX) {
|
|
LOGF_WARN("Invalid port number: %d", port);
|
|
return UD3TN_EINVAL;
|
|
}
|
|
|
|
if (!addr) {
|
|
LOG_WARN("INET address is null");
|
|
return UD3TN_EINVAL;
|
|
}
|
|
|
|
char service[6];
|
|
const int rv = snprintf(service, 6, "%d", port);
|
|
|
|
if (rv <= 0 || rv >= 6) {
|
|
LOGF_WARN("Failed to convert port number: %d", port);
|
|
return UD3TN_EINVAL;
|
|
}
|
|
|
|
UD3TNClientInfo *const out_client = malloc(sizeof(UD3TNClientInfo));
|
|
|
|
if (!out_client) {
|
|
LOG_ERROR("Could not allocate memory for client");
|
|
return UD3TN_EOPEN;
|
|
}
|
|
|
|
*out_client = (UD3TNClientInfo){
|
|
aap2_client_open_tcp(addr, service, 0),
|
|
aap2_client_open_tcp(addr, service, 0)
|
|
};
|
|
|
|
if (!out_client->input_client || !out_client->output_client) {
|
|
LOG_WARN("Opening AAP2 client connections failed");
|
|
aap2_client_close(out_client->input_client);
|
|
aap2_client_close(out_client->output_client);
|
|
free(out_client);
|
|
return UD3TN_EOPEN;
|
|
}
|
|
|
|
LOGF_DEBUG(
|
|
"Connected to AAP2 through a INET socket towards: %s:%d",
|
|
addr,
|
|
port
|
|
);
|
|
LOGF_DEBUG("Node ID: %s", aap2_client_get_node_id(out_client->input_client));
|
|
*out_handle = out_client;
|
|
return UD3TN_SUCCESS;
|
|
}
|
|
|
|
// Generate a random string consisting of valid ASCII characters != '\0'
|
|
static bool generate_random_ascii_string(uint8_t *buf, size_t buf_len)
|
|
{
|
|
ASSERT(buf_len != 0 && buf_len <= (size_t)SSIZE_MAX);
|
|
|
|
const int fd = open("/dev/urandom", O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
return false;
|
|
|
|
const ssize_t rv = read(fd, buf, buf_len);
|
|
|
|
if (rv != (ssize_t)buf_len)
|
|
return false;
|
|
|
|
// Ensure that it is an ASCII character != '\0'
|
|
for (ssize_t c = 0; c < rv; c++)
|
|
buf[c] = (buf[c] % 127) + 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @note This function performs the configuration of both connections.
|
|
* One where the client is passive (to receive AAPMessage
|
|
* sent by the AA) and one where the client is active (to send AAPMessage to the AA).
|
|
*/
|
|
ud3tn_error_t ud3tn_register(ud3tn_handle_t handle, ud3tn_reg_info_t *const reg_info)
|
|
{
|
|
// Params check
|
|
if (!handle->input_client || !handle->output_client || !reg_info ||
|
|
reg_info->keepalive_seconds < 0 ||
|
|
aap2_client_is_configured(handle->input_client) ||
|
|
aap2_client_is_configured(handle->output_client)) {
|
|
LOG_WARN("Invalid input param(s)");
|
|
return UD3TN_EINVAL;
|
|
}
|
|
|
|
LOGF_DEBUG("Entering %s", __func__);
|
|
|
|
// Generate a random secret.
|
|
// As we only want to send/receive bundles, the sole requirement is that both secrets match.
|
|
char secret[64];
|
|
|
|
if (!generate_random_ascii_string((uint8_t *)secret, 64)) {
|
|
LOG_WARN("Cannot generate random secret for AAP2 connection");
|
|
return UD3TN_EREG;
|
|
}
|
|
|
|
const enum aap2_client_error rv_output = aap2_client_register(
|
|
handle->output_client,
|
|
(struct aap2_client_spec){
|
|
reg_info->endpoint.uri,
|
|
secret,
|
|
false,
|
|
reg_info->keepalive_seconds,
|
|
},
|
|
0
|
|
);
|
|
|
|
if (rv_output < 0) {
|
|
LOGF_WARN("Registration of output client failed with status code: %d", rv_output);
|
|
return UD3TN_EREG;
|
|
}
|
|
|
|
const enum aap2_client_error rv_input = aap2_client_register(
|
|
handle->input_client,
|
|
(struct aap2_client_spec){
|
|
reg_info->endpoint.uri,
|
|
secret,
|
|
true,
|
|
reg_info->keepalive_seconds,
|
|
},
|
|
0
|
|
);
|
|
|
|
if (rv_input < 0) {
|
|
LOGF_WARN("Registration of input client failed with status code: %d", rv_input);
|
|
return UD3TN_EREG;
|
|
}
|
|
|
|
LOG_DEBUG("Successfully registered to AAP2");
|
|
return UD3TN_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @note This function fills a BundleADU with input parameters, sends it along with its payload
|
|
* and retreives the timestamp and seqno from the response.
|
|
*/
|
|
ud3tn_error_t ud3tn_send(ud3tn_handle_t handle, ud3tn_bundle_spec_t *const spec,
|
|
ud3tn_bundle_payload_t *const payload)
|
|
{
|
|
LOGF_DEBUG("Entering %s", __func__);
|
|
|
|
// Params check
|
|
if (!handle->output_client || !spec || !payload ||
|
|
!aap2_client_is_configured(handle->output_client) ||
|
|
aap2_client_is_subscriber(handle->output_client)) {
|
|
LOG_WARN("Invalid input param(s)");
|
|
return UD3TN_EINVAL;
|
|
}
|
|
|
|
struct aap2_client_bundle_spec aap2_cspec = {
|
|
.source_eid = spec->source.uri,
|
|
.dest_eid = spec->dest.uri,
|
|
.creation_timestamp_dtn_ms = 0, // assign automatically
|
|
.sequence_number = 0, // assign automatically
|
|
.flags = (struct aap2_bundle_adu_flags){
|
|
.is_bpdu = (spec->proc_flags == BUNDLE_FLAG_ADMINISTRATIVE_RECORD),
|
|
.with_bdm_auth = spec->bdm_auth_validated,
|
|
},
|
|
};
|
|
|
|
const enum aap2_client_error rv = aap2_client_send(
|
|
handle->output_client,
|
|
aap2_cspec,
|
|
(uint8_t *)payload->buf.buf_val,
|
|
payload->buf.buf_len,
|
|
0,
|
|
&aap2_cspec // fine if contents are overwritten
|
|
);
|
|
|
|
switch (rv) {
|
|
case AAP2_CLIENT_OK:
|
|
break;
|
|
default:
|
|
// NOTE: We may want to handle the individual error codes differently.
|
|
LOGF_WARN("Failed sending ADU via AAP2, code = %d", rv);
|
|
return UD3TN_ESEND;
|
|
}
|
|
|
|
LOG_DEBUG("Got SUCCESS back. Successfully sent bundle");
|
|
|
|
#ifndef __clang_analyzer__
|
|
// NOTE: clang-tidy reports a false positive for a double free here.
|
|
free(aap2_cspec.source_eid); // unused
|
|
free(aap2_cspec.dest_eid); // unused
|
|
#endif // __clang_analyzer__
|
|
spec->creation_ts.time = aap2_cspec.creation_timestamp_dtn_ms;
|
|
spec->creation_ts.seqno = aap2_cspec.sequence_number;
|
|
return UD3TN_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @note This function receives a BundleADU and its payload.
|
|
*/
|
|
ud3tn_error_t ud3tn_recv(ud3tn_handle_t handle, ud3tn_bundle_spec_t *out_spec,
|
|
ud3tn_bundle_payload_t *out_payload, ud3tn_timeval_t timeout)
|
|
{
|
|
// Params check
|
|
if (!handle->input_client || !out_spec || !out_payload ||
|
|
!aap2_client_is_configured(handle->input_client) ||
|
|
!aap2_client_is_subscriber(handle->input_client)) {
|
|
LOG_WARN("Invalid input param(s)");
|
|
return UD3TN_EINVAL;
|
|
}
|
|
|
|
LOGF_DEBUG("Entering %s", __func__);
|
|
|
|
if (timeout == UD3TN_NO_TIMEOUT || timeout == 0)
|
|
LOG_DEBUG("About to recv with no timeout");
|
|
else
|
|
LOGF_DEBUG("About to start recv with timeout: %lu", timeout);
|
|
|
|
struct aap2_client_bundle_spec aap2_cspec = {
|
|
.source_eid = NULL,
|
|
.dest_eid = NULL,
|
|
.creation_timestamp_dtn_ms = 0,
|
|
.sequence_number = 0,
|
|
.flags = (struct aap2_bundle_adu_flags){false, false, false},
|
|
};
|
|
uint8_t *payload;
|
|
size_t payload_len;
|
|
enum aap2_client_error rv;
|
|
|
|
do {
|
|
rv = aap2_client_recv(
|
|
handle->input_client,
|
|
&aap2_cspec,
|
|
&payload,
|
|
&payload_len,
|
|
timeout
|
|
);
|
|
} while (rv == AAP2_CLIENT_EAGAIN); // auto-retry if wrong message type was received
|
|
|
|
switch (rv) {
|
|
case AAP2_CLIENT_OK:
|
|
break;
|
|
default:
|
|
// NOTE: We may want to handle the individual error codes differently.
|
|
LOGF_WARN("Failed receiving ADU via AAP2, code = %d", rv);
|
|
return UD3TN_ERECV;
|
|
}
|
|
|
|
out_payload->buf.buf_len = payload_len;
|
|
out_payload->buf.buf_val = (char *)payload;
|
|
out_payload->status_report = NULL;
|
|
snprintf(
|
|
out_spec->source.uri,
|
|
ARRAY_LENGTH(out_spec->source.uri),
|
|
"%s",
|
|
aap2_cspec.source_eid
|
|
);
|
|
free(aap2_cspec.source_eid);
|
|
free(aap2_cspec.dest_eid);
|
|
out_spec->creation_ts.time = aap2_cspec.creation_timestamp_dtn_ms;
|
|
out_spec->creation_ts.seqno = aap2_cspec.sequence_number;
|
|
|
|
return UD3TN_SUCCESS;
|
|
}
|
|
|
|
ud3tn_error_t ud3tn_build_local_eid(ud3tn_handle_t handle, ud3tn_endpoint_id_t *out_local_eid,
|
|
const char *const service_tag, ud3tn_scheme_t eid_scheme)
|
|
{
|
|
LOGF_DEBUG("Entering %s", __func__);
|
|
|
|
// Params check
|
|
if (!handle->input_client || !out_local_eid || !service_tag) {
|
|
LOG_WARN("Invalid input param(s)");
|
|
return UD3TN_EINVAL;
|
|
}
|
|
|
|
const char *nodeID = aap2_client_get_node_id(handle->input_client);
|
|
const enum eid_scheme auto_determined_scheme = get_eid_scheme(nodeID);
|
|
|
|
switch (eid_scheme) {
|
|
case UD3TN_DTN_SCHEME:
|
|
if (auto_determined_scheme != EID_SCHEME_DTN) {
|
|
LOG_WARN("Cannot register as DTN as the node EID is not dtn");
|
|
return UD3TN_EINVAL;
|
|
}
|
|
break;
|
|
case UD3TN_IPN_SCHEME:
|
|
if (auto_determined_scheme != EID_SCHEME_IPN) {
|
|
LOG_WARN("Cannot register as IPN as the node EID is not ipn");
|
|
return UD3TN_EINVAL;
|
|
}
|
|
break;
|
|
default:
|
|
LOGF_WARN("Wrong EID scheme: %d", eid_scheme);
|
|
return UD3TN_EINVAL;
|
|
}
|
|
|
|
char *eid = aap2_client_get_eid_for_agent(nodeID, service_tag);
|
|
|
|
if (!eid) {
|
|
LOG_WARN("Failed to determine EID for registered AAP2 client");
|
|
return UD3TN_EINVAL;
|
|
}
|
|
|
|
const int rv = snprintf(out_local_eid->uri, ARRAY_LENGTH(out_local_eid->uri), "%s", eid);
|
|
|
|
if (rv <= 0 || rv >= (int)ARRAY_LENGTH(out_local_eid->uri)) {
|
|
LOG_WARN("Failed to copy resulting EID to dest buffer (too long?)");
|
|
free(eid);
|
|
return UD3TN_EINVAL;
|
|
}
|
|
|
|
free(eid);
|
|
return UD3TN_SUCCESS;
|
|
}
|
|
|
|
ud3tn_error_t ud3tn_copy_eid(ud3tn_endpoint_id_t *dst, ud3tn_endpoint_id_t *const src)
|
|
{
|
|
// Params check
|
|
if (!dst || !src) {
|
|
LOG_WARN("Invalid input param(s)");
|
|
return UD3TN_EINVAL;
|
|
}
|
|
|
|
memset(dst->uri, 0, sizeof(dst->uri));
|
|
|
|
if (src->uri[0] == '\0')
|
|
return UD3TN_SUCCESS;
|
|
|
|
const int rv = snprintf(dst->uri, ARRAY_LENGTH(dst->uri), "%s", src->uri);
|
|
|
|
if (rv <= 0 || rv >= (int)ARRAY_LENGTH(dst->uri)) {
|
|
LOG_WARN("Failed to copy EID (too long?)");
|
|
return UD3TN_EINVAL;
|
|
}
|
|
return UD3TN_SUCCESS;
|
|
}
|
|
|
|
ud3tn_error_t ud3tn_parse_eid_string(ud3tn_endpoint_id_t *out_eid, const char *const str)
|
|
{
|
|
// Params check
|
|
if (!str || !out_eid) {
|
|
LOG_WARN("Invalid input param(s)");
|
|
return UD3TN_EINVAL;
|
|
}
|
|
|
|
memset(out_eid->uri, 0, sizeof(out_eid->uri));
|
|
|
|
const int rv = snprintf(out_eid->uri, ARRAY_LENGTH(out_eid->uri), "%s", str);
|
|
|
|
if (rv <= 0 || rv >= (int)ARRAY_LENGTH(out_eid->uri)) {
|
|
LOG_WARN("Failed to copy EID (too long?)");
|
|
return UD3TN_EINVAL;
|
|
}
|
|
return UD3TN_SUCCESS;
|
|
}
|
|
|
|
ud3tn_error_t ud3tn_set_payload(ud3tn_bundle_payload_t *out_payload, const uint8_t *const buf,
|
|
uint32_t buf_length)
|
|
{
|
|
// Params check
|
|
if (!out_payload || !out_payload->buf.buf_val || !buf) {
|
|
LOG_WARN("Invalid input param(s)");
|
|
return UD3TN_EINVAL;
|
|
}
|
|
|
|
// FIXME: Dangerous API. Implicitly assumes that out_payload->buf.buf_val is allocated with
|
|
// len == buf_length. Should be adapted in upstream Unified API backend.
|
|
memcpy(out_payload->buf.buf_val, buf, buf_length);
|
|
out_payload->buf.buf_len = buf_length;
|
|
|
|
return UD3TN_SUCCESS;
|
|
}
|
|
|
|
void ud3tn_free_payload(ud3tn_bundle_payload_t *const payload)
|
|
{
|
|
LOGF_DEBUG("Entering %s", __func__);
|
|
|
|
free(payload->buf.buf_val);
|
|
payload->buf.buf_val = NULL;
|
|
payload->buf.buf_len = 0;
|
|
}
|
|
|
|
ud3tn_error_t ud3tn_ping_aap(ud3tn_handle_t handle)
|
|
{
|
|
LOGF_DEBUG("Entering %s", __func__);
|
|
|
|
// Params check
|
|
if (!handle->output_client ||
|
|
!aap2_client_is_configured(handle->output_client) ||
|
|
aap2_client_is_subscriber(handle->output_client)) {
|
|
LOG_WARN("Invalid input param(s)");
|
|
return UD3TN_EINVAL;
|
|
}
|
|
|
|
const enum aap2_client_error rv = aap2_client_keepalive(
|
|
handle->output_client,
|
|
0
|
|
);
|
|
|
|
switch (rv) {
|
|
case AAP2_CLIENT_OK:
|
|
break;
|
|
default:
|
|
// NOTE: We may want to handle the individual error codes differently.
|
|
LOGF_WARN("Failed sending keepalive via AAP2, code = %d", rv);
|
|
return UD3TN_ESEND;
|
|
}
|
|
|
|
LOG_DEBUG("Got reply. AAP2 connection is still alive.");
|
|
return UD3TN_SUCCESS;
|
|
}
|