You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
938 lines
33 KiB
938 lines
33 KiB
// test_etcp_router_unit.c — Юнит-тест etcp_router: reorder, seq, ACK, conn lifecycle |
|
// Без ETCP-соединений, без сокетов, без BGP. |
|
// Инжектит SVC_ROUTE пакеты напрямую через etcp_router_recv_cb из api_bindings. |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <time.h> |
|
#include <unistd.h> |
|
#include <sys/time.h> |
|
|
|
#include "etcp.h" |
|
#include "etcp_api.h" |
|
#include "etcp_router.h" |
|
#include "topo_group.h" |
|
#include "../src/utun_instance.h" |
|
#include "../lib/u_async.h" |
|
#include "../lib/ll_queue.h" |
|
#include "../lib/debug_config.h" |
|
#include "../lib/mem.h" |
|
|
|
#define TEST_SVC_ID 0x42 |
|
#define TEST_SVC_ID2 0x43 |
|
#define TEST_REMOTE_NODE 0xAAAA000000000001ULL |
|
#define TEST_REMOTE_NODE2 0xAAAA000000000002ULL |
|
|
|
// ======================== Test handler state ======================== |
|
static struct { |
|
int delivered; |
|
int errors; |
|
uint32_t expected_seq; |
|
uint32_t last_seq; |
|
uint8_t marker; |
|
} rx; |
|
|
|
static int g_notify_count = 0; |
|
|
|
static void test_handler(struct ETCP_CONN* conn, struct ll_entry* entry) { |
|
(void)conn; |
|
if (!entry || !entry->dgram || entry->len < 2) { |
|
g_notify_count++; |
|
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } |
|
return; |
|
} |
|
// close/restart: len==9 (svc_id + node_id), conn==NULL |
|
if (entry->len == 9 && !conn) { |
|
g_notify_count++; |
|
queue_dgram_free(entry); queue_entry_free(entry); |
|
return; |
|
} |
|
if (entry->dgram[1] != rx.marker) { rx.errors++; } else { rx.delivered++; rx.last_seq = rx.expected_seq; rx.expected_seq++; } |
|
queue_dgram_free(entry); queue_entry_free(entry); |
|
} |
|
|
|
static void rx_reset(uint8_t marker) { |
|
memset(&rx, 0, sizeof(rx)); |
|
rx.marker = marker; |
|
} |
|
|
|
// ======================== Inject helper ======================== |
|
static struct ETCP_CONN fake_conn; |
|
|
|
static void inject(etcp_recv_fn recv_cb, struct UTUN_INSTANCE* inst, |
|
uint64_t src, uint8_t svc_id, uint32_t seq, |
|
const uint8_t* pl, size_t pl_len, int is_ack, uint8_t flags) { |
|
struct SVC_ROUTE_HDR hdr; |
|
memset(&hdr, 0, sizeof(hdr)); |
|
hdr.cmd = ETCP_RT_ID_SVC_ROUTE; |
|
hdr.group_id = TOPO_GROUP_UTUN; |
|
hdr.dst_node_id = inst->node_id; |
|
hdr.src_node_id = src; |
|
hdr.seq = seq; |
|
hdr.svc_id = svc_id; |
|
hdr.flags = flags; |
|
|
|
size_t total = SVC_ROUTE_HDR_SIZE + (is_ack ? 0 : pl_len); |
|
struct ll_entry* e = queue_entry_new(0); |
|
if (!e) { printf(" inject: queue_entry_new failed\n"); return; } |
|
e->dgram = u_malloc(total); |
|
if (!e->dgram) { queue_entry_free(e); return; } |
|
memcpy(e->dgram, &hdr, SVC_ROUTE_HDR_SIZE); |
|
if (!is_ack && pl_len > 0) |
|
memcpy(e->dgram + SVC_ROUTE_HDR_SIZE, pl, pl_len); |
|
e->len = total; |
|
|
|
recv_cb(&fake_conn, e); |
|
} |
|
|
|
// ======================== Setup ======================== |
|
#define SETUP() do { \ |
|
ua = uasync_create(); \ |
|
if (!ua) { printf(" SETUP: uasync_create failed\n"); return -1; } \ |
|
memset(&inst, 0, sizeof(inst)); \ |
|
inst.ua = ua; \ |
|
inst.node_id = 0xBBBB000000000001ULL; \ |
|
debug_config_init(); \ |
|
debug_set_level(DEBUG_LEVEL_ERROR); \ |
|
debug_set_categories(DEBUG_CATEGORY_ALL); \ |
|
memset(&fake_conn, 0, sizeof(fake_conn)); \ |
|
fake_conn.instance = &inst; \ |
|
TESTASSERT(etcp_router_init(&inst) == 0); \ |
|
TESTASSERT(etcp_router_bind(&inst, TEST_SVC_ID, test_handler) == 0); \ |
|
recv_cb = inst.api_bindings.callbacks[ETCP_RT_ID_SVC_ROUTE]; \ |
|
TESTASSERT(recv_cb != NULL); \ |
|
} while(0) |
|
|
|
#define TEARDOWN() do { \ |
|
etcp_router_destroy(&inst); \ |
|
uasync_destroy(ua, 0); \ |
|
ua = NULL; \ |
|
} while(0) |
|
|
|
// ======================== Macros ======================== |
|
static int test_total = 0, test_passed = 0, test_failed = 0; |
|
|
|
#define TESTASSERT(cond) do { \ |
|
if (!(cond)) { printf(" FAIL: %s:%d — %s\n", __FILE__, __LINE__, #cond); return -1; } \ |
|
} while(0) |
|
|
|
#define TEST(name) do { \ |
|
test_total++; \ |
|
printf("TEST %d: %-50s ", test_total, name); fflush(stdout); \ |
|
} while(0) |
|
|
|
#define PASS() do { puts("PASS"); test_passed++; } while(0) |
|
#define FAIL(msg) do { printf("FAIL: %s\n", msg); test_failed++; return -1; } while(0) |
|
|
|
// ======================== Sign test infrastructure ======================== |
|
static int g_sign_ready = 0; |
|
static struct SC_MYKEYS g_sign_keys; |
|
static sc_context_t g_sign_ctx; |
|
static uint8_t g_sign_ed25519_pubkey[SC_PUBKEY_SIZE]; |
|
static const uint64_t SIGNER_NODE = 0xCCCC000000000001ULL; |
|
|
|
static int setup_sign_keys(void) { |
|
if (g_sign_ready) return 0; |
|
if (sc_generate_keypair(&g_sign_keys) != SC_OK) { printf(" setup_sign_keys: generate_keypair failed\n"); return -1; } |
|
sc_init_ctx(&g_sign_ctx, &g_sign_keys); |
|
if (sc_derive_ed25519_pubkey(g_sign_keys.private_key, g_sign_ed25519_pubkey) != SC_OK) { printf(" setup_sign_keys: derive_ed25519_pubkey failed\n"); return -1; } |
|
g_sign_ready = 1; |
|
return 0; |
|
} |
|
|
|
static void setup_bgp_for_sign_test(struct UTUN_INSTANCE* inst, int with_ed25519_key) { |
|
inst->topo_groups = u_calloc(1, sizeof(struct TOPO_GROUPS)); |
|
if (!inst->topo_groups) { printf(" setup_bgp: topo_groups alloc failed\n"); return; } |
|
inst->topo_groups->group_list = queue_new(inst->ua, 16, 0, 8, "group_list_test"); |
|
inst->topo_groups->node_registry = queue_new(inst->ua, BGP_NODES_HASH_SIZE, 0, 8, "node_registry_test"); |
|
|
|
struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_GROUP)); |
|
if (!qe) { printf(" setup_bgp: queue_entry_new failed\n"); return; } |
|
struct TOPO_GROUP* group = (struct TOPO_GROUP*)qe; |
|
memset((uint8_t*)group + sizeof(struct ll_entry), 0, sizeof(*group) - sizeof(struct ll_entry)); |
|
group->instance = inst; |
|
group->group_id = TOPO_GROUP_UTUN; |
|
group->nodes = queue_new(inst->ua, BGP_NODES_HASH_SIZE, |
|
offsetof(struct TOPO_GROUP_NODE, node_id) - sizeof(struct ll_entry), 8, "group_nodes_test"); |
|
if (!group->nodes) { printf(" setup_bgp: queue_new failed\n"); queue_entry_free(qe); return; } |
|
queue_data_put_with_index(inst->topo_groups->group_list, &group->ll); |
|
|
|
struct TOPO_GROUP_NODE* nq = u_calloc(1, sizeof(struct TOPO_GROUP_NODE)); |
|
if (!nq) { printf(" setup_bgp: nq alloc failed\n"); return; } |
|
nq->ll.size = sizeof(struct TOPO_GROUP_NODE) - sizeof(struct ll_entry); |
|
struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE)); |
|
if (!ni) { printf(" setup_bgp: ni alloc failed\n"); u_free(nq); return; } |
|
ni->group_ref_count = 1; ni->node_id = SIGNER_NODE; |
|
if (with_ed25519_key) memcpy(ni->ed25519_public_key, g_sign_ed25519_pubkey, SC_PUBKEY_SIZE); |
|
topo_node_registry_store(inst->topo_groups, ni); |
|
nq->node_id = ni->node_id; |
|
queue_data_put_with_index(group->nodes, &nq->ll); |
|
} |
|
|
|
static void teardown_bgp_for_sign_test(struct UTUN_INSTANCE* inst) { |
|
if (!inst || !inst->topo_groups) return; |
|
struct TOPO_GROUP* group = topo_groups_get_default(inst->topo_groups); |
|
if (group && group->nodes) { |
|
struct ll_entry* e; |
|
while ((e = queue_data_get(group->nodes)) != NULL) queue_entry_free(e); |
|
queue_free(group->nodes); |
|
group->nodes = NULL; |
|
} |
|
struct ll_entry* ge; |
|
while ((ge = queue_data_get(inst->topo_groups->group_list)) != NULL) queue_entry_free(ge); |
|
queue_free(inst->topo_groups->group_list); |
|
u_free(inst->topo_groups); |
|
inst->topo_groups = NULL; |
|
} |
|
|
|
static void inject_signed(etcp_recv_fn recv_cb, struct UTUN_INSTANCE* inst, |
|
uint64_t src, uint8_t svc_id, uint32_t seq, |
|
const uint8_t* pl, size_t pl_len, uint8_t flags, |
|
int tamper_sig) { |
|
size_t hdr_pl = SVC_ROUTE_HDR_SIZE + pl_len; |
|
size_t total = hdr_pl + SC_SIGN_SIZE; |
|
uint8_t* dgram = u_malloc(total); |
|
if (!dgram) { printf(" inject_signed: u_malloc failed\n"); return; } |
|
|
|
struct SVC_ROUTE_HDR* hdr = (struct SVC_ROUTE_HDR*)dgram; |
|
memset(hdr, 0, SVC_ROUTE_HDR_SIZE); |
|
hdr->cmd = ETCP_RT_ID_SVC_ROUTE; |
|
hdr->group_id = TOPO_GROUP_UTUN; |
|
hdr->dst_node_id = inst->node_id; |
|
hdr->src_node_id = src; |
|
hdr->seq = seq; |
|
hdr->svc_id = svc_id; |
|
hdr->flags = flags | ROUTER_FLAG_SIGNED; |
|
if (pl_len > 0) memcpy(dgram + SVC_ROUTE_HDR_SIZE, pl, pl_len); |
|
|
|
struct sc_stream_sign_state sign_state; |
|
if (sc_stream_sign_init(&g_sign_ctx, &sign_state) != SC_OK) { u_free(dgram); return; } |
|
if (sc_stream_sign_update(&sign_state, dgram, hdr_pl) != SC_OK) { sc_stream_sign_cleanup(&sign_state); u_free(dgram); return; } |
|
size_t sig_len = SC_SIGN_SIZE; |
|
if (sc_stream_sign_final(&sign_state, dgram + hdr_pl, &sig_len) != SC_OK) { u_free(dgram); return; } |
|
|
|
if (tamper_sig) dgram[hdr_pl] ^= 0x01; |
|
|
|
struct ll_entry* e = queue_entry_new(0); |
|
if (!e) { u_free(dgram); return; } |
|
e->dgram = dgram; |
|
e->len = total; |
|
recv_cb(&fake_conn, e); |
|
} |
|
|
|
// ======================== Tests ======================== |
|
|
|
static int test_init_destroy(void) { |
|
TEST("init/destroy"); |
|
struct UASYNC* ua = uasync_create(); |
|
if (!ua) FAIL("uasync_create"); |
|
struct UTUN_INSTANCE inst; |
|
memset(&inst, 0, sizeof(inst)); |
|
inst.ua = ua; |
|
inst.node_id = 1; |
|
debug_config_init(); |
|
debug_set_level(DEBUG_LEVEL_ERROR); |
|
debug_set_categories(DEBUG_CATEGORY_ALL); |
|
|
|
memset(&fake_conn, 0, sizeof(fake_conn)); |
|
fake_conn.instance = &inst; |
|
|
|
if (etcp_router_init(&inst) != 0) FAIL("init"); |
|
if (inst.router_conns == NULL) FAIL("router_conns NULL after init"); |
|
if (inst.api_bindings.callbacks[ETCP_RT_ID_SVC_ROUTE] == NULL) FAIL("recv_cb not bound"); |
|
|
|
etcp_router_destroy(&inst); |
|
if (inst.router_conns != NULL) FAIL("router_conns not NULL after destroy"); |
|
if (inst.api_bindings.callbacks[ETCP_RT_ID_SVC_ROUTE] != NULL) FAIL("recv_cb still bound"); |
|
|
|
uasync_destroy(ua, 0); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_bind_unbind(void) { |
|
TEST("bind/unbind API"); |
|
struct UASYNC* ua = uasync_create(); |
|
struct UTUN_INSTANCE inst; |
|
memset(&inst, 0, sizeof(inst)); |
|
inst.ua = ua; |
|
inst.node_id = 1; |
|
debug_config_init(); |
|
debug_set_level(DEBUG_LEVEL_ERROR); |
|
debug_set_categories(DEBUG_CATEGORY_ALL); |
|
|
|
if (etcp_router_init(&inst) != 0) FAIL("init"); |
|
|
|
// bind |
|
if (etcp_router_bind(&inst, 0xEE, test_handler) != 0) FAIL("bind"); |
|
if (inst.router_bindings.callbacks[0xEE] != test_handler) FAIL("cb not set"); |
|
// rebind (overwrite) |
|
if (etcp_router_bind(&inst, 0xEE, test_handler) != 0) FAIL("rebind"); |
|
// unbind |
|
if (etcp_router_unbind(&inst, 0xEE) != 0) FAIL("unbind"); |
|
if (inst.router_bindings.callbacks[0xEE] != NULL) FAIL("cb not cleared"); |
|
// double unbind |
|
if (etcp_router_unbind(&inst, 0xEE) == 0) FAIL("double unbind should fail"); |
|
// null args |
|
if (etcp_router_bind(NULL, 0, test_handler) == 0) FAIL("bind null inst"); |
|
etcp_router_destroy(&inst); |
|
uasync_destroy(ua, 0); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_conn_get_create(void) { |
|
TEST("conn_get — create"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
|
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("conn_get returned NULL"); |
|
if (c->remote_node_id != TEST_REMOTE_NODE) FAIL("wrong remote_node_id"); |
|
if (c->svc_id != TEST_SVC_ID) FAIL("wrong svc_id"); |
|
if (c->tx_seq != 0) FAIL("tx_seq != 0"); |
|
if (c->rx_seq != 0) FAIL("rx_seq != 0"); |
|
if (c->tx_acked != 0) FAIL("tx_acked != 0"); |
|
if (c->last_sent_ack_seq != 0) FAIL("last_sent_ack_seq != 0"); |
|
if (c->recv_q == NULL) FAIL("recv_q is NULL"); |
|
if (c->last_dgram_ts == 0) FAIL("last_dgram_ts not set"); |
|
if (c->ack_timer != NULL) FAIL("ack_timer set at creation"); |
|
|
|
etcp_router_conn_close(c); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_conn_get_reuse(void) { |
|
TEST("conn_get — reuse / different"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
|
|
struct ETCP_ROUTER_CONN* c1 = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c1) FAIL("c1 NULL"); |
|
struct ETCP_ROUTER_CONN* c2 = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (c1 != c2) FAIL("reuse: different pointers"); |
|
|
|
struct ETCP_ROUTER_CONN* c3 = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE2, TEST_SVC_ID); |
|
if (c3 == c1) FAIL("different remote, same pointer"); |
|
struct ETCP_ROUTER_CONN* c4 = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID2); |
|
if (c4 == c1) FAIL("different svc, same pointer"); |
|
|
|
etcp_router_conn_close(c1); |
|
etcp_router_conn_close(c3); |
|
etcp_router_conn_close(c4); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_conn_close(void) { |
|
TEST("conn_close — new after close"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
|
|
struct ETCP_ROUTER_CONN* c1 = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c1) FAIL("c1 NULL"); |
|
c1->tx_seq = 99; // mark |
|
etcp_router_conn_close(c1); |
|
struct ETCP_ROUTER_CONN* c2 = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c2) FAIL("c2 NULL"); |
|
if (c2->tx_seq != 0 || c2->rx_seq != 0) FAIL("not fresh after close+reopen"); |
|
|
|
etcp_router_conn_close(c2); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
// ======================== Seq/reorder tests ======================== |
|
|
|
static int test_in_order(void) { |
|
TEST("in-order delivery (seq 0,1,2)"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("conn_get failed"); |
|
|
|
rx_reset(0xAA); |
|
uint8_t data[] = { TEST_SVC_ID, 0xAA, 0 }; |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, 0); |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 1, data + 1, 2, 0, 0); |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 2, data + 1, 2, 0, 0); |
|
|
|
if (rx.delivered != 3) FAIL("delivered != 3"); |
|
if (rx.errors != 0) FAIL("errors != 0"); |
|
if (c->rx_seq != 3) FAIL("rx_seq != 3"); |
|
|
|
etcp_router_conn_close(c); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_out_of_order(void) { |
|
TEST("out-of-order (0,2,3 then 1 → assembly to 3)"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("conn_get failed"); |
|
|
|
rx_reset(0xBB); |
|
uint8_t data[] = { TEST_SVC_ID, 0xBB, 0 }; |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, 0); |
|
if (rx.delivered != 1) FAIL("after seq 0: delivered != 1"); |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 2, data + 1, 2, 0, 0); |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 3, data + 1, 2, 0, 0); |
|
if (rx.delivered != 1) FAIL("after seq 2,3: still delivered != 1 (gap at 1)"); |
|
|
|
// Fill the gap |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 1, data + 1, 2, 0, 0); |
|
if (rx.delivered != 4) FAIL("after seq 1 fill: delivered != 4"); |
|
if (rx.errors != 0) FAIL("errors != 0"); |
|
if (c->rx_seq != 4) FAIL("rx_seq != 4"); |
|
|
|
etcp_router_conn_close(c); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_duplicate_same(void) { |
|
TEST("duplicate — same seq injected twice"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("conn_get failed"); |
|
|
|
rx_reset(0xCC); |
|
uint8_t data[] = { TEST_SVC_ID, 0xCC, 0 }; |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, 0); |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 1, data + 1, 2, 0, 0); |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 1, data + 1, 2, 0, 0); // dup |
|
|
|
if (rx.delivered != 2) FAIL("delivered != 2 (dup dropped)"); |
|
if (c->rx_seq != 2) FAIL("rx_seq != 2"); |
|
|
|
etcp_router_conn_close(c); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_duplicate_past(void) { |
|
TEST("duplicate — past seq (already delivered)"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("conn_get failed"); |
|
|
|
rx_reset(0xDD); |
|
uint8_t data[] = { TEST_SVC_ID, 0xDD, 0 }; |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, 0); |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 1, data + 1, 2, 0, 0); |
|
if (rx.delivered != 2) FAIL("delivered != 2"); |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, 0); // past |
|
|
|
if (rx.delivered != 2) FAIL("delivered != 2 (past dup dropped)"); |
|
if (c->rx_seq != 2) FAIL("rx_seq != 2"); |
|
|
|
etcp_router_conn_close(c); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_out_of_bounds(void) { |
|
TEST("out of bounds — seq too far ahead"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("conn_get failed"); |
|
|
|
rx_reset(0xEE); |
|
uint8_t data[] = { TEST_SVC_ID, 0xEE, 0 }; |
|
/* first packet syncs rx_seq (peer_sync_done), then OOB check works */ |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, 0); |
|
uint32_t bad_seq = ROUTER_MAX_INFLIGHT + 2; |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, bad_seq, data + 1, 2, 0, 0); |
|
|
|
if (rx.delivered != 1) FAIL("first packet not delivered"); |
|
if (c->rx_seq != 1) FAIL("rx_seq should advance to 1 after first"); |
|
|
|
etcp_router_conn_close(c); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_circular_wrap(void) { |
|
TEST("32-bit circular wrap-around"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("conn_get failed"); |
|
|
|
c->rx_seq = 0xFFFFFFFE; |
|
rx_reset(0x11); |
|
rx.expected_seq = 0xFFFFFFFE; |
|
uint8_t data[] = { TEST_SVC_ID, 0x11, 0 }; |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0xFFFFFFFE, data + 1, 2, 0, 0); |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0xFFFFFFFF, data + 1, 2, 0, 0); |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, 0); |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 1, data + 1, 2, 0, 0); |
|
|
|
if (rx.delivered != 4) FAIL("delivered != 4 (wrap)"); |
|
if (c->rx_seq != 2) FAIL("rx_seq != 2 after wrap (wrapped)"); |
|
|
|
etcp_router_conn_close(c); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_data_integrity(void) { |
|
TEST("data integrity — payload preserved"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("conn_get failed"); |
|
|
|
rx_reset(0x77); |
|
uint8_t payload[10] = { 0x77, 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6, 0x07, 0x18, 0x29 }; |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0, payload, 10, 0, 0); |
|
|
|
if (rx.delivered != 1) FAIL("delivered != 1"); |
|
if (rx.errors != 0) FAIL("data mismatch"); |
|
|
|
etcp_router_conn_close(c); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_ack_receive(void) { |
|
TEST("ACK receive — tx_acked updated"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("conn_get failed"); |
|
|
|
rx_reset(0x00); |
|
// Send ACK: is_ack=1, seq=7 means remote has rx_seq=7 |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 7, NULL, 0, 1, 0); |
|
|
|
if (c->tx_acked != 7) FAIL("tx_acked not updated from ACK"); |
|
if (rx.delivered != 0) FAIL("ACK delivered to handler (should not)"); |
|
if (c->last_dgram_ts == 0) FAIL("last_dgram_ts not updated on ACK"); |
|
|
|
etcp_router_conn_close(c); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_ack_timer(void) { |
|
TEST("ACK timer scheduled on data receive"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("conn_get failed"); |
|
|
|
if (c->ack_timer != NULL) FAIL("ack_timer set before any data"); |
|
|
|
rx_reset(0x55); |
|
uint8_t data[] = { TEST_SVC_ID, 0x55, 0 }; |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, 0); |
|
|
|
if (c->ack_timer != NULL) FAIL("ack_timer set without BGP route"); |
|
if (c->last_sent_ack_seq != 0) FAIL("last_sent_ack_seq changed without BGP route"); |
|
|
|
etcp_router_conn_close(c); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_send_q(void) { |
|
TEST("send_q — inflight full queues, not lost"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("conn_get failed"); |
|
|
|
// Simulate full inflight: set tx_acked far behind tx_seq |
|
c->tx_seq = ROUTER_MAX_INFLIGHT; |
|
c->tx_acked = 0; |
|
if (c->send_q == NULL) FAIL("send_q is NULL"); |
|
|
|
// Send via conn_send — should queue, not drop |
|
uint8_t data[4] = { 1, 2, 3, 4 }; |
|
int ret = etcp_router_conn_send(c, data, sizeof(data)); |
|
if (ret != 0) FAIL("conn_send should return 0 (queued in send_q)"); |
|
if (c->send_blocked == 0) FAIL("send_blocked not set"); |
|
if (c->send_resume_timer == NULL) FAIL("send_resume_timer not set"); |
|
if (queue_entry_count(c->send_q) != 1) FAIL("send_q count != 1"); |
|
if (c->tx_seq != ROUTER_MAX_INFLIGHT) FAIL("tx_seq advanced while queued"); |
|
|
|
// Cancel timers manually |
|
if (c->send_resume_timer) { uasync_cancel_timeout(ua, c->send_resume_timer); c->send_resume_timer = NULL; } |
|
|
|
etcp_router_conn_close(c); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_loopback(void) { |
|
TEST("loopback — etcp_route_send to self"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
|
|
rx_reset(0x66); |
|
uint8_t buf[4] = { TEST_SVC_ID, 0x66, 0x00, 0x00 }; |
|
struct ll_entry* e = queue_entry_new(0); |
|
if (!e) FAIL("queue_entry_new"); |
|
e->dgram = u_malloc(4); |
|
if (!e->dgram) { queue_entry_free(e); FAIL("u_malloc"); } |
|
memcpy(e->dgram, buf, 4); |
|
e->len = 4; |
|
|
|
if (etcp_route_send(&inst, TOPO_GROUP_UTUN, inst.node_id, e, 0) != 0) FAIL("loopback send failed"); |
|
if (rx.delivered != 1) FAIL("loopback not delivered"); |
|
if (rx.errors != 0) FAIL("loopback data mismatch"); |
|
|
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_timestamp(void) { |
|
TEST("last_dgram_ts updated on data"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("conn_get failed"); |
|
|
|
uint16_t before = c->last_dgram_ts; |
|
|
|
rx_reset(0x99); |
|
uint8_t data[] = { TEST_SVC_ID, 0x99, 0 }; |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, 0); |
|
|
|
if (c->last_dgram_ts == 0) FAIL("last_dgram_ts zero after data"); |
|
// Cancel ack_timer |
|
if (c->ack_timer) { uasync_cancel_timeout(ua, c->ack_timer); c->ack_timer = NULL; } |
|
|
|
etcp_router_conn_close(c); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_conn_send_no_bgp(void) { |
|
TEST("conn_send — no BGP returns error"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("conn_get failed"); |
|
|
|
uint8_t data[4] = { 1, 2, 3, 4 }; |
|
int ret = etcp_router_conn_send(c, data, sizeof(data)); |
|
if (ret == 0) FAIL("conn_send should fail without BGP"); |
|
|
|
etcp_router_conn_close(c); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
// ======================== Helper: pump random data ======================== |
|
static int pump_data(etcp_recv_fn recv_cb, struct UTUN_INSTANCE* inst, |
|
uint8_t marker, int count, uint8_t flags) { |
|
uint8_t data[] = { TEST_SVC_ID, marker, 0 }; |
|
for (int i = 0; i < count; i++) |
|
inject(recv_cb, inst, TEST_REMOTE_NODE, TEST_SVC_ID, i, data + 1, 2, 0, flags); |
|
return count; |
|
} |
|
|
|
// ======================== Restart/session tests ======================== |
|
|
|
static int test_rst_flag(void) { |
|
TEST("RST flag — rconn destroyed, service notified"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
g_notify_count = 0; |
|
|
|
int n = rand() % 10 + 1; |
|
rx_reset(0xA1); |
|
pump_data(recv_cb, &inst, 0xA1, n, 0); |
|
if (rx.delivered != n) FAIL("initial pump failed"); |
|
|
|
uint8_t data[] = { TEST_SVC_ID, 0x01, 0 }; |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, n, data + 1, 2, 0, ROUTER_FLAG_RST); |
|
if (g_notify_count != 1) FAIL("RST should notify service"); |
|
|
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("new rconn should exist"); |
|
etcp_router_conn_close(c); |
|
|
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_client_restart(void) { |
|
TEST("client restart — START flag detected after random data"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
g_notify_count = 0; |
|
|
|
int n = rand() % 10 + 1; |
|
rx_reset(0xB1); |
|
pump_data(recv_cb, &inst, 0xB1, n, 0); |
|
if (rx.delivered != n) FAIL("initial pump failed"); |
|
|
|
rx_reset(0xB2); |
|
uint8_t data[] = { TEST_SVC_ID, 0xB2, 0 }; |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, ROUTER_FLAG_START); |
|
if (g_notify_count != 1) FAIL("restart not notified"); |
|
if (rx.delivered < 1) FAIL("data after restart not delivered"); |
|
|
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_sess_id_change(void) { |
|
TEST("sess_id change — 0→1 with START triggers restart"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
g_notify_count = 0; |
|
|
|
int n = rand() % 10 + 1; |
|
rx_reset(0xC1); |
|
pump_data(recv_cb, &inst, 0xC1, n, 0); |
|
if (rx.delivered != n) FAIL("initial pump failed"); |
|
|
|
rx_reset(0xC2); |
|
uint8_t data[] = { TEST_SVC_ID, 0xC2, 0 }; |
|
uint8_t f = (1 << ROUTER_SESS_ID_SHIFT) | ROUTER_FLAG_START; |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, f); |
|
if (g_notify_count != 1) FAIL("restart not notified"); |
|
if (rx.delivered < 1) FAIL("data after restart not delivered"); |
|
|
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_sess_id_wrap(void) { |
|
TEST("sess_id wrap — 3→0 with START triggers restart"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
g_notify_count = 0; |
|
|
|
int n = rand() % 10 + 1; |
|
uint8_t f3 = (3 << ROUTER_SESS_ID_SHIFT); |
|
rx_reset(0xD1); |
|
|
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("rconn not found"); |
|
c->peer_sess_id = 3; |
|
|
|
pump_data(recv_cb, &inst, 0xD1, n, f3); |
|
if (rx.delivered != n) FAIL("initial pump failed"); |
|
|
|
rx_reset(0xD2); |
|
g_notify_count = 0; |
|
uint8_t data[] = { TEST_SVC_ID, 0xD2, 0 }; |
|
uint8_t f0 = (0 << ROUTER_SESS_ID_SHIFT) | ROUTER_FLAG_START; |
|
inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, f0); |
|
if (g_notify_count != 1) FAIL("restart not notified on wrap"); |
|
if (rx.delivered < 1) FAIL("data after wrap not delivered"); |
|
|
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_server_reinit(void) { |
|
TEST("server reinit — new rconn gets start_sent=0 sess_id=0"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
|
|
int n = rand() % 10 + 1; |
|
pump_data(recv_cb, &inst, 0xE1, n, 0); |
|
|
|
etcp_router_destroy(&inst); |
|
memset(&inst.api_bindings, 0, sizeof(inst.api_bindings)); |
|
memset(&inst.router_bindings, 0, sizeof(inst.router_bindings)); |
|
if (etcp_router_init(&inst) != 0) FAIL("reinit failed"); |
|
etcp_router_bind(&inst, TEST_SVC_ID, test_handler); |
|
recv_cb = inst.api_bindings.callbacks[ETCP_RT_ID_SVC_ROUTE]; |
|
if (!recv_cb) FAIL("recv_cb not bound"); |
|
|
|
struct ETCP_ROUTER_CONN* c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); |
|
if (!c) FAIL("rconn after reinit"); |
|
if (c->start_sent != 0) FAIL("start_sent != 0 after reinit"); |
|
if (c->sess_id != 0) FAIL("sess_id != 0 after reinit"); |
|
|
|
etcp_router_conn_close(c); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
// ======================== Signed message tests ======================== |
|
|
|
static int test_sign_verify_ok(void) { |
|
TEST("signed packet — verify OK, delivered to handler"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
setup_bgp_for_sign_test(&inst, 1); |
|
|
|
rx_reset(0xF1); |
|
uint8_t data[] = { TEST_SVC_ID, 0xF1, 0 }; |
|
inject_signed(recv_cb, &inst, SIGNER_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, 0); |
|
|
|
if (rx.delivered != 1) FAIL("signed packet not delivered"); |
|
if (rx.errors != 0) FAIL("data mismatch on signed packet"); |
|
|
|
teardown_bgp_for_sign_test(&inst); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_sign_bad_signature(void) { |
|
TEST("signed packet — tampered signature → dropped"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
setup_bgp_for_sign_test(&inst, 1); |
|
|
|
rx_reset(0xF2); |
|
uint8_t data[] = { TEST_SVC_ID, 0xF2, 0 }; |
|
inject_signed(recv_cb, &inst, SIGNER_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, 1 /*tamper*/); |
|
|
|
if (rx.delivered != 0) FAIL("tampered signed packet should be dropped"); |
|
if (rx.errors != 0) FAIL("handler should not be called for bad signature"); |
|
|
|
teardown_bgp_for_sign_test(&inst); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_sign_unknown_node(void) { |
|
TEST("signed packet — sender not in routing table → dropped"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
setup_bgp_for_sign_test(&inst, 1); |
|
|
|
rx_reset(0xF3); |
|
uint8_t data[] = { TEST_SVC_ID, 0xF3, 0 }; |
|
uint64_t unknown = 0xDEAD000000000001ULL; |
|
inject_signed(recv_cb, &inst, unknown, TEST_SVC_ID, 0, data + 1, 2, 0, 0); |
|
|
|
if (rx.delivered != 0) FAIL("packet from unknown node should be dropped"); |
|
if (rx.errors != 0) FAIL("handler not called for unknown node"); |
|
|
|
teardown_bgp_for_sign_test(&inst); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_sign_no_ed25519_key(void) { |
|
TEST("signed packet — node in table but no Ed25519 key → dropped"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
setup_bgp_for_sign_test(&inst, 0 /*zero ed25519 key*/); |
|
|
|
rx_reset(0xF4); |
|
uint8_t data[] = { TEST_SVC_ID, 0xF4, 0 }; |
|
inject_signed(recv_cb, &inst, SIGNER_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, 0); |
|
|
|
if (rx.delivered != 0) FAIL("packet with zero ed25519 key should be dropped"); |
|
|
|
teardown_bgp_for_sign_test(&inst); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
static int test_sign_too_short(void) { |
|
TEST("signed packet — SIGNED flag but no signature bytes → dropped"); |
|
struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; |
|
SETUP(); |
|
setup_bgp_for_sign_test(&inst, 1); |
|
|
|
rx_reset(0xF5); |
|
struct SVC_ROUTE_HDR hdr; |
|
memset(&hdr, 0, sizeof(hdr)); |
|
hdr.cmd = ETCP_RT_ID_SVC_ROUTE; |
|
hdr.group_id = TOPO_GROUP_UTUN; |
|
hdr.dst_node_id = inst.node_id; |
|
hdr.src_node_id = SIGNER_NODE; |
|
hdr.seq = 0; |
|
hdr.svc_id = TEST_SVC_ID; |
|
hdr.flags = ROUTER_FLAG_SIGNED; |
|
uint8_t payload[] = { TEST_SVC_ID, 0xF5, 0 }; |
|
size_t total = SVC_ROUTE_HDR_SIZE + sizeof(payload); |
|
struct ll_entry* e = queue_entry_new(0); |
|
if (!e) FAIL("queue_entry_new"); |
|
e->dgram = u_malloc(total); |
|
if (!e->dgram) { queue_entry_free(e); FAIL("u_malloc"); } |
|
memcpy(e->dgram, &hdr, SVC_ROUTE_HDR_SIZE); |
|
memcpy(e->dgram + SVC_ROUTE_HDR_SIZE, payload, sizeof(payload)); |
|
e->len = total; |
|
recv_cb(&fake_conn, e); |
|
|
|
if (rx.delivered != 0) FAIL("too-short signed packet should be dropped"); |
|
|
|
teardown_bgp_for_sign_test(&inst); |
|
TEARDOWN(); |
|
PASS(); |
|
return 0; |
|
} |
|
|
|
// ======================== Main ======================== |
|
int main(void) { |
|
// Run all tests |
|
test_init_destroy(); |
|
test_bind_unbind(); |
|
test_conn_get_create(); |
|
test_conn_get_reuse(); |
|
test_conn_close(); |
|
test_in_order(); |
|
test_out_of_order(); |
|
test_duplicate_same(); |
|
test_duplicate_past(); |
|
test_out_of_bounds(); |
|
test_circular_wrap(); |
|
test_data_integrity(); |
|
test_ack_receive(); |
|
test_ack_timer(); |
|
test_send_q(); |
|
test_loopback(); |
|
test_timestamp(); |
|
test_conn_send_no_bgp(); |
|
test_rst_flag(); |
|
test_client_restart(); |
|
test_sess_id_change(); |
|
test_sess_id_wrap(); |
|
test_server_reinit(); |
|
|
|
if (setup_sign_keys() != 0) { printf(" SKIP signed tests — crypto init failed\n"); } |
|
else { |
|
test_sign_verify_ok(); |
|
test_sign_bad_signature(); |
|
test_sign_unknown_node(); |
|
test_sign_no_ed25519_key(); |
|
test_sign_too_short(); |
|
} |
|
|
|
printf("\n=== Results: %d/%d passed, %d failed ===\n", test_passed, test_total, test_failed); |
|
return test_failed > 0 ? 1 : 0; |
|
}
|
|
|