conn_mgr: verify route loss and document handle lifetime recovery
This commit is contained in:
1 parent
464575ef44
commit
426fc2f4e8
2 files changed
+134
-5
No files matched your search
@@ -52,6 +52,7 @@ static struct CONN_MGR_HANDLE *h_a = NULL, *h_b = NULL;
|
||||
|
||||
static int g_failed = 0;
|
||||
static int up_count, down_count, timeout_count, data_count, expected_marker;
|
||||
static int close_on_timeout;
|
||||
static uint64_t data_hop;
|
||||
#define TEST_CM_SERVICE 200
|
||||
|
||||
@@ -109,6 +110,7 @@ static void conn_cb_a(struct CONN_MGR_HANDLE* h, uint64_t node, uint64_t grp, en
|
||||
if (ev == CONN_EVENT_UP) up_count++;
|
||||
else if (ev == CONN_EVENT_DOWN) down_count++;
|
||||
else timeout_count++;
|
||||
if (ev == CONN_EVENT_TIMEOUT && close_on_timeout) { conn_mgr_close(h); h_a = NULL; }
|
||||
}
|
||||
static void conn_cb_b(struct CONN_MGR_HANDLE* h, uint64_t node, uint64_t grp, enum conn_mgr_event ev, void* arg) {
|
||||
(void)h; (void)node; (void)grp; (void)arg; ev_b = (int)ev;
|
||||
@@ -258,6 +260,7 @@ static void scenario_teardown(void) {
|
||||
if (inst_c) { inst_c->running = 0; utun_instance_destroy(inst_c); inst_c = NULL; }
|
||||
cleanup_configs();
|
||||
up_count = down_count = timeout_count = data_count = expected_marker = 0;
|
||||
close_on_timeout = 0;
|
||||
ev_a = ev_b = -1;
|
||||
}
|
||||
|
||||
@@ -627,6 +630,96 @@ static int run_recovery(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* WITHDRAW последнего маршрута убирает резерв; новый NODEINFO восстанавливает доставку. */
|
||||
static int run_relay_recovery(void) {
|
||||
fprintf(stderr, "\n=== Scenario 9: lost relay route and new exchange ===\n");
|
||||
if (create_configs("nat", "nat") || scenario_setup() || !poll_until(candidates_a_and_b, 8000)) return -1;
|
||||
mangle_addrs(inst_a, nid_b, (uint16_t)(base_port + 1000));
|
||||
mangle_addrs(inst_b, nid_a, (uint16_t)(base_port + 1000));
|
||||
if (conn_mgr_open(inst_a, TOPO_GROUP_UTUN, nid_b, conn_cb_a, NULL, &h_a) || !poll_until(ev_a_up, 3000)) return -1;
|
||||
struct CONN_MGR_ENTRY* entry = h_a->entry;
|
||||
uint32_t old_request = entry->exchange_request_id;
|
||||
if (etcp_router_bind(inst_b, TEST_CM_SERVICE, data_recv) < 0 || send_data(1, nid_c)) return -1;
|
||||
// Входящий обмен уже истёк, а собственная попытка ещё в backoff.
|
||||
struct CONN_MGR_ENTRY* inbound = cm_find_entry(defgrp(inst_b)->conn_mgr, nid_a);
|
||||
if (!inbound) { fail("RELAY RECOVERY: missing inbound exchange"); return -1; }
|
||||
inbound->peer_exchange_deadline_tb = get_time_tb() - 1;
|
||||
entry->deadline_tb = get_time_tb() + 100; cm_arm_timer(entry); poll_ms(30);
|
||||
struct TOPO_GROUP_NODE* target = topo_node_find_by_id(defgrp(inst_a), nid_b);
|
||||
while (target && target->paths && target->paths->head)
|
||||
topo_group_remove_path(target, ((struct TOPO_NODEPATH*)target->paths->head)->conn);
|
||||
conn_mgr_routes_changed(defgrp(inst_a)->conn_mgr); poll_ms(20);
|
||||
if (down_count != 1 || up_count != 1 || timeout_count || entry->conn_type != CONN_TYPE_NONE ||
|
||||
!entry->attempt_active || !entry->exchange_request_id || entry->exchange_request_id == old_request) {
|
||||
fail("RELAY RECOVERY: lost route did not start a fresh exchange with DOWN"); return -1;
|
||||
}
|
||||
struct CM_INTERM_SEL stale = {0}; stale.request_id = old_request; stale.count = 1;
|
||||
stale.selected[0] = (struct CONN_MGR_CANDIDATE){nid_c, 1};
|
||||
cm_handle_interm_ack(defgrp(inst_a)->conn_mgr, nid_b, (const uint8_t*)&stale, sizeof(stale));
|
||||
if (entry->indirect_confirmed || entry->intermediariy_count != 1 || up_count != 1) {
|
||||
fail("RELAY RECOVERY: old ACK confirmed a new attempt"); return -1;
|
||||
}
|
||||
struct ETCP_CONN* to_a = instance_find_conn(inst_c, nid_a);
|
||||
struct TOPO_GROUP_NODE* b_on_c = topo_node_find_by_id(defgrp(inst_c), nid_b);
|
||||
if (!to_a || !b_on_c || topo_group_send_nodeinfo(defgrp(inst_c), b_on_c, to_a) < 0) return -1;
|
||||
ev_a = -1;
|
||||
if (!poll_until(ev_a_up, 3000) || up_count != 2 || down_count != 1 || timeout_count ||
|
||||
entry->conn_type != CONN_TYPE_INDIRECT || inbound->peer_exchange_request_id != entry->exchange_request_id ||
|
||||
inbound->peer_exchange_deadline_tb <= get_time_tb()) {
|
||||
fail("RELAY RECOVERY: fresh exchange did not restore UP"); return -1;
|
||||
}
|
||||
if (send_data(2, nid_c)) return -1;
|
||||
fprintf(stderr, " OK: one DOWN/UP, immediate exchange, stale ACK ignored and expired incoming deadline renewed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Начальный TIMEOUT не снимает потребность; callback может безопасно закрыть последний handle. */
|
||||
static int run_initial_timeout(void) {
|
||||
fprintf(stderr, "\n=== Scenario 10: initial TIMEOUT keeps demand and callback close ===\n");
|
||||
if (create_configs("nat", "nat") || scenario_setup() || !poll_until(candidates_a_and_b, 8000)) return -1;
|
||||
mangle_addrs(inst_a, nid_b, (uint16_t)(base_port + 1000));
|
||||
mangle_addrs(inst_b, nid_a, (uint16_t)(base_port + 1000));
|
||||
struct TOPO_GROUP_NODE* target = topo_node_find_by_id(defgrp(inst_a), nid_b);
|
||||
while (target && target->paths && target->paths->head)
|
||||
topo_group_remove_path(target, ((struct TOPO_NODEPATH*)target->paths->head)->conn);
|
||||
if (conn_mgr_open(inst_a, TOPO_GROUP_UTUN, nid_b, conn_cb_a, NULL, &h_a)) return -1;
|
||||
struct CONN_MGR_ENTRY* entry = h_a->entry;
|
||||
entry->initial_deadline_tb = entry->deadline_tb = get_time_tb() + 100;
|
||||
cm_arm_timer(entry); poll_ms(30);
|
||||
if (timeout_count != 1 || up_count || down_count || h_a->entry != entry || entry->attempt_active || !entry->retry_tb) {
|
||||
fail("INITIAL TIMEOUT: demand was lost or wrong events"); return -1;
|
||||
}
|
||||
uint64_t start = entry->start_tb;
|
||||
entry->retry_tb = get_time_tb() + 100; cm_arm_timer(entry); poll_ms(30);
|
||||
if (!entry->attempt_active || entry->start_tb == start || timeout_count != 1) {
|
||||
fail("INITIAL TIMEOUT: no subsequent retry"); return -1;
|
||||
}
|
||||
struct ETCP_CONN* to_a = instance_find_conn(inst_c, nid_a);
|
||||
struct TOPO_GROUP_NODE* b_on_c = topo_node_find_by_id(defgrp(inst_c), nid_b);
|
||||
if (!to_a || !b_on_c || topo_group_send_nodeinfo(defgrp(inst_c), b_on_c, to_a) < 0) return -1;
|
||||
ev_a = -1;
|
||||
if (!poll_until(ev_a_up, 3000) || up_count != 1 || timeout_count != 1 || down_count) {
|
||||
fail("INITIAL TIMEOUT: late success did not deliver UP"); return -1;
|
||||
}
|
||||
if (etcp_router_bind(inst_b, TEST_CM_SERVICE, data_recv) < 0 || send_data(1, nid_c)) return -1;
|
||||
conn_mgr_close(h_a); h_a = NULL; poll_ms(30);
|
||||
target = topo_node_find_by_id(defgrp(inst_a), nid_b);
|
||||
while (target && target->paths && target->paths->head)
|
||||
topo_group_remove_path(target, ((struct TOPO_NODEPATH*)target->paths->head)->conn);
|
||||
close_on_timeout = 1;
|
||||
if (conn_mgr_open(inst_a, TOPO_GROUP_UTUN, nid_b, conn_cb_a, NULL, &h_a)) return -1;
|
||||
entry = h_a->entry;
|
||||
entry->initial_deadline_tb = entry->deadline_tb = get_time_tb() + 100;
|
||||
cm_arm_timer(entry); poll_ms(30);
|
||||
if (h_a || cm_find_entry(defgrp(inst_a)->conn_mgr, nid_b) || timeout_count != 2) {
|
||||
fail("INITIAL TIMEOUT: last close inside callback did not cancel recovery"); return -1;
|
||||
}
|
||||
poll_ms(1100);
|
||||
if (up_count != 1 || down_count || timeout_count != 2) { fail("INITIAL TIMEOUT: callback after close"); return -1; }
|
||||
fprintf(stderr, " OK: one initial TIMEOUT, continuing retries, late UP and safe close inside TIMEOUT callback\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ─── main ─── */
|
||||
|
||||
int main(void) {
|
||||
@@ -664,6 +757,10 @@ int main(void) {
|
||||
scenario_teardown();
|
||||
if (run_recovery() != 0) g_failed = 1;
|
||||
scenario_teardown();
|
||||
if (run_relay_recovery() != 0) g_failed = 1;
|
||||
scenario_teardown();
|
||||
if (run_initial_timeout() != 0) g_failed = 1;
|
||||
scenario_teardown();
|
||||
|
||||
if (ua) { uasync_poll(ua, 0); uasync_destroy(ua, 0); ua = NULL; }
|
||||
|
||||
|
||||
Reference in new issue
Block a user