diff --git a/src/routing_layer/nat_detection.c b/src/routing_layer/nat_detection.c index 9485b9f9..303a922b 100644 --- a/src/routing_layer/nat_detection.c +++ b/src/routing_layer/nat_detection.c @@ -17,6 +17,7 @@ #include "route_crypto.h" #include "stcp_client.h" #include "stun_nat.h" +#include "sock_match.h" #include "../lib/debug_config.h" #include "../lib/mem.h" #include "../lib/u_async.h" @@ -29,10 +30,11 @@ #define NAT_ROUND_TB 300000 #define NAT_REFRESH_TB 18000000 #define NAT_RETRY_TB 300000 +#define NAT_FAILURE_RECHECK_TB 3000000 #define NAT_PROBE_COUNT 3 #define NAT_QUERY_TAG_SIZE (ROUTE_OBSERVE_TAG_SIZE + 9) -enum nat_command { NAT_RUN = 1, NAT_RESULT }; +enum nat_command { NAT_RUN = 1, NAT_RESULT, NAT_RECHECK }; enum nat_phase { NAT_IDLE, NAT_OBSERVE_B, NAT_PREPARE_C, NAT_PRE, NAT_OBSERVE_C, NAT_PREPARE_ALT, NAT_OBSERVE_ALT, NAT_POST, NAT_REPEAT_B, NAT_TCP }; enum nat_step { NAT_STEP_PREPARE = 1, NAT_STEP_PRE, NAT_STEP_ALT, NAT_STEP_POST, NAT_STEP_TCP }; @@ -46,6 +48,7 @@ struct nat_wire { }; struct nat_pair { uint64_t group, observer, helper; }; +struct nat_recheck_wire { uint8_t cmd, command, version, socket_id, address[ROUTE_ADDR_WIRE_SIZE]; }; struct nat_socket { struct nat_socket *next; @@ -55,7 +58,7 @@ struct nat_socket { struct sockaddr_storage baseline, helper_seen, alternate_seen; struct sockaddr_storage measured_address; struct address_profile measured_profile; - uint64_t select_due, available_fingerprint, reservation_fingerprint, cold_until; + uint64_t select_due, available_fingerprint, reservation_fingerprint, cold_until, failure_recheck_tb; uint8_t fallback_allowed; uint8_t observer_key[32], helper_key[32]; uint64_t group_id, round, observer, helper, token, deadline, due, completed_tb, preferred_observer; @@ -106,6 +109,17 @@ static int helper_send(struct nat_probe *); static void owner_start(struct nat_socket *); static int contacted(struct nat_socket *, const struct sockaddr_storage *); +/* Не сбрасывает последний успех; повторные ошибки одного endpoint объединяются и ограничиваются по частоте. */ +static int schedule_failure_recheck(struct nat_socket *state, uint64_t peer, int remote) { + uint64_t now = get_time_tb(); + if (!state || !state->detector->automatic || state->phase || state->cycling || state->forced || + (state->failure_recheck_tb && now - state->failure_recheck_tb < NAT_FAILURE_RECHECK_TB)) return 0; + state->failure_recheck_tb = now; state->due = now; state->forced = 1; state->preferred_observer = 0; + DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT failure recheck scheduled socket=%s generation=%016llx peer=%016llx remote_request=%d", + state->socket->name, (unsigned long long)state->socket->generation, (unsigned long long)peer, remote); + return 1; +} + static uint64_t read64(const uint8_t *data) { uint64_t value = 0; for (unsigned i = 0; i < 8; i++) value = (value << 8) | data[i]; @@ -1029,13 +1043,29 @@ static void receive_control(struct ETCP_CONN *from, struct ll_entry *entry) { (unsigned long long)group_id, (unsigned long long)sender); queue_dgram_free(entry); queue_entry_free(entry); cancel_route(detector, group_id, sender); return; } - if (entry->len != ROUTER_SVC_HDR_SIZE + sizeof(struct nat_wire) || - entry->dgram[ROUTER_SVC_FLAGS_OFF] != (ROUTER_FLAG_SIGNED | ROUTER_FLAG_ENCRYPTED) || + if (entry->dgram[ROUTER_SVC_FLAGS_OFF] != (ROUTER_FLAG_SIGNED | ROUTER_FLAG_ENCRYPTED) || !group_node(detector, group_id, sender)) { DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT routed control rejected group=%016llx sender=%016llx size=%zu flags=%u", (unsigned long long)group_id, (unsigned long long)sender, entry->len, entry->dgram[ROUTER_SVC_FLAGS_OFF]); queue_dgram_free(entry); queue_entry_free(entry); return; } + if (entry->len == ROUTER_SVC_HDR_SIZE + sizeof(struct nat_recheck_wire)) { + struct nat_recheck_wire request; memcpy(&request, entry->dgram + ROUTER_SVC_PAYLOAD_OFF, sizeof(request)); + queue_dgram_free(entry); queue_entry_free(entry); + struct ETCP_SOCKET *socket = find_socket(detector, request.socket_id, 0); + struct sockaddr_storage address = {0}; + if (request.cmd != ETCP_RT_ID_NAT_DETECTION || request.command != NAT_RECHECK || request.version != NAT_WIRE_VERSION || + !socket || socket->is_tcp || socket->probe_only || socket->socks_udp || route_ping_addr_decode(&address, request.address) || + (!route_ping_addr_equal(&address, &socket->local_addr, 1) && !route_ping_addr_equal(&address, &socket->nat_addr, 1))) { + DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT failure recheck rejected group=%016llx peer=%016llx socket=%u: stale/invalid endpoint", + (unsigned long long)group_id, (unsigned long long)sender, request.socket_id); return; + } + schedule_failure_recheck(socket_state(detector, socket, 1), sender, 1); return; + } + if (entry->len != ROUTER_SVC_HDR_SIZE + sizeof(struct nat_wire)) { + DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT control invalid payload size=%zu", entry->len - ROUTER_SVC_HDR_SIZE); + queue_dgram_free(entry); queue_entry_free(entry); return; + } struct nat_wire message; memcpy(&message, entry->dgram + ROUTER_SVC_PAYLOAD_OFF, sizeof(message)); queue_dgram_free(entry); queue_entry_free(entry); if (message.cmd != ETCP_RT_ID_NAT_DETECTION || message.version != NAT_WIRE_VERSION || @@ -1290,6 +1320,57 @@ void nat_detection_cancel_for_conn(struct NAT_DETECTION *detector, struct ETCP_C nat_detection_topology_changed(detector); } +/* Сопоставление с текущим подписанным snapshot не зависит от remote_socket_id до завершения INIT. */ +void nat_detection_connection_failed(struct NAT_DETECTION *detector, struct ETCP_CONN *connection, int reverse_done) { + if (!detector || detector->stopping || !detector->automatic || !connection || connection->links_up) return; + const struct TOPO_NODE *node = topo_node_registry_find(detector->instance->topo_groups, connection->peer_node_id); + if (!node || !node->timestamp || memcmp(node->public_key, connection->crypto_ctx.peer_public_key, 32)) return; + for (struct ETCP_LINK *link = connection->links; link; link = link->next) { + struct ETCP_SOCKET *socket = link->conn; + if (!socket || link->is_tcp || !link->init_retry_count || link->link_status || socket->probe_only || socket->socks_udp || + socket->type == CFG_SERVER_TYPE_LOCAL || socket->type == CFG_SERVER_TYPE_PRIVATE) continue; + const struct address_profile *profile = NULL; uint8_t target_socket = 0, config_type = CFG_SERVER_TYPE_UNKNOWN, type = 0; + for (const struct TOPO_ADDR4 *a = node->v4_addrs; a; a = a->next) { + const struct sockaddr_in *target = (const struct sockaddr_in *)&link->remote_addr; + if (link->remote_addr.ss_family != AF_INET || !(a->protocol & TOPO_PROTO_UDP) || + a->port != ntohs(target->sin_port) || memcmp(a->addr, &target->sin_addr, 4)) continue; + for (const struct TOPO_SOCKMETA4 *m = node->v4_sock_meta; m; m = m->next) + if (m->id == a->socket_id) { profile = &m->address_profile; config_type = m->config_type; break; } + target_socket = a->socket_id; type = a->type; break; + } + for (const struct TOPO_ADDR6 *a = node->v6_addrs; a; a = a->next) { + const struct sockaddr_in6 *target = (const struct sockaddr_in6 *)&link->remote_addr; + if (link->remote_addr.ss_family != AF_INET6 || !(a->protocol & TOPO_PROTO_UDP) || + a->port != ntohs(target->sin6_port) || memcmp(a->addr, &target->sin6_addr, 16)) continue; + for (const struct TOPO_SOCKMETA6 *m = node->v6_sock_meta; m; m = m->next) + if (m->id == a->socket_id) { profile = &m->address_profile; config_type = m->config_type; break; } + target_socket = a->socket_id; type = a->type; break; + } + uint8_t target_class = sock_target_class(type, config_type, profile, public_address(detector, &link->remote_addr)); + if (target_class != SOCK_TC_PUB && !(reverse_done && target_class == SOCK_TC_CONTACT)) continue; + struct nat_socket *state = socket_state(detector, socket, 1); + if (!schedule_failure_recheck(state, connection->peer_node_id, 0)) continue; + for (struct ll_entry *e = detector->instance->topo_groups->group_list->head; e; e = e->next) { + struct TOPO_GROUP *group = (struct TOPO_GROUP *)e; + if (!control_route(detector, group->group_id, connection->peer_node_id)) continue; + struct nat_recheck_wire request = {.cmd = ETCP_RT_ID_NAT_DETECTION, .command = NAT_RECHECK, + .version = NAT_WIRE_VERSION, .socket_id = target_socket}; + if (route_ping_addr_encode(request.address, &link->remote_addr)) break; + struct ll_entry *packet = ll_alloc_lldgram(1 + sizeof(request)); + 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)); + if (etcp_route_send(detector->instance, group->group_id, connection->peer_node_id, packet, 0, + ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT)) + DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT failure recheck forwarding failed group=%016llx peer=%016llx", + (unsigned long long)group->group_id, (unsigned long long)connection->peer_node_id); + else DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT failure recheck sent group=%016llx peer=%016llx endpoint=%s", + (unsigned long long)group->group_id, (unsigned long long)connection->peer_node_id, + sockaddr_storage_to_str(&link->remote_addr).str); + break; + } + } +} + static void connection_status(struct ETCP_CONN *connection, int status, void *arg) { struct NAT_DETECTION *detector = arg; if (status == ETCP_CONN_STATUS_DOWN || status == ETCP_CONN_STATUS_DELETE || status == ETCP_CONN_STATUS_REINIT) diff --git a/src/routing_layer/nat_detection.h b/src/routing_layer/nat_detection.h index 1a291559..54c3e86a 100644 --- a/src/routing_layer/nat_detection.h +++ b/src/routing_layer/nat_detection.h @@ -48,6 +48,8 @@ int nat_detection_ping_allowed(struct NAT_DETECTION *detector, struct ETCP_SOCKE uint8_t flags, const uint8_t *tag, size_t length); void nat_detection_link_ready(struct NAT_DETECTION *detector, struct ETCP_LINK *link); void nat_detection_cancel_for_conn(struct NAT_DETECTION *detector, struct ETCP_CONN *connection); +/* Перепроверить после реально отправленных INIT к OPEN; CONTACT — только после исчерпания reverse. */ +void nat_detection_connection_failed(struct NAT_DETECTION *detector, struct ETCP_CONN *connection, int reverse_done); void nat_detection_socket_removed(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket); void nat_detection_note_send(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket, const struct sockaddr_storage *destination);