diff --git a/tests/stream_netns/README.md b/tests/stream_netns/README.md index 9feffafa..3533424d 100644 --- a/tests/stream_netns/README.md +++ b/tests/stream_netns/README.md @@ -50,10 +50,12 @@ JOIN/стриминг в этом режиме не запускаются. до первого исходящего контакта рабочего сокета с помощником. Проверяются отдельно mapping и inbound каждого UDP endpoint, точный собственный -внешний IP:port, отсутствие ложного STRICT без помощника и корректность TCP listener. +внешний IP:port и отсутствие ложного STRICT без помощника. Ожидания: public — UNCHANGED/OPEN, cone — EIM/OPEN, restricted — EIM/CONTACT_REQUIRED, -strict — DEPENDENT без универсального внешнего адреса. TCP за NAT без port forwarding -остаётся UNKNOWN/no_listener. Результаты: `nat-profiles.json`, `nat-result.json`; +strict — DEPENDENT без универсального внешнего адреса. Для TCP один настоящий TCP-пир +сообщает внешний IP: public — UNCHANGED/UNKNOWN, за NAT — UNKNOWN/UNKNOWN с source=ETCP. +Во всех TCP-наблюдениях внешний порт 0, раундов с помощником нет; тип NAT и вход не проверяются. +Результаты: `nat-profiles.json`, `nat-result.json`; обычная телеметрия также включает `address_profiles`, контакты и причину неполного измерения. Полная baseline-проверка длится 120 секунд после старта доставки, плюс калибровка и JOIN. Она покрывает оборот 16-битного timestamp и несколько циклов проб. Короткая проверка: diff --git a/tests/stream_netns/nat_scenario.py b/tests/stream_netns/nat_scenario.py index d64a2c3c..9fcc42c4 100644 --- a/tests/stream_netns/nat_scenario.py +++ b/tests/stream_netns/nat_scenario.py @@ -18,12 +18,12 @@ def check_profiles(scenario): for agent in agents.values(): agent.rpc("nat_auto", enabled=1) - def connect(source, target): - pair = (source.name, target.name) + def connect(source, target, protocol=transport): + pair = (source.name, target.name, protocol) if pair in connected: return snapshot = agents[target.name].rpc("snapshot")["snapshot"] - agents[source.name].rpc("nat_link", snapshot=snapshot, socket=transport) + agents[source.name].rpc("nat_link", snapshot=snapshot, socket=protocol) target_id = agents[target.name].identity source_id = agents[source.name].identity scenario.wait(f"nat-link-{source.name}-{target.name}", @@ -83,16 +83,19 @@ def check_profiles(scenario): protocols = ["tcp"] if scenario.args.tcp_only else ["udp", "tcp"] if scenario.args.tcp else ["udp"] observed = {} for protocol in protocols: - if protocol == "tcp" and owner.nat: - # Приватный TCP listener не имеет обнаруженного публичного endpoint. - agents[owner.name].rpc("nat_check", socket="tcp", observer=agents[observer.name].identity) - data = scenario.wait(f"nat-private-tcp-{owner.name}", lambda statistics: - profile(statistics[owner.name], "tcp")["reason"] == "no_listener", 5) + if protocol == "tcp": + # Только один действующий TCP-пир, без испытания входящих и без сравнения портов. + connect(owner, observer, "tcp") + data = scenario.wait(f"nat-tcp-ip-{owner.name}", lambda statistics: + profile(statistics[owner.name], "tcp")["source"] == "ETCP" and + bool(profile(statistics[owner.name], "tcp")["external"]), 10) actual = profile(data[owner.name], "tcp") - expected = ("UNKNOWN", "UNKNOWN") + expected = ("UNKNOWN" if owner.nat else "UNCHANGED", "UNKNOWN") + if actual["rounds"] or int(actual["helper"]): + raise RuntimeError(f"{owner.name}: TCP IP detection unexpectedly used a helper round: {actual}") else: actual = measure(owner, observer, protocol) - if protocol == "tcp" or not owner.nat: + if not owner.nat: expected = ("UNCHANGED", "OPEN") else: gateway = next(item for item in NATS if item.name == owner.nat) @@ -100,7 +103,10 @@ def check_profiles(scenario): "strict": ("DEPENDENT", "UNKNOWN")}[gateway.mode] if (actual["mapping"], actual["inbound"]) != expected: raise RuntimeError(f"{owner.name}/{protocol}: expected {expected}, detector returned {actual}") - if expected[0] == "EIM": + if protocol == "tcp": + ip = next(item for item in NATS if item.name == owner.nat).ip if owner.nat else owner.ip + external = f"{ip}:0" + elif expected[0] == "EIM": gateway = next(item for item in NATS if item.name == owner.nat) external = f"{gateway.ip}:{owner.mapped_port}" elif expected[0] == "UNCHANGED":