Browse Source

Preserve cold NAT trials in the real network fixture

stream-multiview
evgeny 1 week ago
parent
commit
c7be1ebbbe
  1. 1
      src/routing_layer/nat_detection.c
  2. 8
      tests/stream_netns/nat_scenario.py
  3. 2
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt
  4. 2
      tools/chatgui-android/app/src/test/java/com/utun/chat/ui/screens/MemberAddressFormatTest.kt

1
src/routing_layer/nat_detection.c

@ -1367,6 +1367,7 @@ void nat_detection_connection_failed(struct NAT_DETECTION *detector, struct ETCP
struct ll_entry *packet = ll_alloc_lldgram(1 + sizeof(request)); struct ll_entry *packet = ll_alloc_lldgram(1 + sizeof(request));
if (!packet) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT failure recheck allocation failed"); break; } if (!packet) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT failure recheck allocation failed"); break; }
packet->dgram[0] = ETCP_RT_ID_NAT_DETECTION; memcpy(packet->dgram + 1, &request, sizeof(request)); packet->dgram[0] = ETCP_RT_ID_NAT_DETECTION; memcpy(packet->dgram + 1, &request, sizeof(request));
packet->len = 1 + sizeof(request);
if (etcp_route_send(detector->instance, group->group_id, connection->peer_node_id, packet, 0, if (etcp_route_send(detector->instance, group->group_id, connection->peer_node_id, packet, 0,
ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT)) ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT))
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT failure recheck forwarding failed group=%016llx peer=%016llx", DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT failure recheck forwarding failed group=%016llx peer=%016llx",

8
tests/stream_netns/nat_scenario.py

@ -14,6 +14,9 @@ def check_profiles(scenario):
public = [node for node in NODES if not node.nat] public = [node for node in NODES if not node.nat]
transport = "tcp" if scenario.args.tcp_only else "udp" transport = "tcp" if scenario.args.tcp_only else "udp"
connected = set() connected = set()
# Первый профиль измеряется при появлении маршрутов, до фонового обхода всех NODEINFO.
for agent in agents.values():
agent.rpc("nat_auto", enabled=1)
def connect(source, target): def connect(source, target):
pair = (source.name, target.name) pair = (source.name, target.name)
@ -56,13 +59,13 @@ def check_profiles(scenario):
actual = profile(data[first.name], "udp") 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"]: 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}") 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) print(f"NAT routed private helper: {private_helper.name} through {second.name}, group={actual['group']}", flush=True)
verified = {} verified = {}
for owner in public + [node for node in NODES if node.nat]: for owner in public + [node for node in NODES if node.nat]:
observer = second if owner.name == first.name else first observer = second if owner.name == first.name else first
agents[owner.name].rpc("nat_auto", enabled=1) if not scenario.args.tcp_only:
agents[owner.name].rpc("nat_check", socket="udp", observer=agents[observer.name].identity)
if not scenario.args.tcp_only: if not scenario.args.tcp_only:
contacts = set(profile(agents[owner.name].rpc("stats"), "udp")["contacts"]) contacts = set(profile(agents[owner.name].rpc("stats"), "udp")["contacts"])
helper = next((node for node in public if node.name not in {owner.name, observer.name} and node.ip not in contacts), None) helper = next((node for node in public if node.name not in {owner.name, observer.name} and node.ip not in contacts), None)
@ -104,7 +107,6 @@ def check_profiles(scenario):
print(f"NAT {owner.name}/{protocol}: {actual['mapping']} / {actual['inbound']}" print(f"NAT {owner.name}/{protocol}: {actual['mapping']} / {actual['inbound']}"
f" external={actual['external'] or '-'} reason={actual['reason']}", flush=True) f" external={actual['external'] or '-'} reason={actual['reason']}", flush=True)
verified[owner.name] = observed verified[owner.name] = observed
agents[owner.name].rpc("nat_auto", enabled=0)
(scenario.network.directory / "nat-profiles.json").write_text(json.dumps(verified, indent=2) + "\n") (scenario.network.directory / "nat-profiles.json").write_text(json.dumps(verified, indent=2) + "\n")
# Повтор на использовавшемся порту сохраняет mapping, но не обновляет OPEN # Повтор на использовавшемся порту сохраняет mapping, но не обновляет OPEN

2
tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt

@ -102,7 +102,7 @@ data class MemberAddr(val family: Int = 0, val protocol: String = "", val addres
val port: Int = 0, val socketId: Int = 0, val addrType: Int = 0, val rtt: Int = 0) { val port: Int = 0, val socketId: Int = 0, val addrType: Int = 0, val rtt: Int = 0) {
val roleLabelRes: Int get() = when (addrType) { val roleLabelRes: Int get() = when (addrType) {
0 -> R.string.address_local; 1 -> R.string.address_public 0 -> R.string.address_local; 1 -> R.string.address_public
2 -> R.string.address_nat_eim; 3 -> R.string.address_nat_dependent; 4 -> R.string.address_nat 2 -> R.string.address_nat_eim; 3 -> R.string.address_nat_dependent
5 -> R.string.address_contact; else -> R.string.address_unknown 5 -> R.string.address_contact; else -> R.string.address_unknown
} }
} }

2
tools/chatgui-android/app/src/test/java/com/utun/chat/ui/screens/MemberAddressFormatTest.kt

@ -16,6 +16,8 @@ class MemberAddressFormatTest {
@Test fun contactRequiredIsDistinctFromPublicAndNat() { @Test fun contactRequiredIsDistinctFromPublicAndNat() {
assertEquals(R.string.address_public, MemberAddr(addrType = 1).roleLabelRes) assertEquals(R.string.address_public, MemberAddr(addrType = 1).roleLabelRes)
assertEquals(R.string.address_nat_eim, MemberAddr(addrType = 2).roleLabelRes) assertEquals(R.string.address_nat_eim, MemberAddr(addrType = 2).roleLabelRes)
assertEquals(R.string.address_nat_dependent, MemberAddr(addrType = 3).roleLabelRes)
assertEquals(R.string.address_unknown, MemberAddr(addrType = 4).roleLabelRes)
assertEquals(R.string.address_contact, MemberAddr(addrType = 5).roleLabelRes) assertEquals(R.string.address_contact, MemberAddr(addrType = 5).roleLabelRes)
assertEquals(R.string.address_unknown, MemberAddr(addrType = 6).roleLabelRes) assertEquals(R.string.address_unknown, MemberAddr(addrType = 6).roleLabelRes)
} }

Loading…
Cancel
Save