diff --git a/tests/stream_netns/nat_scenario.py b/tests/stream_netns/nat_scenario.py index 8f326b79..18c2f606 100644 --- a/tests/stream_netns/nat_scenario.py +++ b/tests/stream_netns/nat_scenario.py @@ -34,7 +34,8 @@ def check_profiles(scenario): agents[owner.name].rpc("nat_check", socket=protocol, observer=agents[observer.name].identity) data = scenario.wait(f"nat-profile-{owner.name}-{protocol}", lambda statistics: profile(statistics[owner.name], protocol)["rounds"] > before["rounds"] and - profile(statistics[owner.name], protocol)["phase"] == 0, 40) + profile(statistics[owner.name], protocol)["phase"] == 0 and + not profile(statistics[owner.name], protocol)["cycling"], 40) return profile(data[owner.name], protocol) # Отсутствие второго наблюдателя не превращается в STRICT/DIRECT. @@ -45,6 +46,19 @@ def check_profiles(scenario): if (incomplete["mapping"], incomplete["inbound"], incomplete["reason"]) != ("UNKNOWN", "UNKNOWN", "no_helper"): raise RuntimeError(f"NAT no-helper test: {incomplete}") + # Третий узел за NAT появляется через B: A не имеет ни линка, ни прежнего UDP-контакта с C. + private_helper = next(node for node in NODES if node.nat) + agents[first.name].rpc("nat_auto", enabled=1) + connect(private_helper, second) + data = scenario.wait("nat-routed-private-helper", lambda statistics: + (profile(statistics[first.name], "udp")["mapping"], + profile(statistics[first.name], "udp")["inbound"]) == ("UNCHANGED", "OPEN"), 40) + actual = profile(data[first.name], "udp") + if actual["helper"] != agents[private_helper.name].identity or agents[private_helper.name].identity in data[first.name]["direct_up"]: + raise RuntimeError(f"NAT indirect private helper was not used: {actual}") + agents[first.name].rpc("nat_auto", enabled=0) + print(f"NAT routed private helper: {private_helper.name} through {second.name}, group={actual['group']}", flush=True) + verified = {} for owner in public + [node for node in NODES if node.nat]: observer = second if owner.name == first.name else first diff --git a/tests/stream_netns/node.c b/tests/stream_netns/node.c index 4aa2504b..9794be4d 100644 --- a/tests/stream_netns/node.c +++ b/tests/stream_netns/node.c @@ -200,9 +200,9 @@ static void statistics(struct lab *lab, uint64_t id, int loads) { json_string(sockaddr_storage_to_str(&stun.local).str); printf("}"); } printf(",\"phase\":%u,\"reason\":\"%s\",\"group\":\"%" PRIu64 "\",\"observer\":\"%" PRIu64 "\",\"helper\":\"%" PRIu64 "\"," - "\"rounds\":%u,\"helper_reselections\":%u,\"history_complete\":%u,\"contacts\":[", status.phase, + "\"rounds\":%u,\"helper_reselections\":%u,\"cycling\":%u,\"history_complete\":%u,\"contacts\":[", status.phase, nat_check_reason_name(status.reason), status.group_id, status.observer, status.helper, status.rounds, - status.helper_reselections, status.history_complete); + status.helper_reselections, status.cycling, status.history_complete); uint8_t local[ROUTE_ADDR_WIRE_SIZE]; sqlite3_stmt *contacts = NULL; int contact_separator = 0; if (!socket->is_tcp && !route_ping_addr_encode(local, &socket->local_addr) && sqlite3_prepare_v2(lab->inst->topo_sqlite_db, "SELECT remote FROM nat_contacts WHERE local=?", diff --git a/tests/test_stun_nat.c b/tests/test_stun_nat.c index 01269444..587526c9 100644 --- a/tests/test_stun_nat.c +++ b/tests/test_stun_nat.c @@ -416,8 +416,8 @@ static void test_fallback(void) { 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); 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); + while (get_time_tb() < end && (status.round == round || status.phase || status.cycling)); + assert(status.round != round && !status.phase && !status.cycling); assert(!memcmp(&socket->address_profile, &measured, sizeof(measured))); /* Прошедшие пять минут не сбрасывают последний успешный ETCP. */ uint64_t timestamp = snapshot->timestamp;