nat: retain observed candidates and continue mapping after ingress failures
This commit is contained in:
1 parent
b96ae1a718
commit
6037819269
3 files changed
+77
-17
No files matched your search
@@ -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
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user