mirror of
https://gitlab.com/d3tn/ud3tn.git
synced 2026-08-13 12:33:27 +02:00
Re-integrate v0.13.0 routing code as "integrated default BDM"
This makes µD3TN behave the same as v0.13.0 when executed without an
additional commandline argument. A new commandline argument `-d` /
`--external-dispatch` is added, which enables the use of external BDMs.
The default forwarding implementation is now again provided using the
v0.13 code, extracted from e1621765a4 and
adapted to the new agent-based forwarding implementation.
Central changes to the old code include:
- A new "Routing Agent" that handles incoming configuration commands,
FIB updates, and BDM dispatch requests.
- The use of the BDM authorization flag to authorize contact
configuration commands.
- The Contact Manager now only triggers the creation and removal of
links / FIB entries; bundle dispatch is triggered through the FIB and
BDM callback functions of the Routing Agent.
- The fragmentation logic is adapted to store the original bundle along
with an offset and length value, instead of pre-creating and storing
the fragments.
- The bundle re-scheduling logic integrated into the Routing Agent is
simplified and does not support changing the fragmentation parameters.
A new function is added to the Router that searches for a new route
for such fragments that were already scheduled at some point,
considering them as un-fragmentable bundle with overridden fragment
offset and length.
Signed-off-by: Felix Walter <felix.walter@d3tn.com>
This commit is contained in:
parent
ad4f56b62d
commit
87be9749af
35 changed files with 1144 additions and 1002 deletions
|
|
@ -122,10 +122,7 @@ build-library:
|
|||
- pip install -e "python-ud3tn-utils"
|
||||
- while ! [ -r ./ud3tn.socket ]; do sleep 0.1; done
|
||||
- while ! [ -r ./ud3tn.aap2.socket ]; do sleep 0.1; done
|
||||
- aap2-bdm-ud3tn-routing -vv --insecure-config & DISPATCHER_PID=$!
|
||||
- sleep 1 # give the dispatcher some time to start
|
||||
- pytest test/integration
|
||||
- kill -TERM $DISPATCHER_PID
|
||||
|
||||
unit-test-posix-debug:
|
||||
stage: functional_test
|
||||
|
|
@ -179,8 +176,6 @@ unit-test-posix-freebsd:
|
|||
- pip install -e "python-ud3tn-utils"
|
||||
- while ! [ -r ./ud3tn.socket ]; do sleep 0.1; done
|
||||
- while ! [ -r ./ud3tn.aap2.socket ]; do sleep 0.1; done
|
||||
- aap2-bdm-ud3tn-routing -vv --insecure-config & DISPATCHER_PID=$!
|
||||
- sleep 1 # give the dispatcher some time to start
|
||||
- pytest test/integration
|
||||
- CLA=tcpspp pytest test/integration
|
||||
- kill -TERM $UD3TN_PID
|
||||
|
|
@ -209,9 +204,29 @@ integration-test-posix-thread-sanitizer:
|
|||
- pip install -e "python-ud3tn-utils"
|
||||
- while ! [ -r ./ud3tn.socket ]; do sleep 0.1; done
|
||||
- while ! [ -r ./ud3tn.aap2.socket ]; do sleep 0.1; done
|
||||
- aap2-bdm-ud3tn-routing -vv --insecure-config & DISPATCHER_PID=$!
|
||||
- pytest test/integration
|
||||
- kill -TERM $UD3TN_PID
|
||||
- echo "Waiting for uD3TN to exit gracefully - if it doesn't, check for sanitizer warnings."
|
||||
- wait $UD3TN_PID
|
||||
|
||||
integration-test-posix-extdispatch:
|
||||
stage: functional_test
|
||||
when: always
|
||||
image: registry.gitlab.com/d3tn/ud3tn-docker-images/ci-python-clang:3.8-bookworm
|
||||
script:
|
||||
- bash test/dockerfiles/prepare_for_test.sh . /ud3tn_build TOOLCHAIN=clang sanitize-strict=yes
|
||||
- cd /ud3tn_build
|
||||
- rm -f ./ud3tn.socket ./ud3tn.aap2.socket
|
||||
- UBSAN_OPTIONS=print_stacktrace=1 build/posix/ud3tn -L 4 -c "sqlite:file::memory:?cache=shared;tcpclv3:*,4556;tcpspp:*,4223,false,1;smtcp:*,4222,false;mtcp:*,4224" --allow-remote-config --external-dispatch & UD3TN_PID=$!
|
||||
- source /ud3tn_venv/bin/activate
|
||||
- pip install -e "pyd3tn"
|
||||
- pip install -e "python-ud3tn-utils"
|
||||
- while ! [ -r ./ud3tn.socket ]; do sleep 0.1; done
|
||||
- while ! [ -r ./ud3tn.aap2.socket ]; do sleep 0.1; done
|
||||
- aap2-bdm-ud3tn-routing --insecure-config -vv & DISPATCHER_PID=$!
|
||||
- sleep 1 # give the dispatcher some time to start
|
||||
- pytest test/integration
|
||||
- CLA=tcpspp pytest test/integration
|
||||
- kill -TERM $UD3TN_PID
|
||||
- echo "Waiting for uD3TN to exit gracefully - if it doesn't, check for sanitizer warnings."
|
||||
- wait $UD3TN_PID
|
||||
|
|
@ -224,9 +239,10 @@ integration-test-posix-release:
|
|||
- bash test/dockerfiles/prepare_for_test.sh . /ud3tn_build type=release optimize=yes
|
||||
- cd /ud3tn_build
|
||||
- rm -f ./ud3tn.socket ./ud3tn.aap2.socket
|
||||
# NOTE: Here we use the external dispatcher to test also the AAPv2 secret handling
|
||||
# NOTE: TEST_AAP2_BDM_SECRET is also recognized by the integration test
|
||||
- export TEST_AAP2_BDM_SECRET=very_secure_secret
|
||||
- build/posix/ud3tn -L 3 --bdm-secret-var TEST_AAP2_BDM_SECRET --allow-remote-config &
|
||||
- build/posix/ud3tn -L 3 --bdm-secret-var TEST_AAP2_BDM_SECRET --allow-remote-config --external-dispatch &
|
||||
- source /ud3tn_venv/bin/activate
|
||||
- pip install -e "pyd3tn"
|
||||
- pip install -e "python-ud3tn-utils"
|
||||
|
|
@ -253,8 +269,6 @@ integration-test-posix-macos:
|
|||
- source .venv/bin/activate
|
||||
- while ! [ -r ./ud3tn.socket ]; do sleep 0.1; done
|
||||
- while ! [ -r ./ud3tn.aap2.socket ]; do sleep 0.1; done
|
||||
- aap2-bdm-ud3tn-routing -vv --insecure-config & DISPATCHER_PID=$!
|
||||
- sleep 1 # give the dispatcher some time to start
|
||||
- pytest test/integration
|
||||
- kill -TERM $UD3TN_PID
|
||||
- echo "Waiting for uD3TN to exit gracefully - if it doesn't, check for sanitizer warnings."
|
||||
|
|
@ -386,13 +400,10 @@ mtcp-test:
|
|||
- pip install -e "python-ud3tn-utils"
|
||||
- while ! [ -r ./ud3tn.socket ]; do sleep 0.1; done
|
||||
- while ! nc -z localhost 4224; do sleep 0.1; done
|
||||
- aap2-bdm-ud3tn-routing -vv --insecure-config & DISPATCHER_PID=$!
|
||||
- sleep 1 # give the dispatcher some time to start
|
||||
- python tools/cla/mtcp_test.py -t mtcp --payload TESTPAYLOAD &
|
||||
- timeout -v 5 python tools/cla/mtcp_sink.py --count 1 --verify-pl TESTPAYLOAD
|
||||
- sleep 0.2
|
||||
- kill -TERM $UD3TN_PID
|
||||
- kill -TERM $DISPATCHER_PID
|
||||
- echo "Waiting for uD3TN to exit gracefully - if it doesn't, check for sanitizer warnings."
|
||||
- wait $UD3TN_PID
|
||||
|
||||
|
|
@ -414,14 +425,11 @@ fragment-reassembly-test:
|
|||
- while ! nc -z localhost 4223; do sleep 0.1; done
|
||||
- while ! [ -r ./ud3tn1.socket ]; do sleep 0.1; done
|
||||
- while ! [ -r ./ud3tn2.socket ]; do sleep 0.1; done
|
||||
- aap2-bdm-ud3tn-routing -vv --socket ud3tn1.aap2.socket & DISPATCHER_PID=$!
|
||||
- sleep 1 # give the dispatcher some time to start
|
||||
- aap2-config --socket ud3tn1.aap2.socket --schedule 2 1 140 --schedule 4 1 140 dtn://ud3tn2.dtn/ mtcp:localhost:4223
|
||||
- aap-send --socket ud3tn1.socket dtn://ud3tn2.dtn/sink THISISAVERYLONGBUNDLEPAYLOADWHICHWILLBEFRAGMENTED -v
|
||||
- timeout -v 5 aap-receive --socket ud3tn2.socket --agentid sink --count 1 --verify-pl THISISAVERYLONGBUNDLEPAYLOADWHICHWILLBEFRAGMENTED --newline -vv
|
||||
- kill -TERM $UD3TN1_PID
|
||||
- kill -TERM $UD3TN2_PID
|
||||
- kill -TERM $DISPATCHER_PID
|
||||
- echo "Waiting for uD3TN to exit gracefully - if it doesn't, check for sanitizer warnings."
|
||||
- wait $UD3TN1_PID
|
||||
- wait $UD3TN2_PID
|
||||
|
|
@ -435,7 +443,8 @@ fragment-mbs-test:
|
|||
- cd /ud3tn_build
|
||||
- rm -f ./ud3tn.socket
|
||||
- rm -f ./ud3tn.aap2.socket
|
||||
- UBSAN_OPTIONS=print_stacktrace=1 build/posix/ud3tn -L 4 -c "mtcp:*,4222" -m 110 & UD3TN_PID=$!
|
||||
# NOTE: No dispatcher needed - testing that it also works without it here
|
||||
- UBSAN_OPTIONS=print_stacktrace=1 build/posix/ud3tn -L 4 -c "mtcp:*,4222" -m 110 --external-dispatch & UD3TN_PID=$!
|
||||
- source /ud3tn_venv/bin/activate
|
||||
- pip install -e "pyd3tn"
|
||||
- pip install -e "python-ud3tn-utils"
|
||||
|
|
@ -468,8 +477,6 @@ ipv6-compatibility-test:
|
|||
- while ! nc -z localhost 4223; do sleep 0.1; done
|
||||
- while ! nc -z localhost 4242; do sleep 0.1; done
|
||||
- while ! nc -z localhost 4243; do sleep 0.1; done
|
||||
- aap2-bdm-ud3tn-routing -vv --socket ud3tn1.aap2.socket & DISPATCHER_PID=$!
|
||||
- sleep 1 # give the dispatcher some time to start
|
||||
# Check v6 via AAP to ud3tn1 and via MTCP to ud3tn2
|
||||
- aap-config --tcp '::1' 4242 --schedule 2 1 140 --schedule 4 1 140 dtn://ud3tn2.dtn/ 'mtcp:[::1]:4223'
|
||||
# Check v4 via AAP to ud3tn1
|
||||
|
|
@ -480,7 +487,6 @@ ipv6-compatibility-test:
|
|||
- "if timeout -v 1 python test/functional/aap_test.py --tcp '::1' 4243; then echo 'Error: Should not be able to connect via IPv6 here.'; false; else true; fi"
|
||||
- kill -TERM $UD3TN1_PID
|
||||
- kill -TERM $UD3TN2_PID
|
||||
- kill -TERM $DISPATCHER_PID
|
||||
- echo "Waiting for uD3TN to exit gracefully - if it doesn't, check for sanitizer warnings."
|
||||
- wait $UD3TN1_PID
|
||||
- wait $UD3TN2_PID
|
||||
|
|
@ -558,11 +564,8 @@ integration-test-valgrind-memory-check:
|
|||
- pip install -e "python-ud3tn-utils"
|
||||
- while ! [ -r ./ud3tn.socket ]; do sleep 0.1; done
|
||||
- while ! [ -r ./ud3tn.aap2.socket ]; do sleep 0.1; done
|
||||
- aap2-bdm-ud3tn-routing -vv --insecure-config & DISPATCHER_PID=$!
|
||||
- sleep 1 # give the dispatcher some time to start
|
||||
- pytest test/integration
|
||||
- kill -TERM $UD3TN_PID
|
||||
- kill -TERM $DISPATCHER_PID
|
||||
- echo "Waiting for uD3TN to exit gracefully - if it doesn't, check for sanitizer warnings."
|
||||
- wait $UD3TN_PID
|
||||
|
||||
|
|
@ -611,7 +614,7 @@ coverage-test:
|
|||
- rm -f ./ud3tn.socket ./ud3tn.aap2.socket
|
||||
- make unittest-posix coverage=yes
|
||||
- timeout 60 build/posix/testud3tn
|
||||
- build/posix/ud3tn --allow-remote-config & UD3TN_PID=$!
|
||||
- build/posix/ud3tn --allow-remote-config --external-dispatch & UD3TN_PID=$!
|
||||
- source /ud3tn_venv/bin/activate
|
||||
- while ! [ -r ./ud3tn.socket ]; do sleep 0.1; done
|
||||
- while ! [ -r ./ud3tn.aap2.socket ]; do sleep 0.1; done
|
||||
|
|
@ -622,6 +625,15 @@ coverage-test:
|
|||
- kill -TERM $DISPATCHER_PID
|
||||
- echo "Waiting for uD3TN to exit gracefully - if it doesn't, check for sanitizer warnings."
|
||||
- wait $UD3TN_PID
|
||||
- rm -f ./ud3tn.socket ./ud3tn.aap2.socket
|
||||
- build/posix/ud3tn --allow-remote-config & UD3TN_PID=$!
|
||||
- source /ud3tn_venv/bin/activate
|
||||
- while ! [ -r ./ud3tn.socket ]; do sleep 0.1; done
|
||||
- while ! [ -r ./ud3tn.aap2.socket ]; do sleep 0.1; done
|
||||
- pytest test/integration
|
||||
- kill -TERM $UD3TN_PID
|
||||
- echo "Waiting for uD3TN to exit gracefully - if it doesn't, check for sanitizer warnings."
|
||||
- wait $UD3TN_PID
|
||||
- gcovr --xml-pretty --exclude-unreachable-branches --gcov-ignore-parse-errors --print-summary -o coverage.xml --root .
|
||||
- cp -v coverage.xml "$project_dir/"
|
||||
coverage: /^\s*lines:\s*\d+.\d+\%/
|
||||
|
|
|
|||
|
|
@ -1,100 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#include "agents/config_agent.h"
|
||||
#include "agents/config_parser.h"
|
||||
|
||||
#include "ud3tn/agent_manager.h"
|
||||
#include "ud3tn/bundle_processor.h"
|
||||
#include "ud3tn/common.h"
|
||||
#include "ud3tn/eid.h"
|
||||
|
||||
#include "platform/hal_io.h"
|
||||
#include "platform/hal_types.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static struct config_parser parser;
|
||||
|
||||
struct config_agent_params {
|
||||
const char *local_eid;
|
||||
bool allow_remote_configuration;
|
||||
};
|
||||
|
||||
static void callback(struct bundle_adu data, void *param, const void *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
|
||||
struct config_agent_params *const ca_param = param;
|
||||
|
||||
if (!ca_param->allow_remote_configuration) {
|
||||
char *const node_id = get_node_id(data.source);
|
||||
|
||||
if (!node_id || strncmp(ca_param->local_eid, node_id,
|
||||
strlen(ca_param->local_eid)) != 0) {
|
||||
LOGF_WARN(
|
||||
"ConfigAgent: Dropped config message from foreign endpoint \"%s\"",
|
||||
data.source
|
||||
);
|
||||
free(node_id);
|
||||
return;
|
||||
}
|
||||
free(node_id);
|
||||
}
|
||||
|
||||
config_parser_reset(&parser);
|
||||
config_parser_read(
|
||||
&parser,
|
||||
data.payload,
|
||||
data.length
|
||||
);
|
||||
bundle_adu_free_members(data);
|
||||
}
|
||||
|
||||
int config_agent_setup(
|
||||
QueueIdentifier_t bundle_processor_signaling_queue,
|
||||
const char *local_eid,
|
||||
bool allow_remote_configuration,
|
||||
void *bundle_processor_context)
|
||||
{
|
||||
const int is_ipn = get_eid_scheme(local_eid) == EID_SCHEME_IPN;
|
||||
|
||||
ASSERT(config_parser_init(
|
||||
&parser,
|
||||
&bundle_processor_handle_router_command,
|
||||
bundle_processor_context
|
||||
));
|
||||
|
||||
struct config_agent_params *const ca_param = malloc(
|
||||
sizeof(struct config_agent_params)
|
||||
);
|
||||
|
||||
ca_param->local_eid = local_eid;
|
||||
ca_param->allow_remote_configuration = allow_remote_configuration;
|
||||
|
||||
const struct agent agent = {
|
||||
.auth_trx = true,
|
||||
.is_subscriber = true,
|
||||
.sink_identifier = (
|
||||
is_ipn
|
||||
? AGENT_ID_CONFIG_IPN
|
||||
: AGENT_ID_CONFIG_DTN
|
||||
),
|
||||
.trx_callback = callback,
|
||||
.param = ca_param,
|
||||
};
|
||||
const int rv = bundle_processor_perform_agent_action_async(
|
||||
bundle_processor_signaling_queue,
|
||||
BP_SIGNAL_AGENT_REGISTER,
|
||||
agent
|
||||
);
|
||||
|
||||
if (rv) {
|
||||
free(ca_param);
|
||||
config_parser_reset(&parser);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#include "agents/config_parser.h"
|
||||
#include "routing/compat/config_parser.h"
|
||||
#include "routing/compat/node.h"
|
||||
#include "routing/compat/router.h"
|
||||
|
||||
#include "platform/hal_io.h"
|
||||
|
||||
#include "ud3tn/eid.h"
|
||||
#include "ud3tn/node.h"
|
||||
#include "ud3tn/router.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
|
@ -33,8 +33,6 @@ static const uint8_t DEFAULT_EID_BUFFER_SIZE = 16;
|
|||
static const uint8_t DEFAULT_CLA_ADDR_BUFFER_SIZE = 21;
|
||||
static const uint8_t DEFAULT_INT_BUFFER_SIZE = 16;
|
||||
|
||||
static void send_router_command(struct config_parser *parser);
|
||||
|
||||
static void begin_read_data_eid(
|
||||
struct config_parser *parser, struct endpoint_list **target)
|
||||
{
|
||||
|
|
@ -473,9 +471,6 @@ static void config_parser_read_byte(struct config_parser *parser, uint8_t byte)
|
|||
} else {
|
||||
read_command(parser, *(char *)(&byte));
|
||||
}
|
||||
|
||||
if (parser->basedata->status == PARSER_STATUS_DONE)
|
||||
send_router_command(parser);
|
||||
}
|
||||
|
||||
size_t config_parser_read(struct config_parser *parser,
|
||||
|
|
@ -484,7 +479,6 @@ size_t config_parser_read(struct config_parser *parser,
|
|||
size_t i = 0;
|
||||
|
||||
while (i < length) {
|
||||
config_parser_read_byte(parser, buffer[i]);
|
||||
if (parser->basedata->status != PARSER_STATUS_GOOD &&
|
||||
parser->basedata->status != PARSER_STATUS_DONE) {
|
||||
LOGF_WARN(
|
||||
|
|
@ -495,7 +489,10 @@ size_t config_parser_read(struct config_parser *parser,
|
|||
config_parser_reset(parser);
|
||||
return length;
|
||||
}
|
||||
config_parser_read_byte(parser, buffer[i]);
|
||||
i++;
|
||||
if (parser->basedata->status == PARSER_STATUS_DONE)
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
|
|
@ -533,31 +530,14 @@ enum ud3tn_result config_parser_reset(struct config_parser *parser)
|
|||
return UD3TN_OK;
|
||||
}
|
||||
|
||||
struct parser *config_parser_init(
|
||||
struct config_parser *parser,
|
||||
void (*send_callback)(void *, struct router_command *), void *param)
|
||||
struct parser *config_parser_init(struct config_parser *parser)
|
||||
{
|
||||
parser->basedata = malloc(sizeof(struct parser));
|
||||
if (parser->basedata == NULL)
|
||||
return NULL;
|
||||
parser->send_callback = send_callback;
|
||||
parser->send_param = param;
|
||||
parser->basedata->status = PARSER_STATUS_ERROR;
|
||||
parser->router_command = NULL;
|
||||
if (config_parser_reset(parser) != UD3TN_OK)
|
||||
return NULL;
|
||||
return parser->basedata;
|
||||
}
|
||||
|
||||
static void send_router_command(struct config_parser *parser)
|
||||
{
|
||||
struct router_command *ptr;
|
||||
|
||||
if (parser->send_callback == NULL)
|
||||
return;
|
||||
ptr = parser->router_command;
|
||||
/* Unset router cmd, the recipient has to take care of it... */
|
||||
parser->router_command = NULL;
|
||||
parser->send_callback(parser->send_param, ptr);
|
||||
/* Don't reset the parser here as the input task must know the state */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#include "routing/compat/contact_manager.h"
|
||||
#include "routing/compat/node.h"
|
||||
#include "routing/compat/routing_table.h"
|
||||
|
||||
#include "ud3tn/common.h"
|
||||
#include "ud3tn/contact_manager.h"
|
||||
#include "ud3tn/node.h"
|
||||
#include "ud3tn/routing_table.h"
|
||||
|
||||
#include "cla/cla.h"
|
||||
#include "cla/cla_contact_tx_task.h"
|
||||
|
|
@ -28,9 +29,9 @@ struct contact_manager_task_parameters {
|
|||
};
|
||||
|
||||
struct contact_info {
|
||||
struct contact *contact;
|
||||
char *eid;
|
||||
char *cla_addr;
|
||||
uint64_t to_ms;
|
||||
};
|
||||
|
||||
struct contact_manager_context {
|
||||
|
|
@ -46,7 +47,9 @@ static bool contact_active(
|
|||
int8_t i;
|
||||
|
||||
for (i = 0; i < ctx->current_contact_count; i++) {
|
||||
if (ctx->current_contacts[i].contact == contact)
|
||||
if (!strcmp(ctx->current_contacts[i].eid, contact->node->eid) &&
|
||||
ctx->current_contacts[i].to_ms == contact->to_ms
|
||||
)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -61,11 +64,8 @@ static int8_t remove_expired_contacts(
|
|||
int8_t i, c, removed = 0;
|
||||
|
||||
for (i = ctx->current_contact_count - 1; i >= 0; i--) {
|
||||
if (ctx->current_contacts[i].contact->to_ms <=
|
||||
current_timestamp_ms) {
|
||||
if (ctx->current_contacts[i].to_ms <= current_timestamp_ms) {
|
||||
ASSERT(i <= MAX_CONCURRENT_CONTACTS);
|
||||
/* Unset "active" constraint */
|
||||
ctx->current_contacts[i].contact->active = 0;
|
||||
/* The TX task takes care of re-scheduling */
|
||||
list[removed++] = ctx->current_contacts[i];
|
||||
/* If it's not the last element, we have to move mem */
|
||||
|
|
@ -97,26 +97,23 @@ static uint8_t check_upcoming(
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Set "active" constraint, "blocking" the contact */
|
||||
c->active = 1;
|
||||
|
||||
/* Add contact */
|
||||
ctx->current_contacts[ctx->current_contact_count].contact = c;
|
||||
ctx->current_contacts[ctx->current_contact_count].eid = strdup(
|
||||
c->node->eid
|
||||
);
|
||||
if (!ctx->current_contacts[ctx->current_contact_count].eid) {
|
||||
LOG_ERROR("ContactManager: Failed to copy EID");
|
||||
LOG_WARN("ContactManager: Failed to copy EID");
|
||||
return 0;
|
||||
}
|
||||
ctx->current_contacts[ctx->current_contact_count].cla_addr = strdup(
|
||||
c->node->cla_addr
|
||||
);
|
||||
if (!ctx->current_contacts[ctx->current_contact_count].cla_addr) {
|
||||
LOG_ERROR("ContactManager: Failed to copy CLA address");
|
||||
LOG_WARN("ContactManager: Failed to copy CLA address");
|
||||
free(ctx->current_contacts[ctx->current_contact_count].eid);
|
||||
return 0;
|
||||
}
|
||||
ctx->current_contacts[ctx->current_contact_count].to_ms = c->to_ms;
|
||||
list[index] = ctx->current_contacts[ctx->current_contact_count];
|
||||
ctx->current_contact_count++;
|
||||
|
||||
|
|
@ -162,121 +159,20 @@ static int8_t process_upcoming_list(
|
|||
return added;
|
||||
}
|
||||
|
||||
static int hand_over_contact_bundles(
|
||||
struct contact_manager_context *const ctx, Semaphore_t semphr, int8_t i)
|
||||
static void check_for_contacts(
|
||||
struct contact_manager_context *const ctx,
|
||||
struct contact_list **contact_list,
|
||||
Semaphore_t semphr, QueueIdentifier_t bp_queue)
|
||||
{
|
||||
struct contact_info cinfo = ctx->current_contacts[i];
|
||||
struct contact_info added_contacts[MAX_CONCURRENT_CONTACTS];
|
||||
struct contact_info removed_contacts[MAX_CONCURRENT_CONTACTS];
|
||||
const uint64_t current_timestamp_ms = hal_time_get_timestamp_ms();
|
||||
|
||||
ASSERT(semphr != NULL);
|
||||
ASSERT(bp_queue != NULL);
|
||||
|
||||
hal_semaphore_take_blocking(semphr);
|
||||
|
||||
// NOTE: cinfo.contact MAY not be valid at this point!
|
||||
struct node_table_entry *n = routing_table_lookup_eid(cinfo.eid);
|
||||
struct contact_list *cl = (n != NULL) ? n->contacts : NULL;
|
||||
bool found = false;
|
||||
|
||||
while (cl) {
|
||||
if (cl->data == cinfo.contact) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
cl = cl->next;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
LOGF_WARN(
|
||||
"ContactManager: Could not find contact %p to \"%s\" via \"%s\", discarding record",
|
||||
cinfo.contact,
|
||||
cinfo.eid,
|
||||
cinfo.cla_addr
|
||||
);
|
||||
// Remove invalid contact info
|
||||
free(cinfo.eid);
|
||||
free(cinfo.cla_addr);
|
||||
if (i < ctx->current_contact_count - 1) {
|
||||
memmove(
|
||||
&ctx->current_contacts[i],
|
||||
&ctx->current_contacts[i + 1],
|
||||
sizeof(struct contact_info) * (
|
||||
ctx->current_contact_count - 1 - i
|
||||
)
|
||||
);
|
||||
}
|
||||
ctx->current_contact_count--;
|
||||
hal_semaphore_release(semphr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Contact found and valid -> continue!
|
||||
if (cinfo.contact->contact_bundles == NULL) {
|
||||
hal_semaphore_release(semphr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ASSERT(cinfo.cla_addr != NULL);
|
||||
// Try to obtain a handler
|
||||
struct cla_config *cla_config = cla_config_get(cinfo.cla_addr);
|
||||
|
||||
if (!cla_config) {
|
||||
LOGF_WARN(
|
||||
"ContactManager: Could not obtain CLA for address \"%s\"",
|
||||
cinfo.cla_addr
|
||||
);
|
||||
hal_semaphore_release(semphr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct cla_tx_queue tx_queue = cla_config->vtable->cla_get_tx_queue(
|
||||
cla_config,
|
||||
cinfo.eid,
|
||||
cinfo.cla_addr
|
||||
);
|
||||
|
||||
if (!tx_queue.tx_queue_handle) {
|
||||
LOGF_WARN(
|
||||
"ContactManager: Could not obtain queue for TX to \"%s\" via \"%s\"",
|
||||
cinfo.eid,
|
||||
cinfo.cla_addr
|
||||
);
|
||||
// Re-scheduling will be done by routerTask or transmission will
|
||||
// occur after signal of new connection.
|
||||
hal_semaphore_release(semphr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOGF_INFO(
|
||||
"ContactManager: Queuing bundles for contact with \"%s\".",
|
||||
cinfo.eid
|
||||
);
|
||||
|
||||
struct cla_contact_tx_task_command command = {
|
||||
.type = TX_COMMAND_BUNDLES,
|
||||
// Take over the bundles as we can now push them into the queue
|
||||
// that is protected by the CLA semaphore.
|
||||
.bundles = cinfo.contact->contact_bundles,
|
||||
};
|
||||
|
||||
// Ensure the Router does not interfere. We own the list now and the
|
||||
// TX task will free it.
|
||||
cinfo.contact->contact_bundles = NULL;
|
||||
// Now we can also let the BP do its thing again...
|
||||
hal_semaphore_release(semphr);
|
||||
// NOTE: From now on, cinfo.contact MAY become invalid again!
|
||||
|
||||
command.cla_address = strdup(cinfo.cla_addr);
|
||||
hal_queue_push_to_back(tx_queue.tx_queue_handle, &command);
|
||||
hal_semaphore_release(tx_queue.tx_queue_sem); // taken by get_tx_queue
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint8_t check_for_contacts(
|
||||
struct contact_manager_context *const ctx,
|
||||
struct contact_list *contact_list,
|
||||
struct contact_info removed_contacts[])
|
||||
{
|
||||
int8_t i;
|
||||
static struct contact_info added_contacts[MAX_CONCURRENT_CONTACTS];
|
||||
const uint64_t current_timestamp_ms = hal_time_get_timestamp_ms();
|
||||
const int8_t removed_count = remove_expired_contacts(
|
||||
ctx,
|
||||
current_timestamp_ms,
|
||||
|
|
@ -284,102 +180,70 @@ static uint8_t check_for_contacts(
|
|||
);
|
||||
const int8_t added_count = process_upcoming_list(
|
||||
ctx,
|
||||
contact_list,
|
||||
*contact_list, // dereference only when we have the semaphore!
|
||||
current_timestamp_ms,
|
||||
added_contacts
|
||||
);
|
||||
|
||||
hal_semaphore_release(semphr);
|
||||
ASSERT(ctx->next_contact_time_ms > current_timestamp_ms);
|
||||
|
||||
for (i = 0; i < added_count; i++) {
|
||||
for (int8_t i = 0; i < added_count; i++) {
|
||||
LOGF_INFO(
|
||||
"ContactManager: Scheduled contact with \"%s\" started (%p).",
|
||||
added_contacts[i].eid,
|
||||
added_contacts[i].contact
|
||||
"ContactManager: Scheduled contact with \"%s\" started.",
|
||||
added_contacts[i].eid
|
||||
);
|
||||
|
||||
struct cla_config *cla_config = cla_config_get(
|
||||
added_contacts[i].cla_addr
|
||||
struct fib_association *req = malloc(
|
||||
sizeof(struct fib_association)
|
||||
);
|
||||
|
||||
if (!cla_config) {
|
||||
LOGF_WARN(
|
||||
"ContactManager: Could not obtain CLA for address \"%s\"",
|
||||
added_contacts[i].cla_addr
|
||||
);
|
||||
} else {
|
||||
cla_config->vtable->cla_start_scheduled_contact(
|
||||
cla_config,
|
||||
added_contacts[i].eid,
|
||||
added_contacts[i].cla_addr
|
||||
);
|
||||
}
|
||||
if (!req)
|
||||
break;
|
||||
|
||||
req->status = FIB_LINK_STATUS_ACTIVE;
|
||||
req->flags = FIB_LINK_FLAG_NONE;
|
||||
req->node_id = strdup(added_contacts[i].eid);
|
||||
req->cla_addr = strdup(added_contacts[i].cla_addr);
|
||||
|
||||
bundle_processor_inform(
|
||||
bp_queue,
|
||||
(struct bundle_processor_signal){
|
||||
.type = BP_SIGNAL_FIB_UPDATE_REQUEST,
|
||||
.fib_request = req,
|
||||
}
|
||||
);
|
||||
|
||||
// cppcheck-suppress memleak
|
||||
}
|
||||
for (i = 0; i < removed_count; i++) {
|
||||
for (int8_t i = 0; i < removed_count; i++) {
|
||||
LOGF_INFO(
|
||||
"ContactManager: Scheduled contact with \"%s\" ended (%p).",
|
||||
removed_contacts[i].eid,
|
||||
removed_contacts[i].contact
|
||||
"ContactManager: Scheduled contact with \"%s\" ended.",
|
||||
removed_contacts[i].eid
|
||||
);
|
||||
|
||||
struct cla_config *cla_config = cla_config_get(
|
||||
removed_contacts[i].cla_addr
|
||||
struct fib_association *req = malloc(
|
||||
sizeof(struct fib_association)
|
||||
);
|
||||
|
||||
if (!cla_config) {
|
||||
LOGF_WARN(
|
||||
"ContactManager: Could not obtain CLA for address \"%s\"",
|
||||
removed_contacts[i].cla_addr
|
||||
);
|
||||
} else {
|
||||
cla_config->vtable->cla_end_scheduled_contact(
|
||||
cla_config,
|
||||
removed_contacts[i].eid,
|
||||
removed_contacts[i].cla_addr
|
||||
);
|
||||
}
|
||||
free(removed_contacts[i].eid);
|
||||
free(removed_contacts[i].cla_addr);
|
||||
}
|
||||
return removed_count;
|
||||
}
|
||||
if (!req)
|
||||
break;
|
||||
|
||||
/* We assume that contact_list will not change. */
|
||||
static void manage_contacts(
|
||||
struct contact_manager_context *const ctx,
|
||||
struct contact_list **contact_list, enum contact_manager_signal signal,
|
||||
Semaphore_t semphr, QueueIdentifier_t bp_queue)
|
||||
{
|
||||
struct contact_info removed_list[MAX_CONCURRENT_CONTACTS];
|
||||
int8_t removed, i;
|
||||
req->status = FIB_LINK_STATUS_TEARDOWN;
|
||||
req->flags = FIB_LINK_FLAG_NONE;
|
||||
req->node_id = removed_contacts[i].eid;
|
||||
req->cla_addr = removed_contacts[i].cla_addr;
|
||||
|
||||
ASSERT(semphr != NULL);
|
||||
ASSERT(bp_queue != NULL);
|
||||
bundle_processor_inform(
|
||||
bp_queue,
|
||||
(struct bundle_processor_signal){
|
||||
.type = BP_SIGNAL_FIB_UPDATE_REQUEST,
|
||||
.fib_request = req,
|
||||
}
|
||||
);
|
||||
|
||||
// NOTE: CM_SIGNAL_UNKNOWN has both flags
|
||||
if (HAS_FLAG(signal, CM_SIGNAL_UPDATE_CONTACT_LIST)) {
|
||||
hal_semaphore_take_blocking(semphr);
|
||||
removed = check_for_contacts(ctx, *contact_list, removed_list);
|
||||
hal_semaphore_release(semphr);
|
||||
for (i = 0; i < removed; i++) {
|
||||
/* The contact has to be deleted first... */
|
||||
bundle_processor_inform(
|
||||
bp_queue,
|
||||
(struct bundle_processor_signal) {
|
||||
.type = BP_SIGNAL_CONTACT_OVER,
|
||||
.contact = removed_list[i].contact,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: CM_SIGNAL_UNKNOWN has both flags
|
||||
if (HAS_FLAG(signal, CM_SIGNAL_PROCESS_CURRENT_BUNDLES)) {
|
||||
for (int8_t i = 0; i < ctx->current_contact_count; ) {
|
||||
// NOTE this may either return 1 or 0, the latter if it
|
||||
// deleted an item & modified ctx->current_contact_count
|
||||
i += hand_over_contact_bundles(ctx, semphr, i);
|
||||
}
|
||||
removed_contacts[i].eid = NULL;
|
||||
removed_contacts[i].cla_addr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -387,7 +251,7 @@ static void contact_manager_task(void *cm_parameters)
|
|||
{
|
||||
struct contact_manager_task_parameters *parameters =
|
||||
(struct contact_manager_task_parameters *)cm_parameters;
|
||||
enum contact_manager_signal signal = CM_SIGNAL_NONE;
|
||||
int signal;
|
||||
uint64_t cur_time_ms, next_time_ms;
|
||||
int64_t delay_ms;
|
||||
struct contact_manager_context ctx = {
|
||||
|
|
@ -400,16 +264,12 @@ static void contact_manager_task(void *cm_parameters)
|
|||
abort();
|
||||
}
|
||||
for (;;) {
|
||||
if (signal != CM_SIGNAL_NONE) {
|
||||
manage_contacts(
|
||||
&ctx,
|
||||
parameters->contact_list_ptr,
|
||||
signal,
|
||||
parameters->semaphore,
|
||||
parameters->bp_queue
|
||||
);
|
||||
}
|
||||
signal = CM_SIGNAL_UNKNOWN;
|
||||
check_for_contacts(
|
||||
&ctx,
|
||||
parameters->contact_list_ptr,
|
||||
parameters->semaphore,
|
||||
parameters->bp_queue
|
||||
);
|
||||
cur_time_ms = hal_time_get_timestamp_ms();
|
||||
delay_ms = -1; // infinite blocking on queue
|
||||
if (ctx.next_contact_time_ms < UINT64_MAX) {
|
||||
|
|
@ -450,7 +310,7 @@ struct contact_manager_params contact_manager_start(
|
|||
if (semaphore == NULL)
|
||||
return ret;
|
||||
hal_semaphore_release(semaphore);
|
||||
queue = hal_queue_create(1, sizeof(enum contact_manager_signal));
|
||||
queue = hal_queue_create(1, sizeof(int));
|
||||
if (queue == NULL) {
|
||||
hal_semaphore_delete(semaphore);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#include "routing/compat/node.h"
|
||||
|
||||
#include "ud3tn/common.h"
|
||||
#include "ud3tn/node.h"
|
||||
#include "ud3tn/result.h"
|
||||
|
||||
#include "platform/hal_time.h"
|
||||
|
|
@ -51,9 +52,9 @@ struct contact *contact_create(struct node *node)
|
|||
ret->remaining_capacity_p0 = 0;
|
||||
ret->remaining_capacity_p1 = 0;
|
||||
ret->remaining_capacity_p2 = 0;
|
||||
ret->link_active = false;
|
||||
ret->contact_endpoints = NULL;
|
||||
ret->contact_bundles = NULL;
|
||||
ret->active = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +66,6 @@ static void free_contact_internal(
|
|||
|
||||
if (contact == NULL)
|
||||
return;
|
||||
ASSERT(contact->active == 0);
|
||||
if (free_eid_list) {
|
||||
cur_eid = contact->contact_endpoints;
|
||||
while (cur_eid != NULL)
|
||||
|
|
@ -419,10 +419,6 @@ struct contact_list *contact_list_difference(
|
|||
*cur_slot = l->next;
|
||||
l->next = *deleted;
|
||||
*deleted = l;
|
||||
} else if ((*cur_slot)->data->active) {
|
||||
l = *cur_slot;
|
||||
*cur_slot = l->next;
|
||||
free(l);
|
||||
} else {
|
||||
*cur_slot = contact_list_free_internal(
|
||||
*cur_slot,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#include "routing/compat/node.h"
|
||||
#include "routing/compat/router.h"
|
||||
#include "routing/compat/routing_table.h"
|
||||
|
||||
#include "ud3tn/bundle.h"
|
||||
#include "ud3tn/common.h"
|
||||
#include "ud3tn/eid.h"
|
||||
#include "ud3tn/node.h"
|
||||
#include "ud3tn/router.h"
|
||||
#include "ud3tn/routing_table.h"
|
||||
|
||||
#include "cla/cla.h"
|
||||
|
||||
|
|
@ -18,7 +19,6 @@
|
|||
static struct router_config RC = {
|
||||
.global_mbs = ROUTER_GLOBAL_MBS,
|
||||
.fragment_min_payload = FRAGMENT_MIN_PAYLOAD,
|
||||
.router_min_contacts_htab = ROUTER_MIN_CONTACTS_HTAB,
|
||||
};
|
||||
|
||||
struct router_config router_get_config(void)
|
||||
|
|
@ -61,16 +61,16 @@ struct contact_list *router_lookup_destination(char *const dest)
|
|||
|
||||
static inline struct max_fragment_size_result {
|
||||
uint32_t max_fragment_size;
|
||||
uint32_t payload_capacity;
|
||||
uint64_t payload_capacity;
|
||||
} router_get_max_reasonable_fragment_size(
|
||||
struct contact_list *contacts, uint32_t full_size,
|
||||
uint32_t max_fragment_min_size, uint32_t payload_size,
|
||||
struct contact_list *contacts, uint64_t full_size,
|
||||
uint64_t max_fragment_min_size, uint64_t payload_size,
|
||||
enum bundle_routing_priority priority, uint64_t exp_time)
|
||||
{
|
||||
uint32_t payload_capacity = 0;
|
||||
uint64_t payload_capacity = 0;
|
||||
uint32_t max_frag_size = UINT32_MAX;
|
||||
uint32_t min_capacity, c_capacity;
|
||||
int32_t c_pay_capacity;
|
||||
uint64_t min_capacity, c_capacity;
|
||||
int64_t c_pay_capacity;
|
||||
struct contact *c;
|
||||
|
||||
(void)exp_time;
|
||||
|
|
@ -81,7 +81,22 @@ static inline struct max_fragment_size_result {
|
|||
contacts = contacts->next;
|
||||
//if (c->to_s > exp_time)
|
||||
// break;
|
||||
c_capacity = ROUTER_CONTACT_CAPACITY(c, priority);
|
||||
const int32_t rccap = ROUTER_CONTACT_CAPACITY(c, priority);
|
||||
|
||||
// The return value of `contact_get_remaining_capacity_bytes`
|
||||
// is `INT32_MAX` is we assume an "infinite capacity" contact
|
||||
// As this determines the 64-bit payload capacity below and we
|
||||
// can use larger values here, we set the capacity value to
|
||||
// UINT64_MAX in this case.
|
||||
if (rccap == INT32_MAX)
|
||||
c_capacity = UINT64_MAX;
|
||||
else if (rccap < 0)
|
||||
c_capacity = 0;
|
||||
else
|
||||
c_capacity = rccap;
|
||||
// If size_t is smaller than uint64_t we need to cap the value.
|
||||
if (c_capacity > SIZE_MAX)
|
||||
c_capacity = SIZE_MAX;
|
||||
if (c_capacity < min_capacity)
|
||||
continue;
|
||||
|
||||
|
|
@ -93,12 +108,20 @@ static inline struct max_fragment_size_result {
|
|||
if (!cla_config)
|
||||
continue;
|
||||
|
||||
const size_t c_mbs = MIN(
|
||||
MIN(
|
||||
(size_t)c_capacity,
|
||||
cla_config->vtable->cla_mbs_get(cla_config)
|
||||
),
|
||||
RC.global_mbs
|
||||
const uint64_t cla_mbs_bytes = cla_config->vtable->cla_mbs_get(
|
||||
cla_config
|
||||
);
|
||||
const uint64_t bp_mbs_bytes = RC.global_mbs;
|
||||
const uint64_t mbs_bytes = (
|
||||
(bp_mbs_bytes == 0) ? cla_mbs_bytes : (
|
||||
(cla_mbs_bytes == 0)
|
||||
? bp_mbs_bytes
|
||||
: MIN(bp_mbs_bytes, cla_mbs_bytes)
|
||||
)
|
||||
);
|
||||
const size_t c_mbs = mbs_bytes == 0 ? (size_t)c_capacity : MIN(
|
||||
(size_t)c_capacity,
|
||||
mbs_bytes
|
||||
);
|
||||
|
||||
// Contact of "infinite" capacity -> max. frag. size == MBS
|
||||
|
|
@ -122,6 +145,10 @@ static inline struct max_fragment_size_result {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (max_frag_size >= INT32_MAX)
|
||||
max_frag_size = INT32_MAX; // "infinite"
|
||||
|
||||
return (struct max_fragment_size_result){
|
||||
payload_capacity < payload_size ? 0 : max_frag_size,
|
||||
payload_capacity,
|
||||
|
|
@ -129,18 +156,19 @@ static inline struct max_fragment_size_result {
|
|||
}
|
||||
|
||||
uint8_t router_calculate_fragment_route(
|
||||
struct fragment_route *res, uint32_t size,
|
||||
struct contact_list *contacts, uint32_t preprocessed_size,
|
||||
struct fragment_route *res, uint64_t size,
|
||||
struct contact_list *contacts, uint64_t preprocessed_size,
|
||||
enum bundle_routing_priority priority, uint64_t exp_time_ms,
|
||||
struct contact **excluded_contacts, uint8_t excluded_contacts_count)
|
||||
{
|
||||
const uint64_t time_ms = hal_time_get_timestamp_ms();
|
||||
uint32_t cap;
|
||||
uint64_t cap;
|
||||
uint8_t d, i;
|
||||
struct contact *c;
|
||||
|
||||
(void)priority;
|
||||
|
||||
res->contact = NULL;
|
||||
res->preemption_improved = 0;
|
||||
while (contacts != NULL) {
|
||||
c = contacts->data;
|
||||
contacts = contacts->next;
|
||||
|
|
@ -155,6 +183,9 @@ uint8_t router_calculate_fragment_route(
|
|||
if (c->to_ms <= time_ms)
|
||||
continue;
|
||||
cap = ROUTER_CONTACT_CAPACITY(c, 0);
|
||||
// "infinite" capacity contact
|
||||
if (cap == INT32_MAX)
|
||||
cap = UINT64_MAX;
|
||||
if (preprocessed_size != 0) {
|
||||
if (preprocessed_size >= cap) {
|
||||
preprocessed_size -= cap;
|
||||
|
|
@ -165,9 +196,6 @@ uint8_t router_calculate_fragment_route(
|
|||
}
|
||||
}
|
||||
if (cap < size) {
|
||||
if ((ROUTER_CONTACT_CAPACITY(c, priority)
|
||||
- preprocessed_size) >= size)
|
||||
res->preemption_improved++;
|
||||
preprocessed_size = 0;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -179,7 +207,7 @@ uint8_t router_calculate_fragment_route(
|
|||
|
||||
static inline void router_get_first_route_nonfrag(
|
||||
struct router_result *res, struct contact_list *contacts,
|
||||
struct bundle *bundle, uint32_t bundle_size,
|
||||
struct bundle *bundle, uint64_t bundle_size,
|
||||
uint64_t expiration_time_ms)
|
||||
{
|
||||
res->fragment_results[0].payload_size = bundle->payload_block->length;
|
||||
|
|
@ -188,28 +216,26 @@ static inline void router_get_first_route_nonfrag(
|
|||
&res->fragment_results[0], bundle_size,
|
||||
contacts, 0, ROUTER_BUNDLE_PRIORITY(bundle), expiration_time_ms,
|
||||
NULL, 0)
|
||||
) {
|
||||
)
|
||||
res->fragments = 1;
|
||||
res->preemption_improved = res->fragment_results[0].preemption_improved;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void router_get_first_route_frag(
|
||||
struct router_result *res, struct contact_list *contacts,
|
||||
struct bundle *bundle, uint32_t bundle_size,
|
||||
struct bundle *bundle, uint64_t bundle_size,
|
||||
uint64_t expiration_time_ms,
|
||||
uint32_t max_frag_sz, uint32_t first_frag_sz, uint32_t last_frag_sz)
|
||||
uint64_t max_frag_sz, uint64_t first_frag_sz, uint64_t last_frag_sz)
|
||||
{
|
||||
uint32_t mid_frag_sz, next_frag_sz, remaining_pay, processed_sz;
|
||||
int32_t min_pay, max_pay;
|
||||
int32_t success, index;
|
||||
uint64_t mid_frag_sz, next_frag_sz, remaining_pay, processed_sz;
|
||||
int64_t min_pay, max_pay;
|
||||
int64_t success, index;
|
||||
|
||||
/* Determine fragment minimum sizes */
|
||||
mid_frag_sz = bundle_get_mid_fragment_min_size(bundle);
|
||||
next_frag_sz = first_frag_sz;
|
||||
if (next_frag_sz > max_frag_sz || last_frag_sz > max_frag_sz) {
|
||||
LOGF_INFO(
|
||||
"Router: Cannot fragment because max. frag. size of %lu bytes is smaller than bundle headers (first = %lu, mid = %lu, last = %lu)",
|
||||
LOGF_DEBUG(
|
||||
"Router: Cannot fragment because max. frag. size of %llu bytes is smaller than bundle headers (first = %llu, mid = %llu, last = %llu)",
|
||||
max_frag_sz,
|
||||
next_frag_sz,
|
||||
mid_frag_sz,
|
||||
|
|
@ -223,8 +249,8 @@ static inline void router_get_first_route_frag(
|
|||
min_pay = MIN(remaining_pay, RC.fragment_min_payload);
|
||||
max_pay = max_frag_sz - next_frag_sz;
|
||||
if (max_pay < min_pay) {
|
||||
LOGF_INFO(
|
||||
"Router: Cannot fragment because minimum amount of payload (%lu bytes) will not fit in fragment with maximum payload size of %lu bytes",
|
||||
LOGF_DEBUG(
|
||||
"Router: Cannot fragment because minimum amount of payload (%llu bytes) will not fit in fragment with maximum payload size of %llu bytes",
|
||||
min_pay,
|
||||
max_pay
|
||||
);
|
||||
|
|
@ -236,7 +262,7 @@ static inline void router_get_first_route_frag(
|
|||
remaining_pay = 0;
|
||||
} else {
|
||||
/* Another fragment */
|
||||
max_pay = MIN((int32_t)remaining_pay, max_pay);
|
||||
max_pay = MIN((int64_t)remaining_pay, max_pay);
|
||||
res->fragment_results[res->fragments++].payload_size = max_pay;
|
||||
remaining_pay -= max_pay;
|
||||
next_frag_sz = mid_frag_sz;
|
||||
|
|
@ -249,7 +275,6 @@ static inline void router_get_first_route_frag(
|
|||
|
||||
/* Determine routes */
|
||||
success = 0;
|
||||
res->preemption_improved = 0;
|
||||
processed_sz = 0;
|
||||
for (index = 0; index < res->fragments; index++) {
|
||||
bundle_size = res->fragment_results[index].payload_size;
|
||||
|
|
@ -263,28 +288,25 @@ static inline void router_get_first_route_frag(
|
|||
&res->fragment_results[index], bundle_size,
|
||||
contacts, processed_sz, ROUTER_BUNDLE_PRIORITY(bundle),
|
||||
expiration_time_ms, NULL, 0);
|
||||
res->preemption_improved +=
|
||||
res->fragment_results[index].preemption_improved;
|
||||
processed_sz += bundle_size;
|
||||
}
|
||||
if (success != res->fragments)
|
||||
res->fragments = 0;
|
||||
}
|
||||
|
||||
/* max. ~200 bytes on stack */
|
||||
struct router_result router_get_first_route(struct bundle *bundle)
|
||||
{
|
||||
const uint64_t expiration_time_ms = bundle_get_expiration_time_ms(
|
||||
bundle
|
||||
);
|
||||
struct router_result res;
|
||||
struct contact_list *contacts =
|
||||
router_lookup_destination(bundle->destination);
|
||||
struct contact_list *contacts = router_lookup_destination(
|
||||
bundle->destination
|
||||
);
|
||||
|
||||
res.fragments = 0;
|
||||
res.preemption_improved = 0;
|
||||
if (contacts == NULL) {
|
||||
LOGF_INFO(
|
||||
LOGF_DEBUG(
|
||||
"Router: Could not determine a node over which the destination \"%s\" for bundle %p is reachable",
|
||||
bundle->destination,
|
||||
bundle
|
||||
|
|
@ -292,11 +314,11 @@ struct router_result router_get_first_route(struct bundle *bundle)
|
|||
return res;
|
||||
}
|
||||
|
||||
const uint32_t bundle_size = bundle_get_serialized_size(bundle);
|
||||
const uint32_t first_frag_sz = bundle_get_first_fragment_min_size(
|
||||
const uint64_t bundle_size = bundle_get_serialized_size(bundle);
|
||||
const uint64_t first_frag_sz = bundle_get_first_fragment_min_size(
|
||||
bundle
|
||||
);
|
||||
const uint32_t last_frag_sz = bundle_get_last_fragment_min_size(bundle);
|
||||
const uint64_t last_frag_sz = bundle_get_last_fragment_min_size(bundle);
|
||||
|
||||
const struct max_fragment_size_result mrfs =
|
||||
router_get_max_reasonable_fragment_size(
|
||||
|
|
@ -310,7 +332,7 @@ struct router_result router_get_first_route(struct bundle *bundle)
|
|||
|
||||
if (mrfs.max_fragment_size == 0) {
|
||||
LOGF_DEBUG(
|
||||
"Router: Contact payload capacity (%lu bytes) too low for bundle %p of size %lu bytes (min. frag. sz. = %lu, payload sz. = %lu)",
|
||||
"Router: Contact payload capacity (%llu bytes) too low for bundle %p of size %llu bytes (min. frag. sz. = %llu, payload sz. = %llu)",
|
||||
mrfs.payload_capacity,
|
||||
bundle,
|
||||
bundle_size,
|
||||
|
|
@ -320,7 +342,7 @@ struct router_result router_get_first_route(struct bundle *bundle)
|
|||
goto finish;
|
||||
} else if (mrfs.max_fragment_size != INT32_MAX) {
|
||||
LOGF_DEBUG(
|
||||
"Router: Determined max. frag size of %lu bytes for bundle %p of size %lu bytes (payload sz. = %lu)",
|
||||
"Router: Determined max. frag size of %lu bytes for bundle %p of size %llu bytes (payload sz. = %llu)",
|
||||
mrfs.max_fragment_size,
|
||||
bundle,
|
||||
bundle_size,
|
||||
|
|
@ -328,7 +350,7 @@ struct router_result router_get_first_route(struct bundle *bundle)
|
|||
);
|
||||
} else {
|
||||
LOGF_DEBUG(
|
||||
"Router: Determined infinite max. frag size for bundle of size %lu bytes (payload sz. = %lu)",
|
||||
"Router: Determined infinite max. frag size for bundle of size %llu bytes (payload sz. = %llu)",
|
||||
bundle_size,
|
||||
bundle->payload_block->length
|
||||
);
|
||||
|
|
@ -344,8 +366,8 @@ struct router_result router_get_first_route(struct bundle *bundle)
|
|||
mrfs.max_fragment_size, first_frag_sz, last_frag_sz);
|
||||
|
||||
if (!res.fragments)
|
||||
LOGF_INFO(
|
||||
"Router: No feasible route found for bundle %p to \"%s\" with size of %lu bytes",
|
||||
LOGF_DEBUG(
|
||||
"Router: No feasible route found for bundle %p to \"%s\" with size of %llu bytes",
|
||||
bundle,
|
||||
bundle->destination,
|
||||
bundle_size
|
||||
|
|
@ -361,69 +383,125 @@ finish:
|
|||
return res;
|
||||
}
|
||||
|
||||
/* For use with caching of routes */
|
||||
struct router_result router_try_reuse(
|
||||
struct router_result route, struct bundle *bundle)
|
||||
static size_t get_serialized_size_frag(
|
||||
struct bundle *b,
|
||||
uint64_t frag_offset, uint64_t frag_length)
|
||||
{
|
||||
uint64_t header_size;
|
||||
|
||||
// Would be a bug - fragment would end after end of bundle.
|
||||
ASSERT(frag_offset + frag_length <= b->payload_block->length);
|
||||
|
||||
if (frag_offset == 0)
|
||||
header_size = bundle_get_first_fragment_min_size(b);
|
||||
else if (frag_offset + frag_length == b->payload_block->length)
|
||||
header_size = bundle_get_last_fragment_min_size(b);
|
||||
else
|
||||
header_size = bundle_get_mid_fragment_min_size(b);
|
||||
|
||||
return header_size + frag_length;
|
||||
}
|
||||
|
||||
struct router_result router_get_resched_route(
|
||||
struct bundle *bundle,
|
||||
const uint64_t frag_offset, const uint64_t frag_length)
|
||||
{
|
||||
const uint64_t time_ms = hal_time_get_timestamp_ms();
|
||||
const uint64_t expiration_time_ms = bundle_get_expiration_time_ms(
|
||||
bundle
|
||||
);
|
||||
uint32_t remaining_pay = bundle->payload_block->length;
|
||||
uint32_t size, min_cap;
|
||||
struct fragment_route *fr;
|
||||
int32_t f;
|
||||
struct router_result res;
|
||||
struct contact_list *contacts = router_lookup_destination(
|
||||
bundle->destination
|
||||
);
|
||||
|
||||
if (route.fragments == 0)
|
||||
return route;
|
||||
|
||||
/* Not fragmented */
|
||||
if (bundle_must_not_fragment(bundle) || route.fragments == 1) {
|
||||
size = bundle_get_serialized_size(bundle);
|
||||
fr = &route.fragment_results[0];
|
||||
fr->payload_size = remaining_pay;
|
||||
if (fr->contact->to_ms <= time_ms ||
|
||||
fr->contact->to_ms > expiration_time_ms ||
|
||||
ROUTER_CONTACT_CAPACITY(fr->contact, 0) < (int32_t)size)
|
||||
route.fragments = 0;
|
||||
return route;
|
||||
res.fragments = 0;
|
||||
if (contacts == NULL) {
|
||||
LOGF_DEBUG(
|
||||
"Router: Could not determine a node over which the destination \"%s\" for re-scheduling bundle %p [%llu, %llu] is reachable",
|
||||
bundle->destination,
|
||||
bundle,
|
||||
frag_offset,
|
||||
frag_offset + frag_length
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Fragmented */
|
||||
for (f = 0; f < route.fragments; f++) {
|
||||
if (f == 0)
|
||||
size = bundle_get_first_fragment_min_size(bundle);
|
||||
else if (f == route.fragments - 1)
|
||||
size = bundle_get_last_fragment_min_size(bundle);
|
||||
else
|
||||
size = bundle_get_mid_fragment_min_size(bundle);
|
||||
fr = &route.fragment_results[f];
|
||||
min_cap = UINT32_MAX;
|
||||
if (fr->contact->to_ms <= time_ms ||
|
||||
fr->contact->to_ms > expiration_time_ms ||
|
||||
(ROUTER_CONTACT_CAPACITY(fr->contact, 0) <
|
||||
(int32_t)(size + RC.fragment_min_payload))) {
|
||||
route.fragments = 0;
|
||||
return route;
|
||||
}
|
||||
min_cap = MIN(min_cap,
|
||||
ROUTER_CONTACT_CAPACITY(fr->contact, 0)
|
||||
- size);
|
||||
fr->payload_size = MIN(remaining_pay, min_cap);
|
||||
remaining_pay -= fr->payload_size;
|
||||
if (remaining_pay == 0) {
|
||||
route.fragments = f - 1;
|
||||
return route;
|
||||
}
|
||||
const uint64_t bundle_size = get_serialized_size_frag(
|
||||
bundle,
|
||||
frag_offset,
|
||||
frag_length
|
||||
);
|
||||
|
||||
const struct max_fragment_size_result mrfs =
|
||||
router_get_max_reasonable_fragment_size(
|
||||
contacts,
|
||||
bundle_size,
|
||||
bundle_size - frag_length,
|
||||
frag_length,
|
||||
ROUTER_BUNDLE_PRIORITY(bundle),
|
||||
expiration_time_ms
|
||||
);
|
||||
|
||||
if (mrfs.max_fragment_size == 0) {
|
||||
LOGF_DEBUG(
|
||||
"Router: Contact payload capacity (%llu bytes) too low for re-scheduling bundle %p [%llu, %llu] of size %llu bytes",
|
||||
mrfs.payload_capacity,
|
||||
bundle,
|
||||
frag_offset,
|
||||
frag_offset + frag_length,
|
||||
bundle_size
|
||||
);
|
||||
goto finish;
|
||||
} else if (mrfs.max_fragment_size != INT32_MAX) {
|
||||
LOGF_DEBUG(
|
||||
"Router: Determined max. frag size of %lu bytes for re-scheduling bundle %p [%llu, %llu] of size %llu bytes",
|
||||
mrfs.max_fragment_size,
|
||||
bundle,
|
||||
frag_offset,
|
||||
frag_offset + frag_length,
|
||||
bundle_size
|
||||
);
|
||||
} else {
|
||||
LOGF_DEBUG(
|
||||
"Router: Determined infinite max. frag size for re-scheduling bundle %p [%llu, %llu] of size %llu bytes",
|
||||
bundle,
|
||||
frag_offset,
|
||||
frag_offset + frag_length,
|
||||
bundle_size
|
||||
);
|
||||
}
|
||||
|
||||
if (remaining_pay != 0)
|
||||
route.fragments = 0;
|
||||
return route;
|
||||
router_get_first_route_nonfrag(
|
||||
&res,
|
||||
contacts,
|
||||
bundle,
|
||||
bundle_size,
|
||||
expiration_time_ms
|
||||
);
|
||||
|
||||
if (!res.fragments)
|
||||
LOGF_DEBUG(
|
||||
"Router: No feasible route found for re-scheduling bundle %p [%llu, %llu] to \"%s\" with size of %llu bytes",
|
||||
bundle,
|
||||
frag_offset,
|
||||
frag_offset + frag_length,
|
||||
bundle->destination,
|
||||
bundle_size
|
||||
);
|
||||
|
||||
finish:
|
||||
while (contacts) {
|
||||
struct contact_list *const tmp = contacts->next;
|
||||
|
||||
free(contacts);
|
||||
contacts = tmp;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
enum ud3tn_result router_add_bundle_to_contact(
|
||||
struct contact *contact, struct bundle *b)
|
||||
struct contact *contact, struct bundle *b,
|
||||
uint64_t frag_offset, uint64_t frag_length)
|
||||
{
|
||||
struct routed_bundle_list *new_entry, **cur_entry;
|
||||
|
||||
|
|
@ -437,6 +515,8 @@ enum ud3tn_result router_add_bundle_to_contact(
|
|||
if (new_entry == NULL)
|
||||
return UD3TN_FAIL;
|
||||
new_entry->data = b;
|
||||
new_entry->frag_offset = frag_offset;
|
||||
new_entry->frag_length = frag_length;
|
||||
new_entry->next = NULL;
|
||||
cur_entry = &contact->contact_bundles;
|
||||
/* Go to end of list (=> FIFO) */
|
||||
|
|
@ -453,7 +533,11 @@ enum ud3tn_result router_add_bundle_to_contact(
|
|||
if (contact->remaining_capacity_p0 == INT32_MAX)
|
||||
return UD3TN_OK;
|
||||
|
||||
const size_t bundle_size = bundle_get_serialized_size(b);
|
||||
const size_t bundle_size = get_serialized_size_frag(
|
||||
b,
|
||||
frag_offset,
|
||||
frag_length
|
||||
);
|
||||
const enum bundle_routing_priority prio =
|
||||
bundle_get_routing_priority(b);
|
||||
|
||||
|
|
@ -467,7 +551,8 @@ enum ud3tn_result router_add_bundle_to_contact(
|
|||
}
|
||||
|
||||
enum ud3tn_result router_remove_bundle_from_contact(
|
||||
struct contact *contact, struct bundle *bundle)
|
||||
struct contact *contact, struct bundle *bundle,
|
||||
uint64_t frag_offset, uint64_t frag_length)
|
||||
{
|
||||
struct routed_bundle_list **cur_entry, *tmp;
|
||||
|
||||
|
|
@ -478,7 +563,10 @@ enum ud3tn_result router_remove_bundle_from_contact(
|
|||
/* Find bundle */
|
||||
while (*cur_entry != NULL) {
|
||||
ASSERT((*cur_entry)->data != NULL);
|
||||
if ((*cur_entry)->data == bundle) {
|
||||
if ((*cur_entry)->data == bundle &&
|
||||
(*cur_entry)->frag_offset == frag_offset &&
|
||||
(*cur_entry)->frag_length == frag_length
|
||||
) {
|
||||
tmp = *cur_entry;
|
||||
*cur_entry = (*cur_entry)->next;
|
||||
free(tmp);
|
||||
|
|
@ -486,8 +574,11 @@ enum ud3tn_result router_remove_bundle_from_contact(
|
|||
if (contact->remaining_capacity_p0 == INT32_MAX)
|
||||
continue;
|
||||
|
||||
const size_t bundle_size =
|
||||
bundle_get_serialized_size(bundle);
|
||||
const size_t bundle_size = get_serialized_size_frag(
|
||||
bundle,
|
||||
frag_offset,
|
||||
frag_length
|
||||
);
|
||||
const enum bundle_routing_priority prio =
|
||||
bundle_get_routing_priority(bundle);
|
||||
|
||||
|
|
|
|||
587
components/routing/compat/router_agent.c
Normal file
587
components/routing/compat/router_agent.c
Normal file
|
|
@ -0,0 +1,587 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#include "routing/compat/config_parser.h"
|
||||
#include "routing/compat/contact_manager.h"
|
||||
#include "routing/compat/router_agent.h"
|
||||
|
||||
#include "ud3tn/agent_manager.h"
|
||||
#include "ud3tn/bundle_processor.h"
|
||||
#include "ud3tn/common.h"
|
||||
#include "ud3tn/eid.h"
|
||||
|
||||
#include "platform/hal_io.h"
|
||||
#include "platform/hal_queue.h"
|
||||
#include "platform/hal_semaphore.h"
|
||||
#include "platform/hal_time.h"
|
||||
#include "platform/hal_types.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static struct config_parser parser;
|
||||
|
||||
struct router_agent_params {
|
||||
const char *aap2_admin_secret;
|
||||
bool allow_remote_configuration;
|
||||
struct contact_manager_params cm_param;
|
||||
};
|
||||
|
||||
// BUNDLE HANDLING
|
||||
|
||||
struct bundle_processing_result {
|
||||
int32_t status_or_fragments;
|
||||
struct bundle *fragments[ROUTER_MAX_FRAGMENTS];
|
||||
};
|
||||
|
||||
#define BUNDLE_RESULT_NO_ROUTE 0
|
||||
#define BUNDLE_RESULT_NO_TIMELY_CONTACTS -1
|
||||
#define BUNDLE_RESULT_NO_MEMORY -2
|
||||
#define BUNDLE_RESULT_INVALID -3
|
||||
#define BUNDLE_RESULT_EXPIRED -4
|
||||
|
||||
|
||||
static void hand_over_contact_bundles(
|
||||
struct contact *const contact,
|
||||
struct router_agent_params *const ra_param,
|
||||
const void *const bp_context)
|
||||
{
|
||||
(void)ra_param;
|
||||
|
||||
LOGF_DEBUG(
|
||||
"RouterAgent: Dispatching bundles for contact with \"%s\".",
|
||||
contact->node->eid
|
||||
);
|
||||
|
||||
struct routed_bundle_list *rbl = contact->contact_bundles;
|
||||
|
||||
contact->contact_bundles = NULL;
|
||||
|
||||
while (rbl) {
|
||||
struct dispatch_next_hop dnh = {
|
||||
.node_id = contact->node->eid,
|
||||
.fragment_offset = rbl->frag_offset,
|
||||
.fragment_length = rbl->frag_length,
|
||||
};
|
||||
struct dispatch_result dr = {
|
||||
.orig_reason = DISPATCH_REASON_NO_FIB_ENTRY,
|
||||
.next_hop_count = 1,
|
||||
.next_hops = &dnh,
|
||||
};
|
||||
|
||||
rbl->data->forwarding_refcount--;
|
||||
bundle_processor_execute_bdm_dispatch(
|
||||
bp_context,
|
||||
rbl->data,
|
||||
&dr
|
||||
);
|
||||
|
||||
struct routed_bundle_list *const tmp = rbl;
|
||||
|
||||
rbl = tmp->next;
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
struct resched_func_param {
|
||||
const void *bp_context;
|
||||
struct router_agent_params *ra_param;
|
||||
};
|
||||
|
||||
static void cra_bundle_resched_func(
|
||||
void *const param,
|
||||
struct bundle *bundle,
|
||||
uint64_t frag_offset,
|
||||
uint64_t frag_length)
|
||||
{
|
||||
struct resched_func_param *const rfp = param;
|
||||
|
||||
// NOTE that re-scheduling may be triggered by a dropped connection
|
||||
// if there is a scheduled contact but it apparently ended earlier.
|
||||
// In this case, we allow re-scheduling for the very same contact
|
||||
// as it will try to re-connect. However, if there is a better contact,
|
||||
// this one shall be used...
|
||||
|
||||
struct router_result route = router_get_resched_route(
|
||||
bundle,
|
||||
frag_offset,
|
||||
frag_length
|
||||
);
|
||||
|
||||
if (route.fragments == 0)
|
||||
goto resched_failed;
|
||||
|
||||
ASSERT(route.fragment_results[0].payload_size == frag_length);
|
||||
|
||||
const enum ud3tn_result ratc_rv = router_add_bundle_to_contact(
|
||||
route.fragment_results[0].contact,
|
||||
bundle,
|
||||
frag_offset,
|
||||
frag_length
|
||||
);
|
||||
|
||||
if (ratc_rv != UD3TN_OK) {
|
||||
LOGF_WARN(
|
||||
"RouterAgent: Failed to add bundle %p to contact",
|
||||
bundle
|
||||
);
|
||||
goto resched_failed;
|
||||
}
|
||||
|
||||
LOGF_DEBUG(
|
||||
"RouterAgent: Added re-scheduled bundle %p [%llu, %llu] to contact at %llu",
|
||||
bundle,
|
||||
frag_offset,
|
||||
frag_offset + frag_length,
|
||||
route.fragment_results[0].contact->from_ms
|
||||
);
|
||||
|
||||
if (route.fragment_results[0].contact->link_active) {
|
||||
LOGF_DEBUG(
|
||||
"RouterAgent: Issuing immediate dispatch for contact at %llu",
|
||||
route.fragment_results[0].contact->from_ms
|
||||
);
|
||||
hand_over_contact_bundles(
|
||||
route.fragment_results[0].contact,
|
||||
rfp->ra_param,
|
||||
rfp->bp_context
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
resched_failed:
|
||||
LOGF_DEBUG(
|
||||
"RouterAgent: Bundle re-scheduling failed. Dropping bundle %p [%llu, %llu].",
|
||||
bundle,
|
||||
frag_offset,
|
||||
frag_offset + frag_length
|
||||
);
|
||||
|
||||
// Decrement the refcount as we incremented it also for
|
||||
// the reference *we* kept (that was scheduled in the contact).
|
||||
bundle->forwarding_refcount--;
|
||||
|
||||
struct dispatch_result dr = {
|
||||
.orig_reason = DISPATCH_REASON_TX_FAILED,
|
||||
.next_hop_count = 0,
|
||||
.next_hops = NULL,
|
||||
};
|
||||
|
||||
// NOTE that this function is typically called with the
|
||||
// semaphore still held, so it must be ensured that no
|
||||
// transitive dispatch request is processed.
|
||||
bundle_processor_execute_bdm_dispatch(
|
||||
rfp->bp_context,
|
||||
bundle,
|
||||
&dr
|
||||
);
|
||||
}
|
||||
|
||||
static void wake_up_contact_manager(QueueIdentifier_t cm_queue)
|
||||
{
|
||||
int cm_signal = 1;
|
||||
|
||||
// This never blocks and we ignore the return value: If there already
|
||||
// is something in the queue, the CM is about to be triggered, which is
|
||||
// what we want to achieve here.
|
||||
hal_queue_try_push_to_back(cm_queue, &cm_signal, 0);
|
||||
}
|
||||
|
||||
static void rx_callback(
|
||||
struct bundle_adu data,
|
||||
void *const param, const void *const bp_context)
|
||||
{
|
||||
struct router_agent_params *const ra_param = param;
|
||||
|
||||
if (!ra_param->allow_remote_configuration && !data.bdm_auth_validated) {
|
||||
LOGF_DEBUG(
|
||||
"RouterAgent: Dropped unauthorized config message from \"%s\"",
|
||||
data.source
|
||||
);
|
||||
bundle_adu_free_members(data);
|
||||
return;
|
||||
}
|
||||
|
||||
config_parser_read(
|
||||
&parser,
|
||||
data.payload,
|
||||
data.length
|
||||
);
|
||||
|
||||
if (parser.basedata->status != PARSER_STATUS_DONE) {
|
||||
LOGF_DEBUG(
|
||||
"RouterAgent: Dropped invalid config message from \"%s\"",
|
||||
data.source
|
||||
);
|
||||
config_parser_reset(&parser);
|
||||
bundle_adu_free_members(data);
|
||||
return;
|
||||
}
|
||||
|
||||
struct router_command *const cmd = parser.router_command;
|
||||
|
||||
parser.router_command = NULL;
|
||||
config_parser_reset(&parser);
|
||||
bundle_adu_free_members(data);
|
||||
|
||||
hal_semaphore_take_blocking(ra_param->cm_param.semaphore);
|
||||
|
||||
bool success = true;
|
||||
const uint64_t cur_time_s = hal_time_get_timestamp_s();
|
||||
|
||||
if (!node_prepare_and_verify(cmd->data, cur_time_s)) {
|
||||
free_node(cmd->data);
|
||||
LOGF_WARN("Router: Command (T = %c) is invalid!",
|
||||
cmd->type);
|
||||
free(cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
struct resched_func_param rfp = {
|
||||
.bp_context = bp_context,
|
||||
.ra_param = ra_param,
|
||||
};
|
||||
struct rescheduling_handle rescheduler = {
|
||||
.reschedule_func = cra_bundle_resched_func,
|
||||
.reschedule_func_context = &rfp,
|
||||
};
|
||||
|
||||
switch (cmd->type) {
|
||||
case ROUTER_COMMAND_ADD:
|
||||
LOGF_INFO(
|
||||
"RouterAgent: Processing ADD command for node \"%s\"",
|
||||
cmd->data->eid
|
||||
);
|
||||
success = routing_table_add_node(
|
||||
cmd->data,
|
||||
rescheduler
|
||||
);
|
||||
break;
|
||||
case ROUTER_COMMAND_UPDATE:
|
||||
LOGF_INFO(
|
||||
"RouterAgent: Processing UPDATE command for node \"%s\"",
|
||||
cmd->data->eid
|
||||
);
|
||||
success = routing_table_replace_node(
|
||||
cmd->data,
|
||||
rescheduler
|
||||
);
|
||||
break;
|
||||
case ROUTER_COMMAND_DELETE:
|
||||
LOGF_INFO(
|
||||
"RouterAgent: Processing DELETE command for node \"%s\"",
|
||||
cmd->data->eid
|
||||
);
|
||||
success = routing_table_delete_node(
|
||||
cmd->data,
|
||||
rescheduler
|
||||
);
|
||||
break;
|
||||
default:
|
||||
free_node(cmd->data);
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
hal_semaphore_release(ra_param->cm_param.semaphore);
|
||||
|
||||
if (success) {
|
||||
LOGF_INFO(
|
||||
"Router: Command (T = %c) processed.",
|
||||
cmd->type
|
||||
);
|
||||
wake_up_contact_manager(ra_param->cm_param.control_queue);
|
||||
} else {
|
||||
LOGF_INFO(
|
||||
"Router: Processing command (T = %c) failed!",
|
||||
cmd->type
|
||||
);
|
||||
}
|
||||
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
static void fib_callback(
|
||||
const struct fib_entry *const entry,
|
||||
const char *node_id,
|
||||
void *param, const void *bp_context)
|
||||
{
|
||||
struct router_agent_params *const ra_param = param;
|
||||
|
||||
hal_semaphore_take_blocking(ra_param->cm_param.semaphore);
|
||||
|
||||
const uint64_t cur_time_ms = hal_time_get_timestamp_ms();
|
||||
struct resched_func_param rfp = {
|
||||
.bp_context = bp_context,
|
||||
.ra_param = ra_param,
|
||||
};
|
||||
struct rescheduling_handle rescheduler = {
|
||||
.reschedule_func = cra_bundle_resched_func,
|
||||
.reschedule_func_context = &rfp,
|
||||
};
|
||||
struct node *node = routing_table_lookup_node(node_id);
|
||||
|
||||
if (!node) {
|
||||
LOGF_DEBUG(
|
||||
"RouterAgent: Ignoring link update for unknown node ID \"%s\"",
|
||||
node_id
|
||||
);
|
||||
hal_semaphore_release(ra_param->cm_param.semaphore);
|
||||
return;
|
||||
}
|
||||
|
||||
struct contact_list *cl = node->contacts;
|
||||
|
||||
if (entry->status == FIB_LINK_STATUS_ACTIVE) {
|
||||
// Determine the contact that became active and trigger the
|
||||
// bundle transmissions.
|
||||
while (cl) {
|
||||
if (cl->data->from_ms <= cur_time_ms &&
|
||||
cl->data->to_ms >= cur_time_ms
|
||||
) {
|
||||
cl->data->link_active = true;
|
||||
hand_over_contact_bundles(
|
||||
cl->data,
|
||||
ra_param,
|
||||
bp_context
|
||||
);
|
||||
} else if (cl->data->from_ms > cur_time_ms) {
|
||||
// The list is ordered - break here.
|
||||
break;
|
||||
}
|
||||
cl = cl->next;
|
||||
}
|
||||
} else if (entry->status == FIB_LINK_STATUS_TEARDOWN) {
|
||||
// For all contacts with the node that are still present but
|
||||
// expired, execute routing_table_contact_passed.
|
||||
while (cl) {
|
||||
if (cl->data->to_ms > cur_time_ms) {
|
||||
cl->data->link_active = false;
|
||||
// NOTE: This frees `cl`!
|
||||
routing_table_contact_passed(
|
||||
cl->data,
|
||||
rescheduler
|
||||
);
|
||||
// Re-init cl
|
||||
cl = node->contacts;
|
||||
continue;
|
||||
} else if (cl->data->from_ms > cur_time_ms) {
|
||||
// The list is ordered - break here.
|
||||
break;
|
||||
}
|
||||
cl = cl->next;
|
||||
}
|
||||
} else {
|
||||
LOGF_DEBUG(
|
||||
"RouterAgent: Ignoring link update with status %d",
|
||||
entry->status
|
||||
);
|
||||
}
|
||||
hal_semaphore_release(ra_param->cm_param.semaphore);
|
||||
}
|
||||
|
||||
static bool bdm_callback(
|
||||
struct bundle *const bundle,
|
||||
enum bundle_dispatch_reason reason,
|
||||
const char *orig_node_id,
|
||||
const char *orig_cla_addr,
|
||||
void *param, const void *bp_context)
|
||||
{
|
||||
struct router_agent_params *const ra_param = param;
|
||||
|
||||
// Other reasons are currently unsupported and we drop the bundle.
|
||||
if (reason != DISPATCH_REASON_NO_FIB_ENTRY)
|
||||
return false;
|
||||
|
||||
(void)orig_node_id;
|
||||
(void)orig_cla_addr;
|
||||
(void)bp_context;
|
||||
|
||||
ASSERT(bundle != NULL);
|
||||
const uint64_t timestamp_ms = hal_time_get_timestamp_ms();
|
||||
|
||||
if (bundle_get_expiration_time_ms(bundle) < timestamp_ms) {
|
||||
// Bundle is already expired on arrival at the router...
|
||||
return false;
|
||||
}
|
||||
|
||||
hal_semaphore_take_blocking(ra_param->cm_param.semaphore);
|
||||
|
||||
struct router_result route = router_get_first_route(bundle);
|
||||
uint64_t cur_offset = 0;
|
||||
|
||||
for (int i = 0; i < route.fragments; i++) {
|
||||
const enum ud3tn_result ratc_rv = router_add_bundle_to_contact(
|
||||
route.fragment_results[i].contact,
|
||||
bundle,
|
||||
cur_offset,
|
||||
route.fragment_results[i].payload_size
|
||||
);
|
||||
|
||||
if (ratc_rv != UD3TN_OK) {
|
||||
// Remove the already-scheduled fragments again in case
|
||||
// of failure.
|
||||
for (int j = i - 1; j >= 0; j--) {
|
||||
cur_offset -= (
|
||||
route.fragment_results[j].payload_size
|
||||
);
|
||||
router_remove_bundle_from_contact(
|
||||
route.fragment_results[j].contact,
|
||||
bundle,
|
||||
cur_offset,
|
||||
route.fragment_results[j].payload_size
|
||||
);
|
||||
bundle->forwarding_refcount--;
|
||||
}
|
||||
LOGF_WARN("RouterAgent: Failed to add bundle %p to contact",
|
||||
bundle);
|
||||
break;
|
||||
}
|
||||
|
||||
bundle->forwarding_refcount++;
|
||||
|
||||
LOGF_DEBUG(
|
||||
"RouterAgent: Added fragment of bundle %p [%llu, %llu] to contact at %llu",
|
||||
bundle,
|
||||
cur_offset,
|
||||
cur_offset + route.fragment_results[i].payload_size,
|
||||
route.fragment_results[i].contact->from_ms
|
||||
);
|
||||
|
||||
if (route.fragment_results[i].contact->link_active) {
|
||||
LOGF_DEBUG(
|
||||
"RouterAgent: Issuing immediate dispatch for contact at %llu",
|
||||
route.fragment_results[i].contact->from_ms
|
||||
);
|
||||
hand_over_contact_bundles(
|
||||
route.fragment_results[i].contact,
|
||||
ra_param,
|
||||
bp_context
|
||||
);
|
||||
}
|
||||
|
||||
cur_offset += route.fragment_results[i].payload_size;
|
||||
}
|
||||
|
||||
hal_semaphore_release(ra_param->cm_param.semaphore);
|
||||
|
||||
LOGF_DEBUG(
|
||||
"RouterAgent: Bundle %p [ %s ] [ frag = %d ]",
|
||||
bundle,
|
||||
(route.fragments < 1) ? "ERR" : "OK",
|
||||
route.fragments
|
||||
);
|
||||
|
||||
return route.fragments != 0 ? true : false;
|
||||
}
|
||||
|
||||
|
||||
int compat_router_agent_setup(
|
||||
const struct bundle_agent_interface *bai,
|
||||
bool allow_remote_configuration,
|
||||
const char *aap2_admin_secret)
|
||||
{
|
||||
const int is_ipn = get_eid_scheme(bai->local_eid) == EID_SCHEME_IPN;
|
||||
struct router_agent_params *const ra_param = malloc(
|
||||
sizeof(struct router_agent_params)
|
||||
);
|
||||
|
||||
if (!ra_param) {
|
||||
LOG_ERROR("RouterAgent: Memory allocation failed!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ra_param->aap2_admin_secret = aap2_admin_secret;
|
||||
ra_param->allow_remote_configuration = allow_remote_configuration;
|
||||
|
||||
struct parser *const config_parser_base = config_parser_init(&parser);
|
||||
|
||||
if (!config_parser_base) {
|
||||
LOG_ERROR("RouterAgent: Could not initialize config parser!");
|
||||
free(ra_param);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (routing_table_init() != UD3TN_OK) {
|
||||
LOG_ERROR("RouterAgent: Could not initialize routing table!");
|
||||
free(ra_param);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// NOTE: Compared to v0.13, the Contact Manager only starts and
|
||||
// terminates contacts (issuing the relevant FIB commands). Handing
|
||||
// over bundles to the BP for dispatch is done in response to the
|
||||
// FIB callback.
|
||||
ra_param->cm_param = contact_manager_start(
|
||||
bai->bundle_signaling_queue,
|
||||
routing_table_get_raw_contact_list_ptr()
|
||||
);
|
||||
|
||||
if (ra_param->cm_param.task_creation_result != UD3TN_OK) {
|
||||
LOG_ERROR("RouterAgent: Failed to start Contact Manager!");
|
||||
free(ra_param);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const struct agent agent = {
|
||||
.auth_trx = true,
|
||||
.auth_fib = true,
|
||||
.auth_bdm = true,
|
||||
.is_subscriber = true,
|
||||
.sink_identifier = (
|
||||
is_ipn
|
||||
? AGENT_ID_CONFIG_IPN
|
||||
: AGENT_ID_CONFIG_DTN
|
||||
),
|
||||
.trx_callback = rx_callback,
|
||||
.fib_callback = fib_callback,
|
||||
.bdm_callback = bdm_callback,
|
||||
.param = ra_param,
|
||||
.secret = aap2_admin_secret,
|
||||
};
|
||||
const int rv = bundle_processor_perform_agent_action_async(
|
||||
bai->bundle_signaling_queue,
|
||||
BP_SIGNAL_AGENT_REGISTER,
|
||||
agent
|
||||
);
|
||||
|
||||
if (rv) {
|
||||
LOG_ERROR("RouterAgent: BP failed to register SUB agent!");
|
||||
free(ra_param);
|
||||
config_parser_reset(&parser);
|
||||
return rv;
|
||||
}
|
||||
|
||||
const struct agent agent_rpc = {
|
||||
.auth_trx = false,
|
||||
.auth_fib = true,
|
||||
.auth_bdm = true,
|
||||
.is_subscriber = false,
|
||||
.sink_identifier = (
|
||||
is_ipn
|
||||
? AGENT_ID_CONFIG_IPN
|
||||
: AGENT_ID_CONFIG_DTN
|
||||
),
|
||||
.secret = aap2_admin_secret,
|
||||
};
|
||||
const int rv_rpc = bundle_processor_perform_agent_action_async(
|
||||
bai->bundle_signaling_queue,
|
||||
BP_SIGNAL_AGENT_REGISTER,
|
||||
agent_rpc
|
||||
);
|
||||
|
||||
if (rv_rpc) {
|
||||
LOG_ERROR("RouterAgent: BP failed to register RPC agent!");
|
||||
bundle_processor_perform_agent_action_async(
|
||||
bai->bundle_signaling_queue,
|
||||
BP_SIGNAL_AGENT_DEREGISTER,
|
||||
agent
|
||||
);
|
||||
free(ra_param);
|
||||
return rv_rpc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,235 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#include "ud3tn/bundle.h"
|
||||
#include "ud3tn/bundle_fragmenter.h"
|
||||
#include "ud3tn/common.h"
|
||||
#include "ud3tn/node.h"
|
||||
#include "ud3tn/router.h"
|
||||
#include "ud3tn/routing_table.h"
|
||||
|
||||
#include "platform/hal_io.h"
|
||||
#include "platform/hal_time.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// COMMAND HANDLING
|
||||
|
||||
static bool process_router_command(
|
||||
struct router_command *router_cmd,
|
||||
struct rescheduling_handle rescheduler)
|
||||
{
|
||||
switch (router_cmd->type) {
|
||||
case ROUTER_COMMAND_ADD:
|
||||
return routing_table_add_node(
|
||||
router_cmd->data,
|
||||
rescheduler
|
||||
);
|
||||
case ROUTER_COMMAND_UPDATE:
|
||||
return routing_table_replace_node(
|
||||
router_cmd->data,
|
||||
rescheduler
|
||||
);
|
||||
case ROUTER_COMMAND_DELETE:
|
||||
return routing_table_delete_node(
|
||||
router_cmd->data,
|
||||
rescheduler
|
||||
);
|
||||
default:
|
||||
free_node(router_cmd->data);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
enum ud3tn_result router_process_command(
|
||||
struct router_command *command,
|
||||
struct rescheduling_handle rescheduler)
|
||||
{
|
||||
bool success = true;
|
||||
const uint64_t cur_time_s = hal_time_get_timestamp_s();
|
||||
|
||||
if (!node_prepare_and_verify(command->data, cur_time_s)) {
|
||||
free_node(command->data);
|
||||
LOGF_WARN("Router: Command (T = %c) is invalid!",
|
||||
command->type);
|
||||
free(command);
|
||||
return UD3TN_FAIL;
|
||||
}
|
||||
|
||||
success = process_router_command(
|
||||
command,
|
||||
rescheduler
|
||||
);
|
||||
if (success) {
|
||||
LOGF_DEBUG(
|
||||
"Router: Command (T = %c) processed.",
|
||||
command->type
|
||||
);
|
||||
} else {
|
||||
LOGF_DEBUG(
|
||||
"Router: Processing command (T = %c) failed!",
|
||||
command->type
|
||||
);
|
||||
}
|
||||
free(command);
|
||||
|
||||
return success ? UD3TN_OK : UD3TN_FAIL;
|
||||
}
|
||||
|
||||
// BUNDLE HANDLING
|
||||
|
||||
struct bundle_processing_result {
|
||||
int32_t status_or_fragments;
|
||||
struct bundle *fragments[ROUTER_MAX_FRAGMENTS];
|
||||
};
|
||||
|
||||
#define BUNDLE_RESULT_NO_ROUTE 0
|
||||
#define BUNDLE_RESULT_NO_TIMELY_CONTACTS -1
|
||||
#define BUNDLE_RESULT_NO_MEMORY -2
|
||||
#define BUNDLE_RESULT_INVALID -3
|
||||
#define BUNDLE_RESULT_EXPIRED -4
|
||||
|
||||
static inline enum router_result_status br_to_rrs(int8_t bh_result)
|
||||
{
|
||||
switch (bh_result) {
|
||||
case BUNDLE_RESULT_NO_ROUTE:
|
||||
return ROUTER_RESULT_NO_ROUTE;
|
||||
case BUNDLE_RESULT_NO_MEMORY:
|
||||
return ROUTER_RESULT_NO_MEMORY;
|
||||
case BUNDLE_RESULT_EXPIRED:
|
||||
return ROUTER_RESULT_EXPIRED;
|
||||
case BUNDLE_RESULT_NO_TIMELY_CONTACTS:
|
||||
default:
|
||||
return ROUTER_RESULT_NO_TIMELY_CONTACTS;
|
||||
}
|
||||
}
|
||||
|
||||
static struct bundle_processing_result apply_fragmentation(
|
||||
struct bundle *bundle, struct router_result route);
|
||||
|
||||
static struct bundle_processing_result process_bundle(struct bundle *bundle)
|
||||
{
|
||||
struct router_result route;
|
||||
struct bundle_processing_result result = {
|
||||
.status_or_fragments = BUNDLE_RESULT_NO_ROUTE
|
||||
};
|
||||
|
||||
ASSERT(bundle != NULL);
|
||||
const uint64_t timestamp_ms = hal_time_get_timestamp_ms();
|
||||
|
||||
if (bundle_get_expiration_time_ms(bundle) < timestamp_ms) {
|
||||
// Bundle is already expired on arrival at the router...
|
||||
result.status_or_fragments = BUNDLE_RESULT_EXPIRED;
|
||||
return result;
|
||||
}
|
||||
|
||||
route = router_get_first_route(bundle);
|
||||
if (route.fragments == 1) {
|
||||
result.fragments[0] = bundle;
|
||||
if (router_add_bundle_to_contact(
|
||||
route.fragment_results[0].contact,
|
||||
bundle) == UD3TN_OK)
|
||||
result.status_or_fragments = 1;
|
||||
else
|
||||
result.status_or_fragments = BUNDLE_RESULT_NO_MEMORY;
|
||||
} else if (route.fragments && !bundle_must_not_fragment(bundle)) {
|
||||
// Only fragment if it is allowed -- if not, there is no route.
|
||||
result = apply_fragmentation(bundle, route);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct bundle_processing_result apply_fragmentation(
|
||||
struct bundle *bundle, struct router_result route)
|
||||
{
|
||||
struct bundle *frags[ROUTER_MAX_FRAGMENTS];
|
||||
uint32_t size;
|
||||
int32_t f, g;
|
||||
int32_t fragments = route.fragments;
|
||||
struct bundle_processing_result result = {
|
||||
.status_or_fragments = BUNDLE_RESULT_NO_MEMORY
|
||||
};
|
||||
|
||||
/* Create fragments */
|
||||
frags[0] = bundlefragmenter_initialize_first_fragment(bundle);
|
||||
if (frags[0] == NULL)
|
||||
return result;
|
||||
|
||||
for (f = 0; f < fragments - 1; f++) {
|
||||
/* Determine minimal fragmented bundle size */
|
||||
if (f == 0)
|
||||
size = bundle_get_first_fragment_min_size(bundle);
|
||||
else
|
||||
size = bundle_get_mid_fragment_min_size(bundle);
|
||||
|
||||
frags[f + 1] = bundlefragmenter_fragment_bundle(frags[f],
|
||||
size + route.fragment_results[f].payload_size);
|
||||
|
||||
if (frags[f + 1] == NULL) {
|
||||
for (g = 0; g <= f; g++)
|
||||
bundle_free(frags[g]);
|
||||
return result;
|
||||
} else if (frags[f] == frags[f + 1]) {
|
||||
// Not fragmented b/c not needed - the router does some
|
||||
// conservative estimations regarding size of CBOR ints
|
||||
// that may lead to fewer actual fragments here.
|
||||
// Just update the count accordingly and do not schedule
|
||||
// the rest.
|
||||
fragments = f + 1;
|
||||
route.fragments = fragments;
|
||||
frags[fragments] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add to route */
|
||||
for (f = 0; f < fragments; f++) {
|
||||
if (router_add_bundle_to_contact(
|
||||
route.fragment_results[f].contact,
|
||||
frags[f]) != UD3TN_OK) {
|
||||
LOGF_INFO(
|
||||
"Router: Scheduling bundle %p failed, dropping all fragments.",
|
||||
bundle
|
||||
);
|
||||
// Remove from all previously-scheduled routes
|
||||
for (g = 0; g < f; g++)
|
||||
router_remove_bundle_from_contact(
|
||||
route.fragment_results[g].contact,
|
||||
frags[g]
|
||||
);
|
||||
// Drop _all_ fragments
|
||||
for (g = 0; g < fragments; g++)
|
||||
bundle_free(frags[g]);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/* Success - remove bundle */
|
||||
bundle_free(bundle);
|
||||
|
||||
for (f = 0; f < fragments; f++)
|
||||
result.fragments[f] = frags[f];
|
||||
result.status_or_fragments = fragments;
|
||||
return result;
|
||||
}
|
||||
|
||||
enum router_result_status router_route_bundle(struct bundle *b)
|
||||
{
|
||||
struct bundle_processing_result proc_result = {
|
||||
.status_or_fragments = BUNDLE_RESULT_INVALID
|
||||
};
|
||||
|
||||
if (b != NULL)
|
||||
proc_result = process_bundle(b);
|
||||
|
||||
LOGF_DEBUG(
|
||||
"Router: Bundle %p [ %s ] [ frag = %d ]",
|
||||
b,
|
||||
(proc_result.status_or_fragments < 1) ? "ERR" : "OK",
|
||||
proc_result.status_or_fragments
|
||||
);
|
||||
if (proc_result.status_or_fragments < 1)
|
||||
return br_to_rrs(proc_result.status_or_fragments);
|
||||
return ROUTER_RESULT_OK;
|
||||
}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#include "routing/compat/node.h"
|
||||
#include "routing/compat/router.h"
|
||||
#include "routing/compat/routing_table.h"
|
||||
|
||||
#include "ud3tn/bundle.h"
|
||||
#include "ud3tn/common.h"
|
||||
#include "ud3tn/node.h"
|
||||
#include "ud3tn/router.h"
|
||||
#include "ud3tn/routing_table.h"
|
||||
#include "ud3tn/simplehtab.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
|
@ -87,23 +88,6 @@ struct node_table_entry *routing_table_lookup_eid(const char *eid)
|
|||
}
|
||||
|
||||
|
||||
uint8_t routing_table_lookup_hot_node(
|
||||
struct node **target, uint8_t max)
|
||||
{
|
||||
struct node_list *cur = node_list;
|
||||
uint8_t c = 0;
|
||||
|
||||
while (cur != NULL) {
|
||||
if (HAS_FLAG(cur->node->flags, NODE_FLAG_INTERNET_ACCESS)) {
|
||||
target[c] = cur->node;
|
||||
if (++c == max)
|
||||
break;
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* NODE LIST MODIFICATION */
|
||||
static void add_node_to_tables(struct node *node);
|
||||
static void remove_node_from_tables(struct node *node, bool drop_contacts,
|
||||
|
|
@ -235,7 +219,7 @@ bool routing_table_delete_node(
|
|||
{
|
||||
struct node_list **entry_ptr, *old_node_entry;
|
||||
struct node *cur_node;
|
||||
struct contact_list *modified = NULL, *deleted = NULL, *next, *tmp;
|
||||
struct contact_list *modified = NULL, *deleted = NULL, *next;
|
||||
|
||||
entry_ptr = get_node_entry_ptr_by_eid(new_node->eid);
|
||||
if (entry_ptr != NULL) {
|
||||
|
|
@ -258,6 +242,8 @@ bool routing_table_delete_node(
|
|||
cur_node->contacts = contact_list_difference(
|
||||
cur_node->contacts, new_node->contacts,
|
||||
&modified, &deleted);
|
||||
/* Add node again before re-scheduling bundles */
|
||||
add_node_to_tables(cur_node);
|
||||
/* Process modified contacts */
|
||||
while (modified != NULL) {
|
||||
reschedule_bundles(
|
||||
|
|
@ -270,15 +256,8 @@ bool routing_table_delete_node(
|
|||
while (deleted != NULL) {
|
||||
reschedule_bundles(
|
||||
deleted->data, rescheduler);
|
||||
if (deleted->data->active) {
|
||||
tmp = deleted;
|
||||
deleted = tmp->next;
|
||||
free(tmp);
|
||||
} else {
|
||||
deleted = contact_list_free(deleted);
|
||||
}
|
||||
deleted = contact_list_free(deleted);
|
||||
}
|
||||
add_node_to_tables(cur_node);
|
||||
free_node(new_node);
|
||||
}
|
||||
return true;
|
||||
|
|
@ -349,16 +328,6 @@ static void remove_node_from_tables(struct node *node, bool drop_contacts,
|
|||
if (drop_contacts) {
|
||||
reschedule_bundles(cur_contact->data,
|
||||
rescheduler);
|
||||
// If the contact is active, un-associate it to prevent
|
||||
// freeing it right now.
|
||||
if (cur_contact->data->active) {
|
||||
cur_contact->data->node = NULL;
|
||||
*cur_slot = cur_contact->next;
|
||||
free(cur_contact);
|
||||
// List item was replaced by next item,
|
||||
// process this one now...
|
||||
continue;
|
||||
}
|
||||
}
|
||||
cur_slot = &(*cur_slot)->next;
|
||||
}
|
||||
|
|
@ -486,8 +455,10 @@ void routing_table_contact_passed(
|
|||
if (contact->node != NULL) {
|
||||
while (contact->contact_bundles != NULL) {
|
||||
rescheduler.reschedule_func(
|
||||
rescheduler.reschedule_func_context,
|
||||
contact->contact_bundles->data,
|
||||
rescheduler.reschedule_func_context
|
||||
contact->contact_bundles->frag_offset,
|
||||
contact->contact_bundles->frag_length
|
||||
);
|
||||
tmp = contact->contact_bundles->next;
|
||||
free(contact->contact_bundles);
|
||||
|
|
@ -502,19 +473,27 @@ void routing_table_contact_passed(
|
|||
static void reschedule_bundles(
|
||||
struct contact *contact, struct rescheduling_handle rescheduler)
|
||||
{
|
||||
struct bundle *b;
|
||||
|
||||
ASSERT(contact != NULL);
|
||||
if (!contact)
|
||||
return;
|
||||
|
||||
/* Empty the bundle list and queue them in for re-scheduling */
|
||||
while (contact->contact_bundles != NULL) {
|
||||
b = contact->contact_bundles->data;
|
||||
router_remove_bundle_from_contact(contact, b);
|
||||
struct bundle *const bdl = contact->contact_bundles->data;
|
||||
const uint64_t f_offset = contact->contact_bundles->frag_offset;
|
||||
const uint64_t f_length = contact->contact_bundles->frag_length;
|
||||
|
||||
router_remove_bundle_from_contact(
|
||||
contact,
|
||||
bdl,
|
||||
f_offset,
|
||||
f_length
|
||||
);
|
||||
rescheduler.reschedule_func(
|
||||
b,
|
||||
rescheduler.reschedule_func_context
|
||||
rescheduler.reschedule_func_context,
|
||||
bdl,
|
||||
f_offset,
|
||||
f_length
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,11 @@ struct bp_context {
|
|||
} *known_bundle_list;
|
||||
};
|
||||
|
||||
struct fib_update_params {
|
||||
const char *cla_addr;
|
||||
struct bp_context *bp_context;
|
||||
};
|
||||
|
||||
/* DECLARATIONS */
|
||||
|
||||
static inline void handle_signal(
|
||||
|
|
@ -70,6 +75,9 @@ static inline void handle_signal(
|
|||
static bool send_fib_to_agent(
|
||||
void *context, const char *node_id,
|
||||
const struct fib_entry *link);
|
||||
static bool send_fib_updates_to_agent(
|
||||
void *param, const char *node_id,
|
||||
const struct fib_entry *link);
|
||||
|
||||
static enum ud3tn_result bundle_dispatch(
|
||||
struct bp_context *const ctx, struct bundle *bundle);
|
||||
|
|
@ -305,6 +313,7 @@ static inline void handle_signal(
|
|||
struct agent_manager_parameters *aaps;
|
||||
agent_no_t agent_feedback;
|
||||
struct fib_entry *fib_entry;
|
||||
struct fib_update_params fup = { .bp_context = ctx };
|
||||
struct bundle *bundle;
|
||||
|
||||
switch (signal.type) {
|
||||
|
|
@ -321,13 +330,14 @@ static inline void handle_signal(
|
|||
} else {
|
||||
bundle = signal.bundle;
|
||||
}
|
||||
bundle->forwarding_refcount--;
|
||||
LOGF_DEBUG(
|
||||
"BundleProcessor: TX of bundle %p to %s via %s OK",
|
||||
"BundleProcessor: TX of bundle %p to %s via %s OK (-> %d references)",
|
||||
bundle,
|
||||
signal.peer_node_id,
|
||||
signal.peer_cla_addr
|
||||
signal.peer_cla_addr,
|
||||
bundle->forwarding_refcount
|
||||
);
|
||||
bundle->forwarding_refcount--;
|
||||
// If the handover fails, declare success immediately.
|
||||
if (agent_handover_to_dispatch(
|
||||
ctx->agent_manager,
|
||||
|
|
@ -351,13 +361,14 @@ static inline void handle_signal(
|
|||
} else {
|
||||
bundle = signal.bundle;
|
||||
}
|
||||
bundle->forwarding_refcount--;
|
||||
LOGF_DEBUG(
|
||||
"BundleProcessor: TX of bundle %p to %s via %s FAILED",
|
||||
"BundleProcessor: TX of bundle %p to %s via %s FAILED (-> %d references)",
|
||||
bundle,
|
||||
signal.peer_node_id,
|
||||
signal.peer_cla_addr
|
||||
signal.peer_cla_addr,
|
||||
bundle->forwarding_refcount
|
||||
);
|
||||
bundle->forwarding_refcount--;
|
||||
if (agent_handover_to_dispatch(
|
||||
ctx->agent_manager,
|
||||
bundle,
|
||||
|
|
@ -414,7 +425,8 @@ static inline void handle_signal(
|
|||
);
|
||||
fib_entry = fib_insert_link(ctx->fib, signal.peer_cla_addr);
|
||||
fib_entry->status = FIB_LINK_STATUS_ACTIVE;
|
||||
agent_send_fib_info(ctx->agent_manager, fib_entry, NULL, ctx);
|
||||
fup.cla_addr = fib_entry->cla_addr;
|
||||
fib_foreach(ctx->fib, send_fib_updates_to_agent, &fup);
|
||||
free(signal.peer_cla_addr);
|
||||
break;
|
||||
case BP_SIGNAL_LINK_DOWN:
|
||||
|
|
@ -422,17 +434,13 @@ static inline void handle_signal(
|
|||
"BundleProcessor: Marking link to CLA address \"%s\" as inactive",
|
||||
signal.peer_cla_addr
|
||||
);
|
||||
fib_remove_cla_addr(ctx->fib, signal.peer_cla_addr);
|
||||
fib_entry = malloc(sizeof(struct fib_entry));
|
||||
ASSERT(fib_entry != NULL);
|
||||
if (fib_entry == NULL)
|
||||
break;
|
||||
// allocation ownership moved
|
||||
fib_entry->cla_addr = signal.peer_cla_addr;
|
||||
fib_entry = fib_insert_link(ctx->fib, signal.peer_cla_addr);
|
||||
fib_entry->status = FIB_LINK_STATUS_TEARDOWN;
|
||||
fib_entry->flags = FIB_LINK_FLAG_NONE;
|
||||
agent_send_fib_info(ctx->agent_manager, fib_entry, NULL, ctx);
|
||||
free(fib_entry);
|
||||
fup.cla_addr = fib_entry->cla_addr;
|
||||
fib_foreach(ctx->fib, send_fib_updates_to_agent, &fup);
|
||||
// TODO: This drops the flags associated with the entry. We
|
||||
// should associate the flags with the node, not the CLA addr.
|
||||
fib_remove_cla_addr(ctx->fib, signal.peer_cla_addr);
|
||||
free(signal.peer_cla_addr);
|
||||
break;
|
||||
case BP_SIGNAL_BUNDLE_BDM_DISPATCH:
|
||||
|
|
@ -689,6 +697,26 @@ static bool send_fib_to_agent(void *context, const char *node_id,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool send_fib_updates_to_agent(
|
||||
void *param, const char *node_id,
|
||||
const struct fib_entry *link)
|
||||
{
|
||||
struct fib_update_params *p = param;
|
||||
|
||||
// TODO: Make this more efficient, e.g., by putting a mapping of
|
||||
// CLA address -> EIDs pointing to it into the FIB.
|
||||
if (strcmp(link->cla_addr, p->cla_addr) == 0) {
|
||||
agent_send_fib_info(
|
||||
p->bp_context->agent_manager,
|
||||
link,
|
||||
node_id,
|
||||
p->bp_context
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* BUNDLE HANDLING */
|
||||
|
||||
/* 5.3 */
|
||||
|
|
@ -1332,6 +1360,7 @@ static void bundle_delete(
|
|||
/* 5.15 (BPv7-bis) */
|
||||
static void bundle_discard(struct bundle *bundle)
|
||||
{
|
||||
LOGF_DEBUG("BundleProcessor: Discarding bundle %p", bundle);
|
||||
bundle_drop(bundle);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ const struct ud3tn_cmdline_options *parse_cmdline(int argc, char *argv[])
|
|||
result->aap2_socket = NULL;
|
||||
result->aap2_bdm_secret = NULL;
|
||||
result->bundle_version = DEFAULT_BUNDLE_VERSION;
|
||||
result->external_dispatch = false;
|
||||
result->status_reporting = false;
|
||||
result->allow_remote_configuration = false;
|
||||
result->exit_immediately = false;
|
||||
|
|
@ -69,7 +70,7 @@ const struct ud3tn_cmdline_options *parse_cmdline(int argc, char *argv[])
|
|||
result->eid = NULL;
|
||||
result->cla_options = NULL;
|
||||
|
||||
const char *GETOPT_SHORTOPTS = ":a:b:c:e:l:L:m:p:s:S:x:rRhu";
|
||||
const char *GETOPT_SHORTOPTS = ":a:b:c:e:l:L:m:p:s:S:x:rRhud";
|
||||
int option_index = 0;
|
||||
|
||||
if (!argv || argc <= 1)
|
||||
|
|
@ -100,6 +101,9 @@ const struct ud3tn_cmdline_options *parse_cmdline(int argc, char *argv[])
|
|||
}
|
||||
result->cla_options = strdup(optarg);
|
||||
break;
|
||||
case 'd':
|
||||
result->external_dispatch = true;
|
||||
break;
|
||||
case 'e':
|
||||
if (!optarg || validate_local_eid(optarg) != UD3TN_OK ||
|
||||
strcmp("dtn:none", optarg) == 0) {
|
||||
|
|
@ -261,6 +265,7 @@ static void shorten_long_cli_options(const int argc, char *argv[])
|
|||
{"--bp-version", "-b"},
|
||||
{"--cla", "-c"},
|
||||
{"--eid", "-e"},
|
||||
{"--external-dispatch", "-d"},
|
||||
{"--help", "-h"},
|
||||
{"--lifetime", "-l"},
|
||||
{"--max-bundle-size", "-m"},
|
||||
|
|
@ -287,7 +292,8 @@ static void print_usage_text(void)
|
|||
const char *usage_text = "Usage: ud3tn\n"
|
||||
" [-a HOST, --aap-host HOST] [-p PORT, --aap-port PORT]\n"
|
||||
" [-b 6|7, --bp-version 6|7] [-c CLA_OPTIONS, --cla CLA_OPTIONS]\n"
|
||||
" [-e EID, --eid EID] [-h, --help] [-l SECONDS, --lifetime SECONDS]\n"
|
||||
" [-d, --external-dispatch] [-e EID, --eid EID]\n"
|
||||
" [-h, --help] [-l SECONDS, --lifetime SECONDS]\n"
|
||||
" [-m BYTES, --max-bundle-size BYTES] [-r, --status-reports]\n"
|
||||
" [-R, --allow-remote-config] [-L " LOG_LEVELS ", --log-level " LOG_LEVELS "]\n"
|
||||
" [-s PATH --aap-socket PATH] [-S PATH --aap2-socket PATH]\n"
|
||||
|
|
@ -305,6 +311,7 @@ static void print_help_text(void)
|
|||
" -b, --bp-version 6|7 bundle protocol version of bundles created via AAP\n"
|
||||
" -c, --cla CLA_OPTIONS configure the CLA subsystem according to the\n"
|
||||
" syntax documented in the man page\n"
|
||||
" -d, --external-dispatch do not load the internal minimal router, allow for using an AAP 2.0 BDM\n"
|
||||
" -e, --eid EID local endpoint identifier\n"
|
||||
" -h, --help print this text and exit\n"
|
||||
" -l, --lifetime SECONDS lifetime of bundles created via AAP\n"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include "cla/cla.h"
|
||||
|
||||
#include "routing/compat/router_agent.h"
|
||||
|
||||
#include "platform/hal_io.h"
|
||||
#include "platform/hal_platform.h"
|
||||
#include "platform/hal_queue.h"
|
||||
|
|
@ -167,6 +169,19 @@ void start_tasks(const struct ud3tn_cmdline_options *const opt)
|
|||
abort();
|
||||
}
|
||||
|
||||
if (!opt->external_dispatch) {
|
||||
const int cra_rv = compat_router_agent_setup(
|
||||
&bundle_agent_interface,
|
||||
opt->allow_remote_configuration,
|
||||
opt->aap2_bdm_secret
|
||||
);
|
||||
|
||||
if (cra_rv) {
|
||||
LOG_ERROR("INIT: Router could not be initialized!");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
daemon_mode = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ new bundles
|
|||
provides parameters to the available CLA subsystem according to the
|
||||
syntax documented below
|
||||
.TP
|
||||
-d, --external-dispatch
|
||||
do not load the internal minimal router, allow for using an AAP 2.0 BDM
|
||||
.TP
|
||||
-e, --eid EID
|
||||
the EID (node ID) which is used by the started ud3tn instance itself
|
||||
.TP
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#ifndef CONFIGAGENT_H_INCLUDED
|
||||
#define CONFIGAGENT_H_INCLUDED
|
||||
|
||||
#include "platform/hal_types.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define CONFIG_AGENT_QUEUE_SIZE 100
|
||||
#define CONFIG_AGENT_TASK_PRIORITY 2
|
||||
|
||||
// Default Agent IDs.
|
||||
#ifndef AGENT_ID_CONFIG_DTN
|
||||
#define AGENT_ID_CONFIG_DTN "config"
|
||||
#endif // AGENT_ID_CONFIG_DTN
|
||||
#ifndef AGENT_ID_CONFIG_IPN
|
||||
#define AGENT_ID_CONFIG_IPN "9000"
|
||||
#endif // AGENT_ID_CONFIG_IPN
|
||||
|
||||
struct config_agent_item {
|
||||
uint8_t *data;
|
||||
size_t data_length;
|
||||
};
|
||||
|
||||
int config_agent_setup(
|
||||
QueueIdentifier_t bundle_processor_signaling_queue,
|
||||
const char *local_eid,
|
||||
bool allow_remote_configuration,
|
||||
void *bundle_processor_context);
|
||||
|
||||
#endif /* CONFIGAGENT_H_INCLUDED */
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#ifndef CONFIGPARSER_H_INCLUDED
|
||||
#define CONFIGPARSER_H_INCLUDED
|
||||
#ifndef RTCOMPAT_CONFIGPARSER_H_INCLUDED
|
||||
#define RTCOMPAT_CONFIGPARSER_H_INCLUDED
|
||||
|
||||
#include "ud3tn/parser.h"
|
||||
#include "ud3tn/result.h"
|
||||
#include "ud3tn/router.h"
|
||||
|
||||
#include "routing/compat/router.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
|
@ -39,8 +40,6 @@ enum config_parser_stage {
|
|||
|
||||
struct config_parser {
|
||||
struct parser *basedata;
|
||||
void (*send_callback)(void *, struct router_command *);
|
||||
void *send_param;
|
||||
enum config_parser_stage stage;
|
||||
struct router_command *router_command;
|
||||
int current_index;
|
||||
|
|
@ -49,11 +48,9 @@ struct config_parser {
|
|||
struct contact_list *current_contact;
|
||||
};
|
||||
|
||||
struct parser *config_parser_init(
|
||||
struct config_parser *parser,
|
||||
void (*send_callback)(void *, struct router_command *), void *param);
|
||||
struct parser *config_parser_init(struct config_parser *parser);
|
||||
size_t config_parser_read(struct config_parser *parser,
|
||||
const uint8_t *buffer, size_t length);
|
||||
enum ud3tn_result config_parser_reset(struct config_parser *parser);
|
||||
|
||||
#endif /* CONFIGPARSER_H_INCLUDED */
|
||||
#endif // RTCOMPAT_CONFIGPARSER_H_INCLUDED
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#ifndef CONTACTMANAGER_H_INCLUDED
|
||||
#define CONTACTMANAGER_H_INCLUDED
|
||||
#ifndef RTCOMPAT_CONTACTMANAGER_H_INCLUDED
|
||||
#define RTCOMPAT_CONTACTMANAGER_H_INCLUDED
|
||||
|
||||
#include "ud3tn/common.h"
|
||||
#include "ud3tn/node.h"
|
||||
|
||||
#include "routing/compat/node.h"
|
||||
|
||||
#include "platform/hal_types.h"
|
||||
|
||||
|
|
@ -20,16 +21,8 @@ struct contact_manager_params {
|
|||
QueueIdentifier_t control_queue;
|
||||
};
|
||||
|
||||
/* Flags what should be checked */
|
||||
enum contact_manager_signal {
|
||||
CM_SIGNAL_NONE = 0x0,
|
||||
CM_SIGNAL_UPDATE_CONTACT_LIST = 0x1,
|
||||
CM_SIGNAL_PROCESS_CURRENT_BUNDLES = 0x2,
|
||||
CM_SIGNAL_UNKNOWN = 0x3
|
||||
};
|
||||
|
||||
struct contact_manager_params contact_manager_start(
|
||||
QueueIdentifier_t bp_queue,
|
||||
struct contact_list **clistptr);
|
||||
|
||||
#endif /* CONTACTMANAGER_H_INCLUDED */
|
||||
#endif // RTCOMPAT_CONTACTMANAGER_H_INCLUDED
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#ifndef NODE_H_INCLUDED
|
||||
#define NODE_H_INCLUDED
|
||||
#ifndef RTCOMPAT_NODE_H_INCLUDED
|
||||
#define RTCOMPAT_NODE_H_INCLUDED
|
||||
|
||||
#include "ud3tn/bundle.h"
|
||||
#include "ud3tn/result.h"
|
||||
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
struct routed_bundle_list {
|
||||
struct bundle *data;
|
||||
uint64_t frag_offset;
|
||||
uint64_t frag_length;
|
||||
struct routed_bundle_list *next;
|
||||
};
|
||||
|
||||
|
|
@ -21,9 +23,9 @@ struct contact {
|
|||
int32_t remaining_capacity_p0;
|
||||
int32_t remaining_capacity_p1;
|
||||
int32_t remaining_capacity_p2;
|
||||
bool link_active;
|
||||
struct endpoint_list *contact_endpoints;
|
||||
struct routed_bundle_list *contact_bundles;
|
||||
int8_t active;
|
||||
};
|
||||
|
||||
struct contact_list {
|
||||
|
|
@ -93,4 +95,4 @@ int add_contact_to_ordered_list(
|
|||
int remove_contact_from_list(
|
||||
struct contact_list **list, struct contact *contact);
|
||||
|
||||
#endif // NODE_H_INCLUDED
|
||||
#endif // RTCOMPAT_NODE_H_INCLUDED
|
||||
|
|
|
|||
|
|
@ -1,21 +1,22 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#ifndef ROUTER_H_INCLUDED
|
||||
#define ROUTER_H_INCLUDED
|
||||
#ifndef RTCOMPAT_ROUTER_H_INCLUDED
|
||||
#define RTCOMPAT_ROUTER_H_INCLUDED
|
||||
|
||||
#include "platform/hal_types.h"
|
||||
|
||||
#include "ud3tn/bundle.h"
|
||||
#include "ud3tn/common.h"
|
||||
#include "ud3tn/contact_manager.h"
|
||||
#include "ud3tn/node.h"
|
||||
#include "ud3tn/routing_table.h"
|
||||
|
||||
#include "routing/compat/contact_manager.h"
|
||||
#include "routing/compat/node.h"
|
||||
#include "routing/compat/routing_table.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Maximum number of fragments created by the router.
|
||||
#ifndef ROUTER_MAX_FRAGMENTS
|
||||
#define ROUTER_MAX_FRAGMENTS 10
|
||||
#define ROUTER_MAX_FRAGMENTS 512
|
||||
#endif // ROUTER_MAX_FRAGMENTS
|
||||
|
||||
// Default maximum bundle size.
|
||||
|
|
@ -28,29 +29,20 @@
|
|||
#define FRAGMENT_MIN_PAYLOAD 8
|
||||
#endif // FRAGMENT_MIN_PAYLOAD
|
||||
|
||||
// Below this, the default route will be used
|
||||
#ifndef ROUTER_MIN_CONTACTS_HTAB
|
||||
#define ROUTER_MIN_CONTACTS_HTAB 10
|
||||
#endif // ROUTER_MIN_CONTACTS_HTAB
|
||||
|
||||
struct router_config {
|
||||
size_t global_mbs;
|
||||
uint16_t fragment_min_payload;
|
||||
uint8_t router_min_contacts_htab;
|
||||
uint8_t router_min_contacts_nbf;
|
||||
};
|
||||
|
||||
struct fragment_route {
|
||||
uint32_t payload_size;
|
||||
uint64_t payload_size;
|
||||
struct contact *contact;
|
||||
uint8_t preemption_improved;
|
||||
};
|
||||
|
||||
/* With MAX_FRAGMENTS = 3 and MAX_CONTACTS = 5: 92 bytes on stack */
|
||||
struct router_result {
|
||||
struct fragment_route fragment_results[ROUTER_MAX_FRAGMENTS];
|
||||
int32_t fragments;
|
||||
uint8_t preemption_improved;
|
||||
};
|
||||
|
||||
#define ROUTER_BUNDLE_PRIORITY(bundle) (bundle_get_routing_priority(bundle))
|
||||
|
|
@ -62,19 +54,22 @@ void router_update_config(struct router_config config);
|
|||
|
||||
struct contact_list *router_lookup_destination(char *dest);
|
||||
uint8_t router_calculate_fragment_route(
|
||||
struct fragment_route *res, uint32_t size,
|
||||
struct contact_list *contacts, uint32_t preprocessed_size,
|
||||
struct fragment_route *res, uint64_t size,
|
||||
struct contact_list *contacts, uint64_t preprocessed_size,
|
||||
enum bundle_routing_priority priority, uint64_t exp_time,
|
||||
struct contact **excluded_contacts, uint8_t excluded_contacts_count);
|
||||
|
||||
struct router_result router_get_first_route(struct bundle *bundle);
|
||||
struct router_result router_try_reuse(
|
||||
struct router_result route, struct bundle *bundle);
|
||||
struct router_result router_get_resched_route(
|
||||
struct bundle *bundle,
|
||||
const uint64_t frag_offset, const uint64_t frag_length);
|
||||
|
||||
enum ud3tn_result router_add_bundle_to_contact(
|
||||
struct contact *contact, struct bundle *b);
|
||||
struct contact *contact, struct bundle *b,
|
||||
uint64_t frag_offset, uint64_t frag_length);
|
||||
enum ud3tn_result router_remove_bundle_from_contact(
|
||||
struct contact *contact, struct bundle *bundle);
|
||||
struct contact *contact, struct bundle *bundle,
|
||||
uint64_t frag_offset, uint64_t frag_length);
|
||||
|
||||
/* BP-side API */
|
||||
|
||||
|
|
@ -91,23 +86,4 @@ struct router_command {
|
|||
struct node *data;
|
||||
};
|
||||
|
||||
struct bundle_tx_result {
|
||||
char *peer_cla_addr;
|
||||
struct bundle *bundle;
|
||||
};
|
||||
|
||||
enum router_result_status {
|
||||
ROUTER_RESULT_OK,
|
||||
ROUTER_RESULT_NO_ROUTE,
|
||||
ROUTER_RESULT_NO_TIMELY_CONTACTS,
|
||||
ROUTER_RESULT_NO_MEMORY,
|
||||
ROUTER_RESULT_EXPIRED,
|
||||
};
|
||||
|
||||
enum ud3tn_result router_process_command(
|
||||
struct router_command *command,
|
||||
struct rescheduling_handle rescheduler);
|
||||
enum router_result_status router_route_bundle(
|
||||
struct bundle *b);
|
||||
|
||||
#endif /* ROUTER_H_INCLUDED */
|
||||
#endif /* RTCOMPAT_ROUTER_H_INCLUDED */
|
||||
|
|
|
|||
24
include/routing/compat/router_agent.h
Normal file
24
include/routing/compat/router_agent.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#ifndef RTCOMPAT_ROUTER_AGENT_H_INCLUDED
|
||||
#define RTCOMPAT_ROUTER_AGENT_H_INCLUDED
|
||||
|
||||
#include "ud3tn/bundle_processor.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Default Agent IDs.
|
||||
#ifndef AGENT_ID_CONFIG_DTN
|
||||
#define AGENT_ID_CONFIG_DTN "config"
|
||||
#endif // AGENT_ID_CONFIG_DTN
|
||||
#ifndef AGENT_ID_CONFIG_IPN
|
||||
#define AGENT_ID_CONFIG_IPN "9000"
|
||||
#endif // AGENT_ID_CONFIG_IPN
|
||||
|
||||
int compat_router_agent_setup(
|
||||
const struct bundle_agent_interface *bundle_agent_interface,
|
||||
bool allow_remote_configuration,
|
||||
const char *aap2_admin_secret);
|
||||
|
||||
#endif // RTCOMPAT_ROUTER_AGENT_H_INCLUDED
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#ifndef ROUTINGTABLE_H_INCLUDED
|
||||
#define ROUTINGTABLE_H_INCLUDED
|
||||
#ifndef RTCOMPAT_ROUTINGTABLE_H_INCLUDED
|
||||
#define RTCOMPAT_ROUTINGTABLE_H_INCLUDED
|
||||
|
||||
#include "ud3tn/bundle.h"
|
||||
#include "ud3tn/node.h"
|
||||
#include "ud3tn/result.h"
|
||||
|
||||
#include "routing/compat/node.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
|
@ -21,12 +22,14 @@ struct node_table_entry {
|
|||
};
|
||||
|
||||
typedef void (*reschedule_func_t)(
|
||||
struct bundle *,
|
||||
const void *reschedule_func_context
|
||||
void *context,
|
||||
struct bundle *bundle,
|
||||
uint64_t frag_offset,
|
||||
uint64_t frag_length
|
||||
);
|
||||
struct rescheduling_handle {
|
||||
reschedule_func_t reschedule_func;
|
||||
const void *reschedule_func_context;
|
||||
void *reschedule_func_context;
|
||||
};
|
||||
|
||||
enum ud3tn_result routing_table_init(void);
|
||||
|
|
@ -34,10 +37,6 @@ void routing_table_free(void);
|
|||
|
||||
struct node *routing_table_lookup_node(const char *eid);
|
||||
struct node_table_entry *routing_table_lookup_eid(const char *eid);
|
||||
uint8_t routing_table_lookup_eid_in_nbf(
|
||||
char *eid, struct node **target, uint8_t max);
|
||||
uint8_t routing_table_lookup_hot_node(
|
||||
struct node **target, uint8_t max);
|
||||
|
||||
bool routing_table_add_node(
|
||||
struct node *new_node, struct rescheduling_handle rescheduler);
|
||||
|
|
@ -54,4 +53,4 @@ void routing_table_delete_contact(struct contact *contact);
|
|||
void routing_table_contact_passed(
|
||||
struct contact *contact, struct rescheduling_handle rescheduler);
|
||||
|
||||
#endif /* ROUTINGTABLE_H_INCLUDED */
|
||||
#endif // RTCOMPAT_ROUTINGTABLE_H_INCLUDED
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ struct ud3tn_cmdline_options {
|
|||
char *aap2_bdm_secret;
|
||||
uint8_t bundle_version;
|
||||
uint8_t log_level;
|
||||
bool external_dispatch;
|
||||
bool status_reporting;
|
||||
bool allow_remote_configuration;
|
||||
bool exit_immediately; // after parsing --help or --usage etc.
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ $(eval $(call addComponentWithRules,components/bundle7))
|
|||
$(eval $(call addComponentWithRules,components/cla))
|
||||
$(eval $(call addComponentWithRules,components/cla/$(PLATFORM)))
|
||||
$(eval $(call addComponentWithRules,components/platform/$(PLATFORM)))
|
||||
$(eval $(call addComponentWithRules,components/routing/compat))
|
||||
$(eval $(call addComponentWithRules,components/spp))
|
||||
$(eval $(call addComponentWithRules,components/ud3tn))
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,12 @@ let
|
|||
type = lib.types.str;
|
||||
};
|
||||
|
||||
externalDispatch = lib.mkOption {
|
||||
description = "Do not load the internal minimal router, allow for using an AAP 2.0 BDM.";
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
};
|
||||
|
||||
lifetime = lib.mkOption {
|
||||
description = "Lifetime in seconds of bundles created via AAP.";
|
||||
default = 86400;
|
||||
|
|
@ -184,6 +190,7 @@ let
|
|||
++ lib.optional (cfg.aap.host != null) "--aap-host ${cfg.aap.host}"
|
||||
++ lib.optional (cfg.aap.port != null) "--aap-port ${toString cfg.aap.port}"
|
||||
++ lib.optional cfg.allowRemoteConfig "--allow-remote-config"
|
||||
++ lib.optional cfg.externalDispatch "--external-dispatch"
|
||||
++ lib.optional cfg.statusReports "--status-reports"
|
||||
);
|
||||
};
|
||||
|
|
@ -234,6 +241,7 @@ in
|
|||
};
|
||||
};
|
||||
eid = "dtn://ud3tn-b.dtn/";
|
||||
externalDispatch = true;
|
||||
lifetime = 43200;
|
||||
logLevel = 3;
|
||||
maxBundleSize = 4294967295; # u32_max
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@
|
|||
logLevel = 3;
|
||||
maxBundleSize = 4294967295; # u32_max
|
||||
statusReports = true;
|
||||
externalDispatch = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -40,15 +40,9 @@ exit_handler() {
|
|||
echo ">>> uD3TN1 LOGFILE"
|
||||
cat "/tmp/ud3tn1.log" || true
|
||||
echo
|
||||
echo ">>> uD3TN1 DISP LOGFILE"
|
||||
cat "/tmp/disp_ud3tn1.log" || true
|
||||
echo
|
||||
echo ">>> uD3TN2 LOGFILE"
|
||||
cat "/tmp/ud3tn2.log" || true
|
||||
echo
|
||||
echo ">>> uD3TN2 DISP LOGFILE"
|
||||
cat "/tmp/disp_ud3tn2.log" || true
|
||||
echo
|
||||
echo ">>> DTN7 LOGFILE"
|
||||
cat "/tmp/dtn7.log" || true
|
||||
echo
|
||||
|
|
@ -63,20 +57,14 @@ trap exit_handler EXIT
|
|||
|
||||
# Start first uD3TN instance (uD3TN1)
|
||||
echo "Starting first uD3TN instance ..."
|
||||
"$UD3TN_DIR/build/posix/ud3tn" --eid "$UD3TN1_EID" --bp-version "$BP_VERSION" --aap-port 4242 -S "$UD3TN_DIR/ud3tn1.aap2.socket" --cla "sqlite:ud3tn1.sqlite;mtcp:127.0.0.1,4224" -L 4 > /tmp/ud3tn1.log 2>&1 &
|
||||
"$UD3TN_DIR/build/posix/ud3tn" --eid "$UD3TN1_EID" --bp-version "$BP_VERSION" --aap-port 4242 -S "$UD3TN_DIR/ud3tn1.aap2.socket" --cla "sqlite:ud3tn1.sqlite;mtcp:127.0.0.1,4224" -L 4 --allow-remote-config > /tmp/ud3tn1.log 2>&1 &
|
||||
UD3TN1_PID=$!
|
||||
|
||||
# Start second uD3TN instance (uD3TN2)
|
||||
echo "Starting second uD3TN instance ..."
|
||||
"$UD3TN_DIR/build/posix/ud3tn" --eid "$UD3TN2_EID" --bp-version "$BP_VERSION" --aap-port 4243 -S "$UD3TN_DIR/ud3tn2.aap2.socket" --cla "sqlite:ud3tn2.sqlite;mtcp:127.0.0.1,4225" -L 4 > /tmp/ud3tn2.log 2>&1 &
|
||||
"$UD3TN_DIR/build/posix/ud3tn" --eid "$UD3TN2_EID" --bp-version "$BP_VERSION" --aap-port 4243 -S "$UD3TN_DIR/ud3tn2.aap2.socket" --cla "sqlite:ud3tn2.sqlite;mtcp:127.0.0.1,4225" -L 4 --allow-remote-config > /tmp/ud3tn2.log 2>&1 &
|
||||
UD3TN2_PID=$!
|
||||
|
||||
sleep 1 # give ud3tn some time to start
|
||||
# Start compat. dispatchers for both instances
|
||||
aap2-bdm-ud3tn-routing -vv --insecure-config --socket "$UD3TN_DIR/ud3tn1.aap2.socket" > /tmp/disp_ud3tn1.log 2>&1 &
|
||||
aap2-bdm-ud3tn-routing -vv --insecure-config --socket "$UD3TN_DIR/ud3tn2.aap2.socket" > /tmp/disp_ud3tn2.log 2>&1 &
|
||||
sleep 1 # give the dispatchers some time to start
|
||||
|
||||
# Start DTN7 instance (along with contact to uD3TN1 via config file for later use)
|
||||
echo "Starting DTN7 instance ..."
|
||||
dtnd --nodeid "$DTN7_EID" --routing "$DTN7_ROUTING" --cla "$DTN7_CLA" --endpoint "$DTN7_ENDPOINT" --config "$DTN7_CONFIG_FILE" > /tmp/dtn7.log 2>&1 &
|
||||
|
|
|
|||
|
|
@ -25,27 +25,15 @@ exit_handler() {
|
|||
echo ">>> LOWER1 LOGFILE"
|
||||
cat "/tmp/lower1.log" || true
|
||||
echo
|
||||
echo ">>> LOWER1 DISP LOGFILE"
|
||||
cat "/tmp/disp_lower1.log" || true
|
||||
echo
|
||||
echo ">>> LOWER2 LOGFILE"
|
||||
cat "/tmp/lower2.log" || true
|
||||
echo
|
||||
echo ">>> LOWER2 DISP LOGFILE"
|
||||
cat "/tmp/disp_lower2.log" || true
|
||||
echo
|
||||
echo ">>> UPPER1 LOGFILE"
|
||||
cat "/tmp/upper1.log" || true
|
||||
echo
|
||||
echo ">>> UPPER1 DISP LOGFILE"
|
||||
cat "/tmp/disp_upper1.log" || true
|
||||
echo
|
||||
echo ">>> UPPER2 LOGFILE"
|
||||
cat "/tmp/upper2.log" || true
|
||||
echo
|
||||
echo ">>> UPPER2 DISP LOGFILE"
|
||||
cat "/tmp/disp_upper2.log" || true
|
||||
echo
|
||||
|
||||
echo "Waiting for uD3TN to exit gracefully - if it doesn't, check for sanitizer warnings."
|
||||
wait $UD3TN1_PID
|
||||
|
|
@ -57,32 +45,24 @@ exit_handler() {
|
|||
rm -f /tmp/*.log
|
||||
|
||||
# Start first uD3TN instance (lower1)
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4242 -S "$UD3TN_DIR/ud3tn1.aap2.socket" -e "dtn://lower1.dtn/" -c "sqlite:ud3tn1.sqlite;mtcp:127.0.0.1,4224" -L 4 > /tmp/lower1.log 2>&1 &
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4242 -S "$UD3TN_DIR/ud3tn1.aap2.socket" -e "dtn://lower1.dtn/" -c "sqlite:ud3tn1.sqlite;mtcp:127.0.0.1,4224" -L 4 --allow-remote-config > /tmp/lower1.log 2>&1 &
|
||||
UD3TN1_PID=$!
|
||||
sleep 1
|
||||
|
||||
# Start second uD3TN instance (upper1)
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4243 -S "$UD3TN_DIR/ud3tn2.aap2.socket" -e "dtn://upper1.dtn/" -c "sqlite:ud3tn2.sqlite;bibe:," -L 4 > /tmp/upper1.log 2>&1 &
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4243 -S "$UD3TN_DIR/ud3tn2.aap2.socket" -e "dtn://upper1.dtn/" -c "sqlite:ud3tn2.sqlite;bibe:," -L 4 --allow-remote-config > /tmp/upper1.log 2>&1 &
|
||||
UD3TN2_PID=$!
|
||||
sleep 1
|
||||
|
||||
# Start third uD3TN instance (lower2)
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4244 -S "$UD3TN_DIR/ud3tn3.aap2.socket" -e "dtn://lower2.dtn/" -c "sqlite:ud3tn3.sqlite;mtcp:127.0.0.1,4225" -L 4 > /tmp/lower2.log 2>&1 &
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4244 -S "$UD3TN_DIR/ud3tn3.aap2.socket" -e "dtn://lower2.dtn/" -c "sqlite:ud3tn3.sqlite;mtcp:127.0.0.1,4225" -L 4 --allow-remote-config > /tmp/lower2.log 2>&1 &
|
||||
UD3TN3_PID=$!
|
||||
sleep 1
|
||||
|
||||
# Start fourth uD3TN instance (upper2)
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4245 -S "$UD3TN_DIR/ud3tn4.aap2.socket" -e "dtn://upper2.dtn/" -c "sqlite:ud3tn4.sqlite;bibe:," -L 4 > /tmp/upper2.log 2>&1 &
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4245 -S "$UD3TN_DIR/ud3tn4.aap2.socket" -e "dtn://upper2.dtn/" -c "sqlite:ud3tn4.sqlite;bibe:," -L 4 --allow-remote-config > /tmp/upper2.log 2>&1 &
|
||||
UD3TN4_PID=$!
|
||||
|
||||
sleep 1 # give ud3tn some time to start
|
||||
# Start compat. dispatchers for all instances
|
||||
aap2-bdm-ud3tn-routing -vv --insecure-config --socket "$UD3TN_DIR/ud3tn1.aap2.socket" > /tmp/disp_lower1.log 2>&1 &
|
||||
aap2-bdm-ud3tn-routing -vv --insecure-config --socket "$UD3TN_DIR/ud3tn2.aap2.socket" > /tmp/disp_upper1.log 2>&1 &
|
||||
aap2-bdm-ud3tn-routing -vv --insecure-config --socket "$UD3TN_DIR/ud3tn3.aap2.socket" > /tmp/disp_lower2.log 2>&1 &
|
||||
aap2-bdm-ud3tn-routing -vv --insecure-config --socket "$UD3TN_DIR/ud3tn4.aap2.socket" > /tmp/disp_upper2.log 2>&1 &
|
||||
sleep 1 # give the dispatchers some time to start
|
||||
|
||||
trap exit_handler EXIT
|
||||
|
||||
# Configure contacts
|
||||
|
|
|
|||
|
|
@ -42,12 +42,12 @@ case "$1" in
|
|||
;;
|
||||
esac
|
||||
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -L 4 -e "dtn://ud3tn1.dtn/" $CLA_CONFIG > "$TEST_DIR/ud3tn1.log" 2>&1 &
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -L 4 --external-dispatch -e "dtn://ud3tn1.dtn/" $CLA_CONFIG > "$TEST_DIR/ud3tn1.log" 2>&1 &
|
||||
UD3TN1_PID=$!
|
||||
sleep 1
|
||||
|
||||
# Start second uD3TN instance (ud3tn2)
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -L 4 -e "dtn://ud3tn2.dtn/" -c "mtcp:127.0.0.1,4225" -s "$UD3TN_DIR/ud3tn2.aap.socket" -S "$UD3TN_DIR/ud3tn2.aap2.socket" > "$TEST_DIR/ud3tn2.log" 2>&1 &
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -L 4 --external-dispatch -e "dtn://ud3tn2.dtn/" -c "mtcp:127.0.0.1,4225" -s "$UD3TN_DIR/ud3tn2.aap.socket" -S "$UD3TN_DIR/ud3tn2.aap2.socket" > "$TEST_DIR/ud3tn2.log" 2>&1 &
|
||||
UD3TN2_PID=$!
|
||||
sleep 1
|
||||
|
||||
|
|
|
|||
|
|
@ -133,7 +133,8 @@ def test_re_scheduling_drop_smtcp_bundle7():
|
|||
incoming_eid,
|
||||
payload_data,
|
||||
))
|
||||
# Wait 1s (between ct), then, drop 1st contact -> re-scheduling
|
||||
# Wait 1s (between ct), then, drop 2nd contact first and 1st
|
||||
# contact afterwards -> re-scheduling becomes impossible -> drop
|
||||
time.sleep(1)
|
||||
_drop_contact(conn, serialize_func, c2)
|
||||
_drop_contact(conn, serialize_func, c1)
|
||||
|
|
|
|||
|
|
@ -26,27 +26,15 @@ exit_handler() {
|
|||
echo ">>> LOWER1 LOGFILE"
|
||||
cat "/tmp/lower1.log" || true
|
||||
echo
|
||||
echo ">>> LOWER1 DISP LOGFILE"
|
||||
cat "/tmp/disp_lower1.log" || true
|
||||
echo
|
||||
echo ">>> LOWER2 LOGFILE"
|
||||
cat "/tmp/lower2.log" || true
|
||||
echo
|
||||
echo ">>> LOWER2 DISP LOGFILE"
|
||||
cat "/tmp/disp_lower2.log" || true
|
||||
echo
|
||||
echo ">>> UPPER1 LOGFILE"
|
||||
cat "/tmp/upper1.log" || true
|
||||
echo
|
||||
echo ">>> UPPER1 DISP LOGFILE"
|
||||
cat "/tmp/disp_upper1.log" || true
|
||||
echo
|
||||
echo ">>> UPPER2 LOGFILE"
|
||||
cat "/tmp/upper2.log" || true
|
||||
echo
|
||||
echo ">>> UPPER2 DISP LOGFILE"
|
||||
cat "/tmp/disp_upper2.log" || true
|
||||
echo
|
||||
|
||||
echo "Waiting for uD3TN to exit gracefully - if it doesn't, check for sanitizer warnings."
|
||||
wait $UD3TN1_PID
|
||||
|
|
@ -59,29 +47,21 @@ rm -f ion.log
|
|||
rm -f /tmp/ion*log /tmp/ud3tn*.log
|
||||
|
||||
# Start first uD3TN instance (lower1)
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4242 -S "$UD3TN_DIR/ud3tn1.aap2.socket" -e dtn://lower1.dtn/ -c "sqlite:ud3tn1.sqlite;tcpclv3:127.0.0.1,4555;mtcp:127.0.0.1,4224" -L 4 > /tmp/lower1.log 2>&1 &
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4242 -S "$UD3TN_DIR/ud3tn1.aap2.socket" -e dtn://lower1.dtn/ -c "sqlite:ud3tn1.sqlite;tcpclv3:127.0.0.1,4555;mtcp:127.0.0.1,4224" -L 4 --allow-remote-config > /tmp/lower1.log 2>&1 &
|
||||
UD3TN1_PID=$!
|
||||
|
||||
# Start second uD3TN instance (upper1)
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4243 -S "$UD3TN_DIR/ud3tn2.aap2.socket" -e dtn://upper1.dtn/ -c "sqlite:ud3tn2.sqlite;bibe:," -L 4 > /tmp/upper1.log 2>&1 &
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4243 -S "$UD3TN_DIR/ud3tn2.aap2.socket" -e dtn://upper1.dtn/ -c "sqlite:ud3tn2.sqlite;bibe:," -L 4 --allow-remote-config > /tmp/upper1.log 2>&1 &
|
||||
UD3TN2_PID=$!
|
||||
|
||||
# Start third uD3TN instance (lower2)
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4244 -S "$UD3TN_DIR/ud3tn3.aap2.socket" -e dtn://lower2.dtn/ -c "sqlite:ud3tn3.sqlite;tcpclv3:127.0.0.1,4554" -L 4 > /tmp/lower2.log 2>&1 &
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4244 -S "$UD3TN_DIR/ud3tn3.aap2.socket" -e dtn://lower2.dtn/ -c "sqlite:ud3tn3.sqlite;tcpclv3:127.0.0.1,4554" -L 4 --allow-remote-config > /tmp/lower2.log 2>&1 &
|
||||
UD3TN3_PID=$!
|
||||
|
||||
# Start fourth uD3TN instance (upper2)
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4245 -S "$UD3TN_DIR/ud3tn4.aap2.socket" -e dtn://upper2.dtn/ -c "sqlite:ud3tn4.sqlite;bibe:," -L 4 > /tmp/upper2.log 2>&1 &
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -a localhost -p 4245 -S "$UD3TN_DIR/ud3tn4.aap2.socket" -e dtn://upper2.dtn/ -c "sqlite:ud3tn4.sqlite;bibe:," -L 4 --allow-remote-config > /tmp/upper2.log 2>&1 &
|
||||
UD3TN4_PID=$!
|
||||
|
||||
sleep 1 # give ud3tn some time to start
|
||||
# Start compat. dispatchers for all instances
|
||||
aap2-bdm-ud3tn-routing -vv --insecure-config --socket "$UD3TN_DIR/ud3tn1.aap2.socket" > /tmp/disp_lower1.log 2>&1 &
|
||||
aap2-bdm-ud3tn-routing -vv --insecure-config --socket "$UD3TN_DIR/ud3tn2.aap2.socket" > /tmp/disp_upper1.log 2>&1 &
|
||||
aap2-bdm-ud3tn-routing -vv --insecure-config --socket "$UD3TN_DIR/ud3tn3.aap2.socket" > /tmp/disp_lower2.log 2>&1 &
|
||||
aap2-bdm-ud3tn-routing -vv --insecure-config --socket "$UD3TN_DIR/ud3tn4.aap2.socket" > /tmp/disp_upper2.log 2>&1 &
|
||||
sleep 1 # give the dispatchers some time to start
|
||||
|
||||
# Start ION instance
|
||||
ulimit -n 512 # fix behavior on systems with a huge limit (e.g. if the container runtime does not change the kernel default), see: #121
|
||||
ionstart -I test/ion_interoperability/bibe_forwarding_test/ionstart.rc
|
||||
|
|
|
|||
|
|
@ -59,15 +59,9 @@ exit_handler() {
|
|||
echo ">>> uD3TN1 LOGFILE"
|
||||
cat "/tmp/ud3tn1.log" || true
|
||||
echo
|
||||
echo ">>> uD3TN1 DISP LOGFILE"
|
||||
cat "/tmp/disp_ud3tn1.log" || true
|
||||
echo
|
||||
echo ">>> uD3TN2 LOGFILE"
|
||||
cat "/tmp/ud3tn2.log" || true
|
||||
echo
|
||||
echo ">>> uD3TN2 DISP LOGFILE"
|
||||
cat "/tmp/disp_ud3tn2.log" || true
|
||||
echo
|
||||
|
||||
echo "Waiting for uD3TN to exit gracefully - if it doesn't, check for sanitizer warnings."
|
||||
wait $UD3TN1_PID
|
||||
|
|
@ -78,19 +72,13 @@ rm -f ion.log
|
|||
rm -f /tmp/ion*log /tmp/ud3tn*.log
|
||||
|
||||
# Start first uD3TN instance (uD3TN1)
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -s "$UD3TN_DIR/ud3tn1.socket" -S "$UD3TN_DIR/ud3tn1.aap2.socket" -c "sqlite:ud3tn1.sqlite;tcpclv3:127.0.0.1,4555" -b $BP_VERSION -e "$UD3TN1_EID" -L 4 > /tmp/ud3tn1.log 2>&1 &
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -s "$UD3TN_DIR/ud3tn1.socket" -S "$UD3TN_DIR/ud3tn1.aap2.socket" -c "sqlite:ud3tn1.sqlite;tcpclv3:127.0.0.1,4555" -b $BP_VERSION -e "$UD3TN1_EID" -L 4 --allow-remote-config > /tmp/ud3tn1.log 2>&1 &
|
||||
UD3TN1_PID=$!
|
||||
|
||||
# Start second uD3TN instance (uD3TN2)
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -s "$UD3TN_DIR/ud3tn2.socket" -S "$UD3TN_DIR/ud3tn2.aap2.socket" -c "sqlite:ud3tn2.sqlite;tcpclv3:127.0.0.1,4554" -b $BP_VERSION -e "$UD3TN2_EID" -L 4 > /tmp/ud3tn2.log 2>&1 &
|
||||
"$UD3TN_DIR/build/posix/ud3tn" -s "$UD3TN_DIR/ud3tn2.socket" -S "$UD3TN_DIR/ud3tn2.aap2.socket" -c "sqlite:ud3tn2.sqlite;tcpclv3:127.0.0.1,4554" -b $BP_VERSION -e "$UD3TN2_EID" -L 4 --allow-remote-config > /tmp/ud3tn2.log 2>&1 &
|
||||
UD3TN2_PID=$!
|
||||
|
||||
sleep 1 # give ud3tn some time to start
|
||||
# Start compat. dispatchers for all instances
|
||||
aap2-bdm-ud3tn-routing -vv --socket "$UD3TN_DIR/ud3tn1.aap2.socket" > /tmp/disp_ud3tn1.log 2>&1 &
|
||||
aap2-bdm-ud3tn-routing -vv --socket "$UD3TN_DIR/ud3tn2.aap2.socket" > /tmp/disp_ud3tn2.log 2>&1 &
|
||||
sleep 1 # give the dispatchers some time to start
|
||||
|
||||
# Start ION instance
|
||||
ulimit -n 512 # fix behavior on systems with a huge limit (e.g. if the container runtime does not change the kernel default), see: #121
|
||||
ionstart -I test/ion_interoperability/minimal_forwarding_test/$ION_SCRIPT_NAME
|
||||
|
|
|
|||
|
|
@ -43,15 +43,9 @@ exit_handler() {
|
|||
echo ">>> uD3TN1 LOGFILE"
|
||||
cat "/tmp/ud3tn1.log" || true
|
||||
echo
|
||||
echo ">>> uD3TN1 DISP LOGFILE"
|
||||
cat "/tmp/disp_ud3tn1.log" || true
|
||||
echo
|
||||
echo ">>> uD3TN2 LOGFILE"
|
||||
cat "/tmp/ud3tn2.log" || true
|
||||
echo
|
||||
echo ">>> uD3TN2 DISP LOGFILE"
|
||||
cat "/tmp/disp_ud3tn2.log" || true
|
||||
echo
|
||||
|
||||
rm -f -r $TMPDIR
|
||||
}
|
||||
|
|
@ -68,12 +62,6 @@ UD3TN1_PID=$!
|
|||
"$UD3TN_DIR/build/posix/ud3tn" -s $UD3TN_DIR/ud3tn2.socket -S $UD3TN_DIR/ud3tn2.aap2.socket -c "sqlite:ud3tn2.sqlite;tcpclv3:127.0.0.1,4558" -b $BP_VERSION -e "$UD3TN2_EID" -L 4 > /tmp/ud3tn2.log 2>&1 &
|
||||
UD3TN2_PID=$!
|
||||
|
||||
sleep 1 # give ud3tn some time to start
|
||||
# Start compat. dispatchers for both instances
|
||||
aap2-bdm-ud3tn-routing -vv --insecure-config --socket "$UD3TN_DIR/ud3tn1.aap2.socket" > /tmp/disp_ud3tn1.log 2>&1 &
|
||||
aap2-bdm-ud3tn-routing -vv --insecure-config --socket "$UD3TN_DIR/ud3tn2.aap2.socket" > /tmp/disp_ud3tn2.log 2>&1 &
|
||||
sleep 1 # give the dispatchers some time to start
|
||||
|
||||
# Start HDTN
|
||||
"$HDTN_SOURCE_ROOT/build/module/hdtn_one_process/hdtn-one-process" --hdtn-config-file=$HDTN_CONFIG_FILE --contact-plan-file=$HDTN_CONTACT_PLAN > /tmp/hdtn.log 2>&1 &
|
||||
HDTN_PID=$!
|
||||
|
|
@ -82,7 +70,7 @@ trap exit_handler EXIT
|
|||
|
||||
# Configure contact to HDTN in uD3TN1 which allows to reach uD3TN2
|
||||
sleep 0.5
|
||||
aap-config --socket $UD3TN_DIR/ud3tn1.socket --schedule 1 3600 10000 --reaches "$UD3TN2_EID" "$HDTN_EID" tcpclv3:127.0.0.1:4556
|
||||
aap2-config --socket $UD3TN_DIR/ud3tn1.aap2.socket --schedule 1 3600 10000 --reaches "$UD3TN2_EID" "$HDTN_EID" tcpclv3:127.0.0.1:4556
|
||||
|
||||
# Send a bundle to uD3TN1, addressed to uD3TN2
|
||||
PAYLOAD="THISISTHEBUNDLEPAYLOAD"
|
||||
|
|
@ -108,7 +96,7 @@ $HDTN_SOURCE_ROOT/build/common/bpcodec/apps/bpreceivefile --save-directory=$TMPD
|
|||
HDTNSINK_PID=$!
|
||||
|
||||
# Configure contact to HDTN in uD3TN1 which allows to reach HDTN_bpreceivefile
|
||||
aap-config --socket $UD3TN_DIR/ud3tn1.socket --schedule 1 3600 10000 --reaches "$UD3TN2_EID" "$HDTN_EID" tcpclv3:127.0.0.1:4556
|
||||
aap2-config --socket $UD3TN_DIR/ud3tn1.aap2.socket --schedule 1 3600 10000 --reaches "$UD3TN2_EID" "$HDTN_EID" tcpclv3:127.0.0.1:4556
|
||||
|
||||
# Send bundle which contains `hdtn_payload.txt` to HDTN_bpreceivefile
|
||||
(sleep 2 && (cat $UD3TN_DIR/received_payload.txt | aap-send --socket $UD3TN_DIR/ud3tn1.socket --agentid "$SOURCE_AGENTID" "$SINK_EID"))&
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ export AAP2_ADM_SECRET
|
|||
set -e
|
||||
|
||||
if [[ -z "$1" || -z "$2" || -z "$3" || -z "$4" ]]; then
|
||||
echo "Usage: $0 <ud3tn-dir> <runas-user> <bundle-count> <bundle-size-bytes> [use-bdm (1|0)]" >&2
|
||||
echo "Usage: $0 <ud3tn-dir> <runas-user> <bundle-count> <bundle-size-bytes> [static|compat|bdm]" >&2
|
||||
fi
|
||||
|
||||
if [[ -z "$5" ]]; then
|
||||
bdm=0
|
||||
mode=static
|
||||
else
|
||||
bdm=$5
|
||||
mode=$5
|
||||
fi
|
||||
|
||||
if [[ "$EUID" -ne 0 ]]; then
|
||||
|
|
@ -42,7 +42,6 @@ WORK_DIR="$1"
|
|||
RUNAS_USER=$2
|
||||
BUNDLE_COUNT=$3
|
||||
BUNDLE_SIZE=$4
|
||||
USE_BDM=$bdm
|
||||
SOCK_DIR="$(mktemp -d)"
|
||||
|
||||
echo "Sockets and logs will be stored here: $SOCK_DIR" >&2
|
||||
|
|
@ -108,16 +107,22 @@ ip -netns $NS2 link set dev $VE2 up
|
|||
ip -netns $NS1 addr add "$IP1/24" dev $VE1
|
||||
ip -netns $NS2 addr add "$IP2/24" dev $VE2
|
||||
|
||||
ip netns exec $NS1 sudo -u $RUNAS_USER AAP2_ADM_SECRET=$AAP2_ADM_SECRET "$WORK_DIR/build/posix/ud3tn" $UFLAG --bdm-secret-var AAP2_ADM_SECRET -c "sqlite:$SOCK_DIR/ud3tn1.sqlite;mtcp:$IP1,4222" -e "dtn://ud3tn1.dtn/" -s "$SOCK_DIR/ud3tn1.socket" -S "$SOCK_DIR/ud3tn1.aap2.socket" > "$SOCK_DIR/ud3tn1.log" 2>&1 &
|
||||
if [[ $mode == "bdm" ]]; then
|
||||
EXT_DISPATCH_FLAG="--external-dispatch"
|
||||
else
|
||||
EXT_DISPATCH_FLAG=""
|
||||
fi
|
||||
|
||||
ip netns exec $NS1 sudo -u $RUNAS_USER AAP2_ADM_SECRET=$AAP2_ADM_SECRET "$WORK_DIR/build/posix/ud3tn" $UFLAG --bdm-secret-var AAP2_ADM_SECRET -c "sqlite:$SOCK_DIR/ud3tn1.sqlite;mtcp:$IP1,4222" -e "dtn://ud3tn1.dtn/" -s "$SOCK_DIR/ud3tn1.socket" -S "$SOCK_DIR/ud3tn1.aap2.socket" $EXT_DISPATCH_FLAG > "$SOCK_DIR/ud3tn1.log" 2>&1 &
|
||||
UD3TN_1=$!
|
||||
ip netns exec $NS2 sudo -u $RUNAS_USER AAP2_ADM_SECRET=$AAP2_ADM_SECRET "$WORK_DIR/build/posix/ud3tn" $UFLAG --bdm-secret-var AAP2_ADM_SECRET -c "sqlite:$SOCK_DIR/ud3tn2.sqlite;mtcp:$IP2,4222" -e "dtn://ud3tn2.dtn/" -s "$SOCK_DIR/ud3tn2.socket" -S "$SOCK_DIR/ud3tn2.aap2.socket" > "$SOCK_DIR/ud3tn2.log" 2>&1 &
|
||||
ip netns exec $NS2 sudo -u $RUNAS_USER AAP2_ADM_SECRET=$AAP2_ADM_SECRET "$WORK_DIR/build/posix/ud3tn" $UFLAG --bdm-secret-var AAP2_ADM_SECRET -c "sqlite:$SOCK_DIR/ud3tn2.sqlite;mtcp:$IP2,4222" -e "dtn://ud3tn2.dtn/" -s "$SOCK_DIR/ud3tn2.socket" -S "$SOCK_DIR/ud3tn2.aap2.socket" $EXT_DISPATCH_FLAG > "$SOCK_DIR/ud3tn2.log" 2>&1 &
|
||||
UD3TN_2=$!
|
||||
ip netns exec $NS1 sudo -u $RUNAS_USER AAP2_ADM_SECRET=$AAP2_ADM_SECRET "$WORK_DIR/build/posix/ud3tn" $UFLAG --bdm-secret-var AAP2_ADM_SECRET -c "sqlite:$SOCK_DIR/ud3tn3.sqlite;mtcp:$IP1,4223" -e "dtn://ud3tn3.dtn/" -s "$SOCK_DIR/ud3tn3.socket" -S "$SOCK_DIR/ud3tn3.aap2.socket" > "$SOCK_DIR/ud3tn3.log" 2>&1 &
|
||||
ip netns exec $NS1 sudo -u $RUNAS_USER AAP2_ADM_SECRET=$AAP2_ADM_SECRET "$WORK_DIR/build/posix/ud3tn" $UFLAG --bdm-secret-var AAP2_ADM_SECRET -c "sqlite:$SOCK_DIR/ud3tn3.sqlite;mtcp:$IP1,4223" -e "dtn://ud3tn3.dtn/" -s "$SOCK_DIR/ud3tn3.socket" -S "$SOCK_DIR/ud3tn3.aap2.socket" $EXT_DISPATCH_FLAG > "$SOCK_DIR/ud3tn3.log" 2>&1 &
|
||||
UD3TN_3=$!
|
||||
|
||||
sleep 1 # give ud3tn some time to start
|
||||
|
||||
if [[ $USE_BDM == 1 ]]; then
|
||||
if [[ $mode == "bdm" ]]; then
|
||||
|
||||
echo "MODE: BDM CONTACT DISPATCH" >&2
|
||||
# Start BDMs and wait a bit for them to start
|
||||
|
|
@ -137,11 +142,24 @@ if [[ $USE_BDM == 1 ]]; then
|
|||
|
||||
else
|
||||
|
||||
echo "MODE: FIB DISPATCH" >&2
|
||||
echo "Configuring FIB links to send bundles in a loop." >&2
|
||||
"$WORK_DIR/.venv/bin/python" "$WORK_DIR/python-ud3tn-utils/ud3tn_utils/aap2/bin/aap2_configure_link.py" --socket "$SOCK_DIR/ud3tn1.aap2.socket" --secret-var AAP2_ADM_SECRET ipn:1.0 "mtcp:$IP2:4222" $PYFLAG > /dev/null
|
||||
"$WORK_DIR/.venv/bin/python" "$WORK_DIR/python-ud3tn-utils/ud3tn_utils/aap2/bin/aap2_configure_link.py" --socket "$SOCK_DIR/ud3tn2.aap2.socket" --secret-var AAP2_ADM_SECRET ipn:1.0 "mtcp:$IP1:4222" $PYFLAG > /dev/null
|
||||
"$WORK_DIR/.venv/bin/python" "$WORK_DIR/python-ud3tn-utils/ud3tn_utils/aap2/bin/aap2_configure_link.py" --socket "$SOCK_DIR/ud3tn3.aap2.socket" --secret-var AAP2_ADM_SECRET ipn:1.0 "mtcp:$IP1:4222" $PYFLAG > /dev/null
|
||||
if [[ $mode == "compat" ]]; then
|
||||
|
||||
echo "MODE: COMPAT ROUTER DISPATCH" >&2
|
||||
# Configure
|
||||
echo "Configuring contacts to send bundles in a loop." >&2
|
||||
"$WORK_DIR/.venv/bin/python" "$WORK_DIR/python-ud3tn-utils/ud3tn_utils/aap2/bin/aap2_config.py" --socket "$SOCK_DIR/ud3tn1.aap2.socket" --secret-var AAP2_ADM_SECRET --schedule 3 100000000 10000000000000 --reaches ipn:1.1 dtn://ud3tn2.dtn/ "mtcp:$IP2:4222" $PYFLAG > /dev/null
|
||||
"$WORK_DIR/.venv/bin/python" "$WORK_DIR/python-ud3tn-utils/ud3tn_utils/aap2/bin/aap2_config.py" --socket "$SOCK_DIR/ud3tn2.aap2.socket" --secret-var AAP2_ADM_SECRET --schedule 1 100000000 10000000000000 --reaches ipn:1.1 dtn://ud3tn1.dtn/ "mtcp:$IP1:4222" $PYFLAG > /dev/null
|
||||
"$WORK_DIR/.venv/bin/python" "$WORK_DIR/python-ud3tn-utils/ud3tn_utils/aap2/bin/aap2_config.py" --socket "$SOCK_DIR/ud3tn3.aap2.socket" --secret-var AAP2_ADM_SECRET --schedule 1 100000000 10000000000000 --reaches ipn:1.1 dtn://ud3tn1.dtn/ "mtcp:$IP1:4222" $PYFLAG > /dev/null
|
||||
|
||||
else
|
||||
|
||||
echo "MODE: FIB DISPATCH" >&2
|
||||
echo "Configuring FIB links to send bundles in a loop." >&2
|
||||
"$WORK_DIR/.venv/bin/python" "$WORK_DIR/python-ud3tn-utils/ud3tn_utils/aap2/bin/aap2_configure_link.py" --socket "$SOCK_DIR/ud3tn1.aap2.socket" --secret-var AAP2_ADM_SECRET ipn:1.0 "mtcp:$IP2:4222" $PYFLAG > /dev/null
|
||||
"$WORK_DIR/.venv/bin/python" "$WORK_DIR/python-ud3tn-utils/ud3tn_utils/aap2/bin/aap2_configure_link.py" --socket "$SOCK_DIR/ud3tn2.aap2.socket" --secret-var AAP2_ADM_SECRET ipn:1.0 "mtcp:$IP1:4222" $PYFLAG > /dev/null
|
||||
"$WORK_DIR/.venv/bin/python" "$WORK_DIR/python-ud3tn-utils/ud3tn_utils/aap2/bin/aap2_configure_link.py" --socket "$SOCK_DIR/ud3tn3.aap2.socket" --secret-var AAP2_ADM_SECRET ipn:1.0 "mtcp:$IP1:4222" $PYFLAG > /dev/null
|
||||
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ void testud3tn(void)
|
|||
RUN_TEST_GROUP(bibe_parser);
|
||||
RUN_TEST_GROUP(bibe_validation);
|
||||
RUN_TEST_GROUP(bundle);
|
||||
RUN_TEST_GROUP(node);
|
||||
RUN_TEST_GROUP(routingTable);
|
||||
#ifdef PLATFORM_POSIX
|
||||
RUN_TEST_GROUP(simple_queue);
|
||||
#endif // PLATFORM_POSIX
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#include "ud3tn/node.h"
|
||||
#include "routing/compat/node.h"
|
||||
|
||||
#include "platform/hal_time.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
||||
#include "ud3tn/bundle_processor.h"
|
||||
#include "ud3tn/node.h"
|
||||
#include "ud3tn/routing_table.h"
|
||||
#include "routing/compat/node.h"
|
||||
#include "routing/compat/routing_table.h"
|
||||
|
||||
#include "util/llsort.h"
|
||||
|
||||
|
|
@ -41,10 +41,14 @@ static struct contact *c1, *c2, *c3, *c5, *c6, *c7,
|
|||
|
||||
static struct rescheduling_handle rescheduler;
|
||||
|
||||
static void rescheduling_mock(struct bundle *b, const void *ctx)
|
||||
static void rescheduling_mock(
|
||||
void *ctx, struct bundle *b,
|
||||
uint64_t frag_offset, uint64_t frag_length)
|
||||
{
|
||||
(void)b;
|
||||
(void)ctx;
|
||||
(void)frag_offset;
|
||||
(void)frag_length;
|
||||
}
|
||||
|
||||
TEST_SETUP(routingTable)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue