From 89f031f4f9616adec80b0f87f1b23f0c77c2486f Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 12 Oct 2026 17:07:35 +0200 Subject: [PATCH] Keep STUN round diagnostics separate from published NAT profiles --- tests/stream_netns/node.c | 8 ++++---- tests/stream_netns/stun_scenario.py | 2 +- tests/test_stun_nat.c | 22 +++++++++++++--------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/tests/stream_netns/node.c b/tests/stream_netns/node.c index 5a547aeb..1ad5f1d2 100644 --- a/tests/stream_netns/node.c +++ b/tests/stream_netns/node.c @@ -186,9 +186,8 @@ static void statistics(struct lab *lab, uint64_t id, int loads) { socket->sock_id, socket->generation, socket->is_tcp ? "tcp" : "udp"); json_string(sockaddr_storage_to_str(socket->is_tcp ? &socket->interface_addr : &socket->local_addr).str); printf(",\"source\":\"%s\"", address_profile_source_name(socket->address_profile.source)); - printf(",\"mapping\":\"%s\",\"inbound\":\"%s\",\"evidence\":\"%" PRIu64 "\",\"checked_at\":\"%" PRIu64 "\",\"external\":", - address_mapping_name(socket->address_profile.mapping), address_inbound_name(socket->address_profile.inbound), - socket->address_profile.evidence_id, socket->address_profile.checked_at); + printf(",\"mapping\":\"%s\",\"inbound\":\"%s\",\"external\":", + address_mapping_name(socket->address_profile.mapping), address_inbound_name(socket->address_profile.inbound)); const struct sockaddr_storage *external = &socket->nat_addr; if (!external->ss_family && socket->address_profile.mapping == ADDRESS_MAP_UNCHANGED && socket->address_profile.source == ADDRESS_SOURCE_ETCP) @@ -196,7 +195,8 @@ static void statistics(struct lab *lab, uint64_t id, int loads) { json_string(external->ss_family ? sockaddr_storage_to_str(external).str : ""); struct stun_nat_result stun; if (!stun_nat_get_socket_result(lab->inst, socket, &stun)) { - printf(",\"stun\":{\"members\":%u,\"generation\":\"%" PRIu64 "\",\"test_local\":", stun.members, stun.generation); + printf(",\"stun\":{\"members\":%u,\"generation\":\"%" PRIu64 "\",\"evidence\":\"%" PRIu64 "\",\"test_local\":", + stun.members, stun.generation, stun.evidence_id); json_string(sockaddr_storage_to_str(&stun.local).str); printf("}"); } printf(",\"phase\":%u,\"reason\":\"%s\",\"observer\":\"%" PRIu64 "\",\"helper\":\"%" PRIu64 "\"," diff --git a/tests/stream_netns/stun_scenario.py b/tests/stream_netns/stun_scenario.py index bee0ab39..d1fb0302 100644 --- a/tests/stream_netns/stun_scenario.py +++ b/tests/stream_netns/stun_scenario.py @@ -48,7 +48,7 @@ def check_stun(scenario): udp, duplicate = profile(data[owner_name], "udp"), profile(data[owner_name], "duplicate") if (udp["mapping"], udp["inbound"]) != (mapping, inbound): raise RuntimeError(f"{owner_name}: STUN class mismatch: {udp}") - if udp["evidence"] != duplicate["evidence"] or udp["stun"]["members"] != 2 or udp["external"]: + if udp["stun"]["evidence"] != duplicate["stun"]["evidence"] or udp["stun"]["members"] != 2 or udp["external"]: raise RuntimeError(f"{owner_name}: duplicated probe or foreign external port: {udp}, {duplicate}") for name in (observer_name,): agents[name] = Agent(network, nodes[name], Path(scenario.args.binary).resolve(), scenario.args.seed, diff --git a/tests/test_stun_nat.c b/tests/test_stun_nat.c index f997e9cb..01269444 100644 --- a/tests/test_stun_nat.c +++ b/tests/test_stun_nat.c @@ -163,8 +163,6 @@ static void fixture_init(struct fixture* f) { f->source.address_profile.mapping = ADDRESS_MAP_DEPENDENT; f->source.address_profile.inbound = ADDRESS_IN_CONTACT; f->source.address_profile.source = ADDRESS_SOURCE_ETCP; - f->source.address_profile.evidence_id = 1; - f->source.address_profile.checked_at = 1; f->source.nat_addr = f->source.local_addr; snprintf(f->source.name, sizeof(f->source.name), "source"); f->inst.etcp_sockets = &f->source; @@ -388,7 +386,7 @@ static void test_fallback(void) { struct ETCP_SOCKET* duplicate = test_etcp_bound_socket(owner, "duplicate"); wait_source(&nf, socket, ADDRESS_SOURCE_STUN); wait_source(&nf, duplicate, ADDRESS_SOURCE_STUN); assert(socket->address_profile.mapping == ADDRESS_MAP_EIM && socket->address_profile.inbound == ADDRESS_IN_OPEN); - assert(socket->address_profile.evidence_id == duplicate->address_profile.evidence_id); + assert(!memcmp(&socket->address_profile, &duplicate->address_profile, sizeof(socket->address_profile))); assert(!socket->nat_addr.ss_family && !duplicate->nat_addr.ss_family); struct nat_check_status status; assert(!nat_detection_get_status(owner->nat_det, socket, &status)); assert(status.reason == NAT_REASON_NO_OBSERVER && status.fallback_allowed); @@ -406,19 +404,25 @@ static void test_fallback(void) { nat_fixture_connect(&nf, observer, "udp", helper); nat_detection_request_check_all(owner->nat_det); wait_source(&nf, socket, ADDRESS_SOURCE_ETCP); wait_source(&nf, duplicate, ADDRESS_SOURCE_ETCP); assert(socket->address_profile.mapping == ADDRESS_MAP_UNCHANGED && socket->address_profile.inbound == ADDRESS_IN_OPEN); - uint64_t evidence = socket->address_profile.evidence_id; + assert(!nat_detection_get_status(owner->nat_det, socket, &status)); + uint64_t round = status.round; + struct address_profile measured = socket->address_profile; struct stun_nat_result prior, next; assert(!stun_nat_get_socket_result(owner, socket, &prior)); stun_nat_recheck(owner); end = get_time_tb() + 3000000; do { test_clock_poll(nf.ua, 10000); assert(!stun_nat_get_socket_result(owner, socket, &next)); } while (get_time_tb() < end && next.status == STUN_NAT_PENDING); assert(next.mapping == STUN_MAP_EIM && next.evidence_id != prior.evidence_id); - assert(socket->address_profile.source == ADDRESS_SOURCE_ETCP && socket->address_profile.evidence_id == evidence); + assert(!memcmp(&socket->address_profile, &measured, sizeof(measured))); nat_detection_request_check_all(owner->nat_det); end = get_time_tb() + 300000; - do { test_clock_poll(nf.ua, 10000); } - while (get_time_tb() < end && socket->address_profile.evidence_id == evidence); - assert(socket->address_profile.source == ADDRESS_SOURCE_ETCP && socket->address_profile.evidence_id != evidence); - assert(socket->address_profile.mapping == ADDRESS_MAP_UNCHANGED && socket->address_profile.inbound == ADDRESS_IN_UNKNOWN); + do { test_clock_poll(nf.ua, 10000); assert(!nat_detection_get_status(owner->nat_det, socket, &status)); } + while (get_time_tb() < end && (status.round == round || status.phase)); + assert(status.round != round && !status.phase); + assert(!memcmp(&socket->address_profile, &measured, sizeof(measured))); + /* Прошедшие пять минут не сбрасывают последний успешный ETCP. */ + uint64_t timestamp = snapshot->timestamp; + test_clock_run_for(nf.ua, 3000001); + assert(!memcmp(&socket->address_profile, &measured, sizeof(measured)) && snapshot->timestamp == timestamp); for (unsigned i = 0; i < 6; i++) { assert(!uasync_remove_socket_t(nf.ua, server.endpoints[i].fd)); socket_close_wrapper(server.endpoints[i].fd); }