From 54294315677ec4fdf882b1fe242bcbd35c2b8d30 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 12 Oct 2026 17:11:56 +0200 Subject: [PATCH] Invalidate changed endpoints and verify the Android address contract --- src/routing_layer/nat_detection.c | 19 ++++++++++++++----- tests/test_android_replies.c | 13 ++++++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/routing_layer/nat_detection.c b/src/routing_layer/nat_detection.c index 3148ec30..86ea2d4b 100644 --- a/src/routing_layer/nat_detection.c +++ b/src/routing_layer/nat_detection.c @@ -419,7 +419,8 @@ static void owner_finish(struct nat_socket *state, uint8_t reason) { state->active_ping = 0; state->phase = NAT_IDLE; state->reason = reason; if (reason == NAT_REASON_NO_OBSERVER || reason == NAT_REASON_NO_HELPER) state->fallback_allowed = 1; state->preferred_observer = 0; - state->due = get_time_tb() + (state->socket->address_profile.source || !reason ? NAT_REFRESH_TB : NAT_RETRY_TB); + state->due = get_time_tb() + (reason == NAT_REASON_ADDRESS_CHANGED ? 2500 : + state->socket->address_profile.source || !reason ? NAT_REFRESH_TB : NAT_RETRY_TB); if (reason) { DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT round incomplete socket=%s generation=%016llx round=%016llx reason=%s", state->socket->name, (unsigned long long)state->socket->generation, @@ -496,7 +497,8 @@ static void owner_ping_cb(int success, uint16_t rtt, void *arg, uint64_t nonce, state->socket->name, state->phase, (unsigned long long)state->round, sockaddr_storage_to_str(&observed).str); if (state->phase == NAT_OBSERVE_B) { state->baseline = observed; - if (state->socket->nat_addr.ss_family && !route_ping_addr_equal(&state->socket->nat_addr, &observed, 1)) + if ((state->measured_profile.mapping == ADDRESS_MAP_UNCHANGED && !route_ping_addr_equal(&state->local, &observed, 1)) || + (state->socket->nat_addr.ss_family && !route_ping_addr_equal(&state->socket->nat_addr, &observed, 1))) clear_profile(state, NAT_REASON_ADDRESS_CHANGED); if (owner_find(state)) owner_finish(state, NAT_REASON_CONTROL_LOST); return; @@ -879,9 +881,13 @@ static void owner_start(struct nat_socket *state) { } if (select_observer(state)) { if (state->reason != NAT_REASON_NO_OBSERVER) - DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT profile remains UNKNOWN: no live observer socket=%s", state->socket->name); + DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT recheck has no live observer socket=%s retained=%s/%s source=%s", state->socket->name, + address_mapping_name(state->socket->address_profile.mapping), + address_inbound_name(state->socket->address_profile.inbound), + address_profile_source_name(state->socket->address_profile.source)); state->reason = NAT_REASON_NO_OBSERVER; state->fallback_allowed = 1; - state->due = get_time_tb() + NAT_RETRY_TB; select_profile(state); return; + select_profile(state); + state->due = get_time_tb() + (state->socket->address_profile.source ? NAT_REFRESH_TB : NAT_RETRY_TB); return; } if (random_id(&state->round)) { state->reason = NAT_REASON_SEND_ERROR; state->due = get_time_tb() + NAT_RETRY_TB; return; } history_load(state); @@ -972,8 +978,11 @@ void nat_detection_observed_by_peer(struct NAT_DETECTION *detector, struct ETCP_ if (!state) return; if ((state->measured_profile.mapping == ADDRESS_MAP_UNCHANGED && !route_ping_addr_equal(&state->local, observed, 1)) || - (link->conn->nat_addr.ss_family && !route_ping_addr_equal(&link->conn->nat_addr, observed, 1))) + (link->conn->nat_addr.ss_family && !route_ping_addr_equal(&link->conn->nat_addr, observed, 1))) { + if (state->phase) owner_finish(state, NAT_REASON_ADDRESS_CHANGED); clear_profile(state, NAT_REASON_ADDRESS_CHANGED); + state->due = get_time_tb() + 2500; + } } void nat_detection_socket_removed(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket) { diff --git a/tests/test_android_replies.c b/tests/test_android_replies.c index d332318c..82676dc3 100644 --- a/tests/test_android_replies.c +++ b/tests/test_android_replies.c @@ -64,13 +64,16 @@ static int check_addresses(const char *channel) { REQUIRE(!topo_node_sqlite_addrs_put(instance->topo_sqlite_db, instance->node_id, &node)); char *json = utun_bridge_get_member_detail_json(channel, instance->node_id); char value[128]; - REQUIRE(json && !extract(json, "$.adr[0].a", value, sizeof(value)) && !strcmp(value, "194.63.142.140")); - REQUIRE(!extract(json, "$.adr[0].p", value, sizeof(value)) && !strcmp(value, "1333")); + REQUIRE(json && !extract(json, "$.adr[0].ad", value, sizeof(value)) && !strcmp(value, "194.63.142.140")); + REQUIRE(!extract(json, "$.adr[0].po", value, sizeof(value)) && !strcmp(value, "1333")); REQUIRE(!extract(json, "$.adr[0].at", value, sizeof(value)) && !strcmp(value, "4")); - REQUIRE(!extract(json, "$.adr[0].pr", value, sizeof(value)) && !strcmp(value, "UDP")); + REQUIRE(!extract(json, "$.adr[0].pt", value, sizeof(value)) && !strcmp(value, "UDP")); + REQUIRE(!extract(json, "$.adr[0].si", value, sizeof(value)) && !strcmp(value, "5")); + REQUIRE(!extract(json, "$.adr[1].ad", value, sizeof(value)) && !strcmp(value, "2a04:5200::f")); REQUIRE(!extract(json, "$.adr[1].at", value, sizeof(value)) && !strcmp(value, "1")); - REQUIRE(!extract(json, "$.adr[1].pr", value, sizeof(value)) && !strcmp(value, "TCP")); - REQUIRE(!extract(json, "$.adr[1].p", value, sizeof(value)) && !strcmp(value, "443")); + REQUIRE(!extract(json, "$.adr[1].pt", value, sizeof(value)) && !strcmp(value, "TCP")); + REQUIRE(!extract(json, "$.adr[1].po", value, sizeof(value)) && !strcmp(value, "443")); + REQUIRE(!extract(json, "$.adr[1].si", value, sizeof(value)) && !strcmp(value, "6")); u_free(json); return 0; }