From fb995db48938b4d4bde6b1ab50abd2fff1e5ac6c Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 23 Oct 2026 23:50:39 +0200 Subject: [PATCH] Cover UDP accept cancellation with surviving paths and NCD owners --- tests/test_etcp_init_liveness.c | 90 +++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 10 deletions(-) diff --git a/tests/test_etcp_init_liveness.c b/tests/test_etcp_init_liveness.c index 847e7aa4..fe0fe5fc 100644 --- a/tests/test_etcp_init_liveness.c +++ b/tests/test_etcp_init_liveness.c @@ -6,6 +6,8 @@ #include "etcp_api.h" #include "etcp_connections.h" #include "etcp_keepalive.h" +#include "node_conn_direct.h" +#include "routing_layer/topo_node.h" #include "utun_instance.h" #include "test_clock.h" #include "test_net.h" @@ -87,6 +89,18 @@ static struct ETCP_LINK* first_link(unsigned side) { static void run_for(unsigned tb) { test_clock_run_for(ua, tb); } +static void start_source(unsigned count) { + ep[0].conn = etcp_connection_create(ep[0].inst, "liveness"); CHECK(ep[0].conn); + ep[0].conn->peer_node_id = ep[1].inst->node_id; + CHECK(sc_init_ctx(&ep[0].conn->crypto_ctx, &ep[0].inst->my_keys) == SC_OK); + CHECK(sc_set_peer_public_key(&ep[0].conn->crypto_ctx, ep[1].inst->my_keys.public_key, SC_PEER_PUBKEY_BIN) == SC_OK); + etcp_conn_add_cbk(ep[0].conn, conn_event, &ep[0], ETCP_CBK_EVENT_UP | ETCP_CBK_EVENT_DOWN | ETCP_CBK_EVENT_REINIT); + for (unsigned i = 0; i < count; i++) { + struct ETCP_SOCKET* socket = i ? test_etcp_bound_socket(ep[0].inst, "extra") : ep[0].socket; + CHECK(etcp_link_new(ep[0].conn, socket, &ep[1].socket->interface_addr, 0)); + } +} + /* Только ядро и одна явно выбранная UDP-пара: BGP/NCD не создают обходных путей. */ static void setup(unsigned swapped, unsigned count) { memset(ep, 0, sizeof(ep)); close_action = close_fired = late_handshakes = 0; @@ -105,16 +119,7 @@ static void setup(unsigned swapped, unsigned count) { CHECK(etcp_bind(ep[side].inst, 253, receive) == 0); } if (!count) return; /* Отдельная проверка входящего INIT до создания транспорта. */ - ep[0].conn = etcp_connection_create(ep[0].inst, "liveness"); CHECK(ep[0].conn); - ep[0].conn->peer_node_id = ep[1].inst->node_id; - CHECK(sc_init_ctx(&ep[0].conn->crypto_ctx, &ep[0].inst->my_keys) == SC_OK); - CHECK(sc_set_peer_public_key(&ep[0].conn->crypto_ctx, ep[1].inst->my_keys.public_key, SC_PEER_PUBKEY_BIN) == SC_OK); - etcp_conn_add_cbk(ep[0].conn, conn_event, &ep[0], ETCP_CBK_EVENT_UP | ETCP_CBK_EVENT_DOWN | ETCP_CBK_EVENT_REINIT); - for (unsigned i = 0; i < count; i++) { - struct ETCP_SOCKET* socket = ep[0].socket; - if (i) socket = test_etcp_bound_socket(ep[0].inst, "extra"); - CHECK(etcp_link_new(ep[0].conn, socket, &ep[1].socket->interface_addr, 0)); - } + start_source(count); uint64_t end = get_time_tb() + 30000; while (get_time_tb() < end) { test_clock_poll(ua, 10); @@ -225,6 +230,69 @@ static void send_init_request_body(const struct ETCP_INIT_REQUEST_PKT *request, etcp_connections_read_callback_socket(ep[1].socket->fd, ep[1].socket); } +struct accept_case { + struct NODE_CONN_DIRECT *owner; + struct ETCP_LINK *replacement; + unsigned with_owner, with_link, fired, indexed, closed; + int open_result; +}; +static void accept_owner_event(struct NODE_CONN_DIRECT *h, enum ncd_event event, void *arg) { + (void)h; + struct accept_case *test = arg; + if (event == NCD_EVENT_CLOSED) test->closed++; +} +static void cancel_accept(struct ETCP_CONN *conn, struct ETCP_LINK *link, int old_state, int old_status, void *arg) { + (void)old_status; + struct accept_case *test = arg; + if (test->fired || old_state != -1 || !link->is_server) return; + test->fired++; + ep[1].conn = conn; CHECK(etcp_conn_ref_take(conn) == 0); + test->indexed = instance_find_conn(conn->instance, conn->peer_node_id) == conn; + struct ETCP_SOCKET *socket = link->conn; + struct sockaddr_storage remote = link->remote_addr; + etcp_link_close(link); + if (test->with_link) { test->replacement = etcp_link_new(conn, socket, &remote, 0); CHECK(test->replacement); } + if (test->with_owner) { + /* Адресов пока нет: владелец удерживает входящий транспорт и ждёт будущего линка. */ + struct TOPO_NODE peer = {.node_id = ep[0].inst->node_id}; + memcpy(peer.public_key, ep[0].inst->my_keys.public_key, SC_PUBKEY_SIZE); + test->open_result = node_conn_direct_open_node(conn->instance, peer.node_id, accept_owner_event, test, + &test->owner, &peer, socket); + CHECK(test->open_result != NCD_ERR && test->owner); + } +} +static void cancelled_accept(unsigned swapped, unsigned with_owner, unsigned with_link) { + setup(swapped, 0); + struct accept_case test = {.with_owner = with_owner, .with_link = with_link}; + etcp_add_link_status_cbk(ep[1].inst, cancel_accept, &test); + struct ETCP_INIT_REQUEST_PKT request = {.code = ETCP_INIT_REQUEST, .link_id = 1}; + for (unsigned i = 0; i < 8; i++) request.node_id[i] = ep[0].inst->node_id >> (56 - 8 * i); + request.mtu[0] = ep[0].socket->mtu >> 8; request.mtu[1] = ep[0].socket->mtu; + send_init_request_body(&request, sizeof(request), 0); + etcp_remove_link_status_cbk(ep[1].inst, cancel_accept, &test); + CHECK(test.fired == 1 && ep[1].conn); + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "UDP cancelled accept swapped=%u other_link=%u owner=%u: indexed=%u closing=%u owner_conn=%p conn=%p", + swapped, with_link, with_owner, test.indexed, ep[1].conn->close_requested, + (void*)node_conn_direct_get_conn(test.owner), (void*)ep[1].conn); + CHECK(test.indexed && !late_handshakes); + if (with_owner || with_link) { + CHECK(!ep[1].conn->close_requested && instance_find_conn(ep[1].inst, ep[0].inst->node_id) == ep[1].conn); + CHECK(ep[1].inst->connections->count == 1 && !test.closed); + if (with_owner) CHECK(test.open_result == NCD_REUSED && node_conn_direct_get_conn(test.owner) == ep[1].conn); + if (with_link) CHECK(ep[1].conn->links == test.replacement && !test.replacement->closing); + start_source(1); run_for(10000); + CHECK(ep[0].conn->links_up && ep[1].conn->links_up && !test.closed); + for (unsigned side = 0; side < 2; side++) { + struct ll_entry *entry = ll_alloc_lldgram(2); CHECK(entry); + entry->len = 2; entry->dgram[0] = 253; entry->dgram[1] = 17; + CHECK(etcp_send(ep[side].conn, entry) == 0); + } + run_for(10000); CHECK(ep[0].received == 1 && ep[1].received == 1); + } else CHECK(ep[1].conn->close_requested); + node_conn_direct_force_close(test.owner); + teardown(); +} + static void request_boundaries(unsigned established) { setup(0, established); unsigned drained = 0; uint8_t pending; @@ -481,6 +549,8 @@ int main(void) { debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_DEBUG); } size_t baseline = u_get_allocated_count(); + for (unsigned swapped = 0; swapped < 2; swapped++) for (unsigned owner = 0; owner < 2; owner++) + for (unsigned link = 0; link < 2; link++) { cancelled_accept(swapped, owner, link); check_memory(baseline); } for (unsigned swapped = 0; swapped < 2; swapped++) for (unsigned stage = 0; stage < 3; stage++) for (unsigned action = 0; action < 3; action++) { transition_callbacks(swapped, stage, action); check_memory(baseline); } for (unsigned established = 0; established < 2; established++) { request_boundaries(established); check_memory(baseline); }