Browse Source

Schedule bounded endpoint rechecks after compatible UDP failure

master
evgeny 7 days ago
parent
commit
952fbe68e2
  1. 89
      src/routing_layer/nat_detection.c
  2. 2
      src/routing_layer/nat_detection.h

89
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)

2
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);

Loading…
Cancel
Save