fix(nat): advance after an unfinished pair uses its full retry budget

This commit is contained in:
evgeny committed 2026-10-17 23:43:21 +02:00
1 parent 1d1f5d081f
commit 305b61ccab
3 files changed
+15 -3

No files matched your search

+2
View File
@@ -81,6 +81,8 @@ IP:порт тестового сокета на том же локальном
через остальных помощников. Порядок обхода фиксирован на его начало; фактический
source всегда проверяется по актуальной истории. При паузе по общему бюджету
незавершённая попытка повторяется с новыми идентификаторами, без сброса обхода.
Если попытка уже получила полный бюджет, его исчерпание считается таймаутом:
обход переходит дальше, а не повторяет одного помощника бесконечно.
Старый сохранённый результат входящих не считается успехом новой проверки.
Соединения по LAN работают независимо и не сбрасывают интернет-профиль.
+8 -1
View File
@@ -70,7 +70,7 @@ struct nat_socket {
uint8_t contacts[NAT_MAX_CONTACTS][17];
unsigned contact_count, contact_cutoff, cycle_contact_cutoff, phase, reason, retries;
uint8_t history_loaded, history_complete, history_incomplete, filter_clean, pre_received, post_received, pre_healthy;
uint8_t cycle_history_complete, cycle_mapping_seen;
uint8_t cycle_history_complete, cycle_mapping_seen, pair_full_budget;
uint8_t helper_socket, active_ping, heartbeat_active, forced, cycling;
uint32_t rounds, helper_reselections;
};
@@ -641,6 +641,11 @@ static void remember_address(struct nat_socket *state, const struct sockaddr_sto
}
static void owner_finish(struct nat_socket *state, uint8_t reason) {
if (reason == NAT_REASON_BUDGET && state->pair_full_budget) {
reason = NAT_REASON_TIMEOUT;
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT pair full budget exhausted socket=%s round=%016llx; advancing candidate",
state->socket->name, (unsigned long long)state->round);
}
/* Неудача входящего испытания не мешает сравнить mapping через уже известный публичный endpoint C. */
if ((reason == NAT_REASON_TIMEOUT || reason == NAT_REASON_CONTROL_LOST || reason == NAT_REASON_SEND_ERROR ||
reason == NAT_REASON_CONTACT_HISTORY) &&
@@ -1279,6 +1284,7 @@ static void owner_start(struct nat_socket *state) {
}
uint64_t now = get_time_tb();
history_load(state);
int full_budget = !state->cycling || now >= state->cycle_deadline;
if (!state->cycling) {
state->cycling = 1; state->cursor = (struct nat_pair){0};
state->cycle_contact_cutoff = state->contact_count; state->cycle_history_complete = state->history_complete;
@@ -1290,6 +1296,7 @@ static void owner_start(struct nat_socket *state) {
state->socket->name, state->cycle_contact_cutoff, state->contact_count);
}
if (select_pair(state)) { owner_finish(state, state->reason); return; }
state->pair_full_budget = full_budget;
if (random_id(&state->round)) { owner_finish(state, NAT_REASON_SEND_ERROR); return; }
state->rounds++;
memset(&state->helper_seen, 0, sizeof(state->helper_seen)); memset(&state->baseline, 0, sizeof(state->baseline));
+5 -2
View File
@@ -425,15 +425,18 @@ static void run_budget_resume(void) {
assert(!nat_detection_get_status(owner->nat_det, fault.socket, &status));
assert(!status.phase && status.cycling && status.round == paused_round && status.helper_reselections == completed_pairs);
assert(fault.socket->address_profile.inbound == ADDRESS_IN_UNKNOWN);
fault.mode = FAULT_NONE; fault.delayed_length = 0; end = get_time_tb() + 700000;
fault.delayed_length = 0; end = get_time_tb() + 1300000;
do { test_clock_poll(fixture.ua, 1000); assert(!nat_detection_get_status(owner->nat_det, fault.socket, &status)); }
while (get_time_tb() < end && status.round == paused_round);
assert(status.round != paused_round && status.helper == paused_helper && status.helper_reselections == completed_pairs);
uint32_t resumed_rounds = status.rounds;
/* Полный бюджет уже дан этой паре: следующий предел завершает её, а не запускает бесконечные повторы. */
do { test_clock_poll(fixture.ua, 1000); assert(!nat_detection_get_status(owner->nat_det, fault.socket, &status)); }
while (get_time_tb() < end && status.cycling);
assert(!status.phase && !status.cycling && status.round != paused_round && status.helper == paused_helper);
assert(status.rounds == resumed_rounds);
assert(status.helper_reselections == completed_pairs + 1 && fault.socket->address_profile.mapping == ADDRESS_MAP_UNCHANGED &&
fault.socket->address_profile.inbound == ADDRESS_IN_UNKNOWN && status.reason == NAT_REASON_CONTACT_HISTORY);
fault.socket->address_profile.inbound == ADDRESS_IN_UNKNOWN && status.reason == NAT_REASON_TIMEOUT);
fault.fixture = NULL; nat_fixture_cleanup(&fixture);
DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS NAT budget pause retains mapping, rejects late ingress and retries the unfinished pair");
}