diff --git a/src/routing_layer/address_profile.h b/src/routing_layer/address_profile.h index db9a74ee..84d0fea0 100644 --- a/src/routing_layer/address_profile.h +++ b/src/routing_layer/address_profile.h @@ -45,8 +45,7 @@ static inline const char *address_profile_source_name(uint8_t value) { static inline int address_profile_valid(const struct address_profile *profile) { return profile && profile->mapping <= ADDRESS_MAP_DEPENDENT && profile->inbound <= ADDRESS_IN_CONTACT && profile->source <= ADDRESS_SOURCE_STUN && - ((!profile->mapping && !profile->inbound && profile->source == ADDRESS_SOURCE_NONE) || - ((profile->mapping || profile->inbound) && profile->source)); + (profile->source || (!profile->mapping && !profile->inbound)); } #endif diff --git a/src/routing_layer/nat_detection.c b/src/routing_layer/nat_detection.c index d3d6a82e..ce2d9320 100644 --- a/src/routing_layer/nat_detection.c +++ b/src/routing_layer/nat_detection.c @@ -58,7 +58,7 @@ struct nat_socket { struct ETCP_SOCKET *socket, *helper_primary; struct sockaddr_storage local, observer_address, helper_address; struct sockaddr_storage baseline, helper_seen; - struct sockaddr_storage measured_address; + struct sockaddr_storage measured_address, observed_address; struct address_profile measured_profile; uint64_t select_due, available_fingerprint, reservation_fingerprint, cold_until, failure_recheck_tb; uint8_t observer_key[32], helper_key[32]; @@ -234,6 +234,8 @@ static int pair_after(struct nat_pair a, struct nat_pair b) { static int select_pair(struct nat_socket *state) { struct NAT_DETECTION *detector = state->detector; struct nat_pair best = {0}; + struct nat_pair single = {0}; + struct sockaddr_storage single_address = {0}; uint8_t single_key[32] = {0}; struct sockaddr_storage observer_address = {0}, helper_address = {0}; uint8_t observer_key[32] = {0}, helper_key[32] = {0}, helper_socket = 0; unsigned observers = 0, groups = 0; @@ -253,6 +255,12 @@ static int select_pair(struct nat_socket *state) { node_udp_endpoint(detector, bn, state->local.ss_family, 0, previous_b, &ba, &bs)) { if (!state->socket->is_tcp && route_ping_addr_encode(previous_b, &ba)) break; observers++; + struct nat_pair observer_only = {.group = group->group_id, .observer = observer}; + memcpy(observer_only.observer_endpoint, previous_b, sizeof(previous_b)); + if (!state->socket->is_tcp && pair_after(observer_only, state->cursor) && + (!single.group || pair_after(single, observer_only))) { + single = observer_only; single_address = ba; memcpy(single_key, bn->public_key, 32); + } for (struct ll_entry *c = state->socket->is_tcp ? b : group->nodes->head; c; c = state->socket->is_tcp ? NULL : c->next) { uint64_t helper = ((struct TOPO_GROUP_NODE *)c)->node_id; if (helper == detector->instance->node_id || (!state->socket->is_tcp && helper == observer) || @@ -278,6 +286,9 @@ static int select_pair(struct nat_socket *state) { } } } + if (!best.group && single.group && !state->using_fallback) { + best = single; observer_address = single_address; memcpy(observer_key, single_key, 32); + } if (!best.group) { if (state->prefer_fresh && !state->using_fallback) { state->prefer_fresh = 0; state->cursor = (struct nat_pair){0}; return select_pair(state); @@ -558,6 +569,7 @@ static void clear_profile(struct nat_socket *state, uint8_t reason) { state->cold_until = get_time_tb() + NAT_ROUND_TB + 2500; memset(&state->measured_profile, 0, sizeof(state->measured_profile)); memset(&state->measured_address, 0, sizeof(state->measured_address)); + memset(&state->observed_address, 0, sizeof(state->observed_address)); if (changed) { DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT profile invalidated socket=%s generation=%016llx reason=%s", state->socket->name, (unsigned long long)state->socket->generation, nat_check_reason_name(reason)); @@ -574,6 +586,9 @@ static void select_profile(struct nat_socket *state) { selected = state->measured_profile; if (selected.mapping == ADDRESS_MAP_EIM) selected_address = state->measured_address; } + if (!selected.mapping && state->observed_address.ss_family) { + selected.source = ADDRESS_SOURCE_ETCP; selected_address = state->observed_address; + } int address_changed = selected_address.ss_family != state->socket->nat_addr.ss_family || (selected_address.ss_family && !route_ping_addr_equal(&selected_address, &state->socket->nat_addr, 1)); if (selected.mapping == current->mapping && selected.inbound == current->inbound && @@ -588,7 +603,33 @@ static void select_profile(struct nat_socket *state) { publish(state->detector); } +/* Одно наблюдение подтверждает NAT-адрес, но не его независимость от IP назначения. */ +static void remember_address(struct nat_socket *state, const struct sockaddr_storage *observed) { + if (route_ping_addr_equal(&state->local, observed, 1)) return; + if (route_ping_addr_equal(&state->observed_address, observed, 1)) return; + state->observed_address = *observed; + DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT address observed socket=%s local=%s external=%s mapping=%s scope=one_destination", + state->socket->name, sockaddr_storage_to_str(&state->local).str, sockaddr_storage_to_str(observed).str, + address_mapping_name(state->measured_profile.mapping)); + select_profile(state); +} + static void owner_finish(struct nat_socket *state, uint8_t reason) { + /* Неудача входящего испытания не мешает сравнить mapping через уже известный публичный endpoint C. */ + if ((reason == NAT_REASON_TIMEOUT || reason == NAT_REASON_CONTROL_LOST || reason == NAT_REASON_SEND_ERROR || + reason == NAT_REASON_CONTACT_HISTORY) && + (state->phase == NAT_PREPARE_C || state->phase == NAT_PRE || state->phase == NAT_POST) && + get_time_tb() < state->cycle_deadline && public_address(state->detector, &state->helper_address) && + !route_ping_addr_equal(&state->helper_address, &state->observer_address, 0) && + !route_ping_addr_equal(&state->helper_address, &state->baseline, 0)) { + if (reason == NAT_REASON_CONTACT_HISTORY || !state->pre_received) state->filter_clean = 0; + state->phase = state->helper_seen.ss_family ? NAT_REPEAT_B : NAT_OBSERVE_C; state->retries = 0; + DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT mapping continues without ingress result socket=%s round=%016llx helper=%s reason=%s", + state->socket->name, (unsigned long long)state->round, sockaddr_storage_to_str(&state->helper_address).str, + nat_check_reason_name(reason)); + if (owner_observe(state)) owner_finish(state, NAT_REASON_SEND_ERROR); + return; + } int next_pair = reason && state->phase && state->cycling && !state->using_fallback && reason != NAT_REASON_CANCELLED && reason != NAT_REASON_ADDRESS_CHANGED; etcp_cancel_pings(state->detector->instance, owner_ping_cb, state); @@ -604,8 +645,9 @@ static void owner_finish(struct nat_socket *state, uint8_t reason) { } else { state->cycling = 0; state->using_fallback = 0; state->preferred_observer = 0; state->cursor = (struct nat_pair){0}; state->fallback = (struct nat_pair){0}; - state->due = get_time_tb() + (reason == NAT_REASON_ADDRESS_CHANGED ? 2500 : - state->socket->address_profile.source || !reason ? NAT_REFRESH_TB : NAT_RETRY_TB); + int complete = state->measured_profile.mapping == ADDRESS_MAP_DEPENDENT || + (state->measured_profile.mapping && state->measured_profile.inbound); + state->due = get_time_tb() + (reason == NAT_REASON_ADDRESS_CHANGED ? 2500 : complete ? NAT_REFRESH_TB : NAT_RETRY_TB); } if (reason) { DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT round incomplete socket=%s group=%016llx generation=%016llx round=%016llx reason=%s next_pair=%d", @@ -647,6 +689,10 @@ static int owner_run(struct nat_socket *state, uint8_t step) { message.step = step; state->phase = step == NAT_STEP_TCP ? NAT_TCP : step == NAT_STEP_PREPARE ? NAT_PREPARE_C : step == NAT_STEP_PRE ? NAT_PRE : NAT_POST; + uint64_t budget = (uint64_t)probe_timeout_ms(state) * 10 * + (step == NAT_STEP_PREPARE ? 2 * NAT_PROBE_COUNT + 2 : NAT_PROBE_COUNT + 2); + state->deadline = get_time_tb() + budget; + if (state->deadline > state->cycle_deadline) state->deadline = state->cycle_deadline; if (step == NAT_STEP_PRE) state->pre_started = get_time_tb(); return send_control(state->detector, state->group_id, state->observer, &message); } @@ -656,6 +702,7 @@ static int owner_observe(struct nat_socket *state) { const uint8_t *key = state->observer_key; if (state->phase == NAT_OBSERVE_C || state->phase == NAT_REPEAT_C) { target = &state->helper_address; key = state->helper_key; } int timeout = probe_timeout_ms(state); + state->deadline = state->cycle_deadline; if (state->phase == NAT_OBSERVE_C && !state->retries) { state->contact_cutoff = state->contact_count; if (contacted(state, &state->helper_address)) state->filter_clean = 0; @@ -715,8 +762,11 @@ static void owner_ping_cb(int success, uint16_t rtt, void *arg, uint64_t nonce, if (state->phase == NAT_OBSERVE_B) { state->baseline = observed; 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))) + (state->measured_profile.mapping == ADDRESS_MAP_EIM && + !route_ping_addr_equal(&state->measured_address, &observed, 1))) clear_profile(state, NAT_REASON_ADDRESS_CHANGED); + remember_address(state, &observed); + if (!state->helper) { owner_finish(state, NAT_REASON_NO_HELPER); return; } if (owner_run(state, NAT_STEP_PREPARE)) owner_finish(state, NAT_REASON_CONTROL_LOST); return; } @@ -793,6 +843,7 @@ void nat_detection_receive_ingress(struct NAT_DETECTION *detector, struct ETCP_S if (!state->pre_received) { if (contacted(state, source) && !state->using_fallback) { if (!state->fallback.group) state->fallback = state->cursor; + state->helper_address = *source; state->filter_clean = 0; DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT helper rejected by receiver socket=%s round=%016llx source=%s reason=previous_udp_contact", socket->name, (unsigned long long)state->round, sockaddr_storage_to_str(source).str); owner_finish(state, NAT_REASON_CONTACT_HISTORY); return; @@ -1066,15 +1117,17 @@ static void handle_result(struct nat_socket *state, const struct nat_wire *messa int same_ip = route_ping_addr_equal(&state->observer_address, &primary, 0) || route_ping_addr_equal(&state->baseline, &primary, 0); int old_contact = state->pre_received ? !state->filter_clean : contacted(state, &primary); - if (same_ip || (old_contact && !state->using_fallback)) { - if (!same_ip && !state->fallback.group) state->fallback = state->cursor; + if (same_ip) { DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT try another pair socket=%s group=%016llx helper=%016llx endpoint=%s reason=%s", state->socket->name, (unsigned long long)state->group_id, (unsigned long long)state->helper, - sockaddr_storage_to_str(&primary).str, same_ip ? "same_external_ip" : "previous_udp_contact"); - owner_finish(state, same_ip ? NAT_REASON_NO_HELPER : NAT_REASON_CONTACT_HISTORY); return; + sockaddr_storage_to_str(&primary).str, "same_external_ip"); + owner_finish(state, NAT_REASON_NO_HELPER); return; } state->helper_address = primary; state->filter_clean = !old_contact; - if (state->pre_received || state->using_fallback) { + if (old_contact) DEBUG_INFO(DEBUG_CATEGORY_NAT, + "NAT known helper used for mapping only socket=%s round=%016llx helper=%s", + state->socket->name, (unsigned long long)state->round, sockaddr_storage_to_str(&primary).str); + if (state->pre_received || old_contact || state->using_fallback) { state->phase = NAT_OBSERVE_C; if (owner_observe(state)) owner_finish(state, NAT_REASON_SEND_ERROR); return; @@ -1265,7 +1318,12 @@ static void detector_tick(void *arg) { if (!socket->probe_only) socket_state(detector, socket, 1); for (struct nat_socket *state = detector->sockets; state; state = state->next) { if (now >= state->select_due) { select_profile(state); state->select_due = now + 10000; } - if (state->phase && now >= state->deadline) { owner_finish(state, NAT_REASON_TIMEOUT); continue; } + if (state->phase && now >= state->deadline) { + DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT phase deadline socket=%s round=%016llx phase=%u cycle_remaining_tb=%llu", + state->socket->name, (unsigned long long)state->round, state->phase, + (unsigned long long)(state->cycle_deadline > now ? state->cycle_deadline - now : 0)); + owner_finish(state, NAT_REASON_TIMEOUT); continue; + } if (!state->phase && now >= state->due && (detector->automatic || state->forced)) { state->forced = 0; owner_start(state); } @@ -1545,11 +1603,13 @@ 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))) { + (state->measured_profile.mapping == ADDRESS_MAP_EIM && + !route_ping_addr_equal(&state->measured_address, 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; } + if (link->conn->type != CFG_SERVER_TYPE_LOCAL && link->conn->type != CFG_SERVER_TYPE_PRIVATE) remember_address(state, observed); } void nat_detection_socket_removed(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket) { diff --git a/src/routing_layer/topo_node.c b/src/routing_layer/topo_node.c index 0092a7f2..638b432c 100644 --- a/src/routing_layer/topo_node.c +++ b/src/routing_layer/topo_node.c @@ -1166,10 +1166,10 @@ int topo_node_update_my_addresses(struct UTUN_INSTANCE* instance) { a->type = TOPO_ADDR_INTERFACE; a->socket_id = e_sock->sock_id; a->protocol = TOPO_PROTO_UDP; a->next = v4_head; v4_head = a; } - /* Общий внешний адрес публикует только владелец после наблюдений EIM. - Наличие ответа интернет-NTP не связано с работоспособностью этого endpoint. */ + /* Одно наблюдение даёт NAT-кандидат с типом UNKNOWN; только EIM подтверждает общий внешний адрес. */ if (e_sock->nat_addr.ss_family == AF_INET && nat_sin->sin_addr.s_addr != 0 && - e_sock->address_profile.mapping == ADDRESS_MAP_EIM && e_sock->address_profile.source == ADDRESS_SOURCE_ETCP) { + (e_sock->address_profile.mapping == ADDRESS_MAP_UNKNOWN || e_sock->address_profile.mapping == ADDRESS_MAP_EIM) && + e_sock->address_profile.source == ADDRESS_SOURCE_ETCP) { struct TOPO_ADDR4* a = memory_pool_alloc(instance->topo_groups->v4_addr_pool); if (!a) goto allocation_failed; memcpy(a->addr, &nat_sin->sin_addr.s_addr, 4); a->port = ntohs(nat_sin->sin_port); @@ -1188,7 +1188,8 @@ int topo_node_update_my_addresses(struct UTUN_INSTANCE* instance) { memcpy(a6->addr, &if_sin6->sin6_addr, 16); a6->port = ntohs(if_sin6->sin6_port); a6->type = TOPO_ADDR_INTERFACE; a6->socket_id = e_sock->sock_id; a6->protocol = TOPO_PROTO_UDP; a6->next = v6_head; v6_head = a6; } - if (e_sock->nat_addr.ss_family == AF_INET6 && e_sock->address_profile.mapping == ADDRESS_MAP_EIM && + if (e_sock->nat_addr.ss_family == AF_INET6 && + (e_sock->address_profile.mapping == ADDRESS_MAP_UNKNOWN || e_sock->address_profile.mapping == ADDRESS_MAP_EIM) && e_sock->address_profile.source == ADDRESS_SOURCE_ETCP) { const struct sockaddr_in6 *mapped = (const struct sockaddr_in6 *)&e_sock->nat_addr; struct TOPO_ADDR6 *a6 = memory_pool_alloc(instance->topo_groups->v6_addr_pool);