keepalive: раздельный sleep-код (announce+ACK-ретраи в окне, dead-link) + без flagless в спячке
This commit is contained in:
1 parent
a3df1720b4
commit
a3e890b711
4 files changed
+189
-98
No files matched your search
@@ -1006,12 +1006,15 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn
|
||||
link->keepalive_recv_count = 0;
|
||||
link->ka_period_ms = (uint16_t)link->keepalive_interval;
|
||||
link->ka_sleep_period_ms = 0;
|
||||
link->ka_sleeping = 0;
|
||||
link->ka_my_sleeping = 0;
|
||||
link->ka_peer_sleeping = 0;
|
||||
link->ka_sleep_acked = 0;
|
||||
link->ka_active_remaining = 0;
|
||||
link->ka_sleep_duration = 0;
|
||||
link->ka_sleep_announce_timer = NULL;
|
||||
link->ka_sleep_announce_end_tb = 0;
|
||||
link->ka_announce_attempts = 0;
|
||||
link->ka_announce_max_attempts = 0;
|
||||
link->ka_fail_count = 0;
|
||||
/* inflight_lim_bytes set above */
|
||||
link->bandwidth = 10000; // начальная оценка 10 Mbps для шейпера
|
||||
link->burst_id = 0;
|
||||
@@ -1199,9 +1202,11 @@ void etcp_link_enter_ready_tcp(struct ETCP_LINK *link) {
|
||||
if (link->tcp_link) { const uint8_t* ed = stcp_link_get_peer_ed25519_pubkey(link->tcp_link); if (ed) memcpy(etcp->peer_ed25519_pubkey, ed, SC_PUBKEY_SIZE); }
|
||||
if (etcp->initialized == 0) etcp_conn_ready(etcp);
|
||||
if (etcp->state == 2) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "enter_ready_tcp: conn DELETED after conn_ready, link=%p", (void*)link); return; }
|
||||
etcp_link_send_keepalive(link);
|
||||
start_keepalive_timer(link);
|
||||
etcp_keepalive_announce_mode(link);
|
||||
/* в standby анонс спячки вместо обычного flagless-keepalive */
|
||||
if (!etcp_keepalive_announce_mode(link)) {
|
||||
etcp_link_send_keepalive(link);
|
||||
start_keepalive_timer(link);
|
||||
}
|
||||
loadbalancer_link_ready(link);
|
||||
etcp_fire_link_status_cbk(link, old_state, link->link_status);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] TCP link %d UP (mtu=%d init=%d up=%d tcp_links=%d)",
|
||||
@@ -1995,12 +2000,11 @@ static int handle_init_response_client(struct ETCP_SOCKET* e_sock, struct ETCP_D
|
||||
loadbalancer_link_ready(link);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] Link %d UP (client, mtu=%d, addr=%s)", link->etcp->log_name, link->local_link_id, link->mtu, sockaddr_storage_to_str(&link->remote_addr).str);
|
||||
|
||||
// Start keepalive timer
|
||||
etcp_link_send_keepalive(link);
|
||||
|
||||
start_keepalive_timer(link);
|
||||
|
||||
etcp_keepalive_announce_mode(link);
|
||||
/* в standby анонс спячки вместо обычного flagless-keepalive */
|
||||
if (!etcp_keepalive_announce_mode(link)) {
|
||||
etcp_link_send_keepalive(link);
|
||||
start_keepalive_timer(link);
|
||||
}
|
||||
|
||||
loadbalancer_link_ready(link);
|
||||
|
||||
@@ -2524,8 +2528,11 @@ int etcp_packet_decrypted(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt,
|
||||
return 0;
|
||||
}
|
||||
if (link->link_state == LINK_STATE_TRY_RECONNECT) {// 0 - just init, 1 - handshake, 2 - try reconnect, 3 - connected
|
||||
start_keepalive_timer(link);
|
||||
etcp_link_send_keepalive(link);
|
||||
/* в standby анонс спячки вместо обычного flagless-keepalive */
|
||||
if (!etcp_keepalive_announce_mode(link)) {
|
||||
start_keepalive_timer(link);
|
||||
etcp_link_send_keepalive(link);
|
||||
}
|
||||
int old_state = link->link_state; link->link_state = LINK_STATE_CONNECTED;
|
||||
etcp_fire_link_status_cbk(link, old_state, link->link_status);
|
||||
}
|
||||
|
||||
@@ -279,16 +279,19 @@ struct ETCP_LINK {
|
||||
uint8_t nat_check_status; // NAT_CHECK_*
|
||||
|
||||
// Keepalive state
|
||||
void* keepalive_timer; // Таймер для отправки keepalive пакетов
|
||||
void* keepalive_timer; // Таймер обычного keepalive (только в normal-режиме)
|
||||
uint32_t keepalive_timeout; // таймаут (ms)
|
||||
uint16_t ka_period_ms; // текущий период отправки keepalive (адаптивный в normal, фиксированный в спячке)
|
||||
uint16_t ka_period_ms; // текущий период отправки keepalive
|
||||
uint16_t ka_sleep_period_ms; // период keepalive в спячке (мс; настройка клиента / ka_period_ms пира)
|
||||
uint8_t ka_sleeping; // 1 = линк в режиме спячки
|
||||
uint8_t ka_sleep_acked; // клиент: получен ack спячки от сервера
|
||||
uint8_t ka_my_sleeping; // 1 = я объявил сон (standby)
|
||||
uint8_t ka_peer_sleeping; // 1 = пир объявил сон
|
||||
uint8_t ka_sleep_acked; // получен ACK моего анонса сна
|
||||
uint16_t ka_active_remaining; // сколько до SLEEP-фазы (x0.1с), 0 = уже спим
|
||||
uint16_t ka_sleep_duration; // длительность SLEEP-фазы (x0.1с)
|
||||
void* ka_sleep_announce_timer; // таймер ретрая анонса спячки (клиент)
|
||||
uint64_t ka_sleep_announce_end_tb; // конец окна ретрая анонса (4с)
|
||||
void* ka_sleep_announce_timer; // таймер ретрая анонса спячки (ACTIVE-окно)
|
||||
uint8_t ka_announce_attempts; // попыток анонса в текущем окне
|
||||
uint8_t ka_announce_max_attempts; // лимит попыток на окно (сколько влезет, но ≥ min)
|
||||
uint8_t ka_fail_count; // кумулятивные провалы (сброс по ACK), ≥ KA_SLEEP_MAX_FAILS → down
|
||||
uint8_t pkt_sent_since_keepalive; // Флаг: был ли отправлен пакет с последнего keepalive тика
|
||||
uint32_t keepalive_sent_count; // Счётчик отправленных keepalive
|
||||
uint32_t keepalive_recv_count; // Счётчик полученных keepalive
|
||||
|
||||
@@ -14,9 +14,12 @@
|
||||
// forward declarations (static)
|
||||
static void keepalive_timer_cb(void* arg);
|
||||
static int etcp_all_links_down(struct ETCP_CONN* etcp);
|
||||
static void ka_stop_keepalive_timer(struct ETCP_LINK* link);
|
||||
static void ka_link_down(struct ETCP_LINK* link);
|
||||
static void ka_send_sleep_keepalive(struct ETCP_LINK* link);
|
||||
static void ka_send_sleep_ack(struct ETCP_LINK* link);
|
||||
static void ka_announce_retry_cb(void* arg);
|
||||
static void ka_announce_burst(struct ETCP_LINK* link);
|
||||
static void ka_enter_sleep(struct ETCP_LINK* link);
|
||||
static void ka_wake(struct ETCP_LINK* link);
|
||||
|
||||
@@ -130,10 +133,9 @@ static void keepalive_timer_cb(void* arg) {
|
||||
|
||||
link->keepalive_timer = NULL;
|
||||
|
||||
// В режиме спячки (я сплю ИЛИ пир спит) обе стороны молчат: только переарм —
|
||||
// никакого recovery/dead-link/отправки. Фазу сигналят фазовые переходы (ka_phase_cb),
|
||||
// NAT держит сам сон (sleep ≤ NAT-таймаут).
|
||||
if (link->ka_sleeping) {
|
||||
// Обычный keepalive работает только в normal-режиме. В спячке (моей или пира)
|
||||
// таймер гасится (ka_stop_keepalive_timer); если долетел — переарм и выход.
|
||||
if (link->ka_my_sleeping || link->ka_peer_sleeping) {
|
||||
link->keepalive_timer = uasync_set_timeout(link->etcp->instance->ua,
|
||||
link->ka_period_ms * 10, link, keepalive_timer_cb, "link_keepalive");
|
||||
return;
|
||||
@@ -190,10 +192,8 @@ static void keepalive_timer_cb(void* arg) {
|
||||
}
|
||||
}
|
||||
|
||||
// Период: спячка — фиксированный sleep-период; normal — адаптивный от базового.
|
||||
if (link->ka_sleeping)
|
||||
link->ka_period_ms = link->ka_sleep_period_ms;
|
||||
else if (link->pkt_sent_since_keepalive)
|
||||
// Период: адаптивный от базового (normal-режим).
|
||||
if (link->pkt_sent_since_keepalive)
|
||||
link->ka_period_ms = (uint16_t)link->keepalive_interval;
|
||||
else if (link->etcp->instance && link->etcp->instance->config &&
|
||||
link->etcp->instance->config->global.keepalive_adaptive) {
|
||||
@@ -202,11 +202,9 @@ static void keepalive_timer_cb(void* arg) {
|
||||
}
|
||||
link->pkt_sent_since_keepalive = 0;
|
||||
|
||||
// Отправка keepalive: в спячке НЕ шлём (ни клиент, ни сервер) — фазу сигналят фазовые переходы.
|
||||
if (!link->ka_sleeping) {
|
||||
if (!link->is_server || link->recv_keepalive)
|
||||
etcp_link_send_keepalive(link);
|
||||
}
|
||||
// Отправка обычного flagless-keepalive.
|
||||
if (!link->is_server || link->recv_keepalive)
|
||||
etcp_link_send_keepalive(link);
|
||||
|
||||
restart_timer:
|
||||
link->keepalive_timer = uasync_set_timeout(link->etcp->instance->ua, link->ka_period_ms * 10, link, keepalive_timer_cb, "link_keepalive");
|
||||
@@ -248,15 +246,70 @@ static uint16_t ka_sleep_duration_01s(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Отправить анонс спячки (обновить поля тайминга) + при необходимости заармить ретрай.
|
||||
// Отменить ретрай-таймер анонса спячки.
|
||||
static void ka_cancel_announce_retry(struct ETCP_LINK* link) {
|
||||
if (link->ka_sleep_announce_timer) {
|
||||
uasync_cancel_timeout(link->etcp->instance->ua, link->ka_sleep_announce_timer);
|
||||
link->ka_sleep_announce_timer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Гасим обычный keepalive-таймер (переход в спячку / пир уснул).
|
||||
static void ka_stop_keepalive_timer(struct ETCP_LINK* link) {
|
||||
if (link->keepalive_timer) {
|
||||
uasync_cancel_timeout(link->etcp->instance->ua, link->keepalive_timer);
|
||||
link->keepalive_timer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Максимум попыток анонса в ACTIVE-окне: сколько влезет в окно, но ≥ KA_SLEEP_MIN_ATTEMPTS.
|
||||
static uint8_t ka_announce_max_calc(struct ETCP_LINK* link) {
|
||||
uint16_t interval = link->keepalive_interval ? link->keepalive_interval : 200;
|
||||
uint32_t window_ms = (uint32_t)ka_active_remaining_01s() * 100; // остаток ACTIVE-окна, мс
|
||||
uint32_t fit = window_ms / interval + 1; // +1 за первый анонс
|
||||
if (fit < KA_SLEEP_MIN_ATTEMPTS) fit = KA_SLEEP_MIN_ATTEMPTS;
|
||||
if (fit > 255) fit = 255;
|
||||
return (uint8_t)fit;
|
||||
}
|
||||
|
||||
// Разрыв линка (мёртвый пир в спячке). Может освободить link — после вызова link не трогать.
|
||||
static void ka_link_down(struct ETCP_LINK* link) {
|
||||
if (!link || !link->etcp) return;
|
||||
link->recv_keepalive = 0;
|
||||
int old_link_status = link->link_status;
|
||||
link->link_status = 0;
|
||||
etcp_fire_link_status_cbk(link, link->link_state, old_link_status);
|
||||
struct ETCP_CONN* etcp = link->etcp;
|
||||
int is_tcp = link->is_tcp;
|
||||
int local_link_id = link->local_link_id;
|
||||
struct sockaddr_storage raddr = link->remote_addr;
|
||||
uint8_t remote_ka = link->remote_keepalive;
|
||||
int link_state = link->link_state, initialized = link->initialized;
|
||||
etcp_on_link_down(etcp, link);
|
||||
if (etcp->state == 2) return; /* conn+link освобождены каскадом */
|
||||
int alive = 0;
|
||||
for (struct ETCP_LINK* x = etcp->links; x; x = x->next) if (x == link) { alive = 1; break; }
|
||||
if (!alive) return;
|
||||
if (is_tcp && link->tcp_link) { stcp_link_close(link->tcp_link); link->tcp_link = NULL; }
|
||||
if (old_link_status) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "[%s] Link %d down (sleep unresponsive): addr=%s remote_ka=%d state=%d init=%d",
|
||||
etcp->log_name, local_link_id, sockaddr_storage_to_str(&raddr).str, remote_ka, link_state, initialized);
|
||||
}
|
||||
}
|
||||
|
||||
// Отправить один анонс спячки с таймингом.
|
||||
static void ka_send_sleep_keepalive(struct ETCP_LINK* link) {
|
||||
link->ka_active_remaining = ka_active_remaining_01s();
|
||||
link->ka_sleep_duration = ka_sleep_duration_01s();
|
||||
link->ka_announce_attempts++;
|
||||
etcp_link_send_keepalive_ex(link, KA_FLAG_SLEEP);
|
||||
if (!link->ka_sleep_acked && link->ka_sleep_announce_timer == NULL) {
|
||||
link->ka_sleep_announce_timer = uasync_set_timeout(link->etcp->instance->ua,
|
||||
KA_SLEEP_RETRY_MS * 10, link, ka_announce_retry_cb, "ka_sleep_announce");
|
||||
}
|
||||
}
|
||||
|
||||
// Заармить ретрай анонса (интервал = keepalive_interval).
|
||||
static void ka_arm_announce_retry(struct ETCP_LINK* link) {
|
||||
if (link->ka_sleep_announce_timer) return;
|
||||
link->ka_sleep_announce_timer = uasync_set_timeout(link->etcp->instance->ua,
|
||||
(uint32_t)link->keepalive_interval * 10, link, ka_announce_retry_cb, "ka_sleep_announce");
|
||||
}
|
||||
|
||||
// Ответ на анонс спячки пира: подтверждение доставки (без тайминга).
|
||||
@@ -268,48 +321,62 @@ static void ka_announce_retry_cb(void* arg) {
|
||||
struct ETCP_LINK* link = (struct ETCP_LINK*)arg;
|
||||
link->ka_sleep_announce_timer = NULL;
|
||||
if (!link || !link->etcp || !link->etcp->instance) return;
|
||||
if (!link->ka_sleeping || link->ka_sleep_acked) return; // анонс завершён
|
||||
if (get_time_tb() >= link->ka_sleep_announce_end_tb) return; // окно 4с вышло
|
||||
ka_send_sleep_keepalive(link); // ретрай
|
||||
if (!link->ka_my_sleeping || link->ka_sleep_acked) return; // анонс завершён
|
||||
|
||||
link->ka_fail_count++; // предыдущая попытка без ACK
|
||||
if (link->ka_fail_count >= KA_SLEEP_MAX_FAILS) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] peer unresponsive in sleep (%u fails) — link down",
|
||||
link->etcp->log_name, link->ka_fail_count);
|
||||
ka_link_down(link);
|
||||
return;
|
||||
}
|
||||
if (link->ka_announce_attempts >= link->ka_announce_max_attempts) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_KEEPALIVE, "[%s] announce attempts exhausted (%u/%u) in window",
|
||||
link->etcp->log_name, link->ka_announce_attempts, link->ka_announce_max_attempts);
|
||||
return;
|
||||
}
|
||||
ka_send_sleep_keepalive(link); // ретрай
|
||||
ka_arm_announce_retry(link);
|
||||
}
|
||||
|
||||
// Вход в спячку (анонс с ретраями) — моя сторона.
|
||||
// Анонс-серия (ACTIVE-окно): анонс + ретраи до ACK, лимит попыток на окно.
|
||||
static void ka_announce_burst(struct ETCP_LINK* link) {
|
||||
link->ka_announce_attempts = 0;
|
||||
link->ka_sleep_acked = 0;
|
||||
link->ka_announce_max_attempts = ka_announce_max_calc(link);
|
||||
ka_send_sleep_keepalive(link); // попытка 1
|
||||
ka_arm_announce_retry(link);
|
||||
}
|
||||
|
||||
// Вход в спячку — моя сторона: гасим normal-keepalive, анонс-серия.
|
||||
static void ka_enter_sleep(struct ETCP_LINK* link) {
|
||||
if (!link || !link->etcp || !link->etcp->instance) return;
|
||||
uint16_t period = ka_sleep_period_ms(link->etcp->instance);
|
||||
link->ka_sleeping = 1;
|
||||
link->ka_sleep_acked = 0;
|
||||
link->ka_my_sleeping = 1;
|
||||
link->ka_sleep_period_ms = period;
|
||||
link->ka_period_ms = period;
|
||||
link->keepalive_timeout = (uint32_t)period * KA_TIMEOUT_MULT;
|
||||
link->ka_sleep_announce_end_tb = get_time_tb() + (uint64_t)KA_SLEEP_ANNOUNCE_TIMEOUT_MS * 10;
|
||||
ka_stop_keepalive_timer(link);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] entering sleep (period=%dms)", link->etcp->log_name, period);
|
||||
ka_send_sleep_keepalive(link);
|
||||
restart_keepalive_timer_ms(link, period);
|
||||
ka_announce_burst(link);
|
||||
}
|
||||
|
||||
// Переанонс на входе в очередной цикл спячки (обновить фазу, без ре-ack).
|
||||
static void ka_reannounce(struct ETCP_LINK* link) {
|
||||
if (!link->ka_sleeping) return;
|
||||
ka_send_sleep_keepalive(link);
|
||||
}
|
||||
|
||||
// Пробуждение (форграунд) — обычный keepalive, возврат к базовому периоду.
|
||||
// Пробуждение (форграунд) — обычный keepalive, возврат к normal-режиму.
|
||||
static void ka_wake(struct ETCP_LINK* link) {
|
||||
if (!link || !link->etcp || !link->etcp->instance) return;
|
||||
if (!link->ka_sleeping) return;
|
||||
link->ka_sleeping = 0;
|
||||
if (!link->ka_my_sleeping) return;
|
||||
link->ka_my_sleeping = 0;
|
||||
link->ka_sleep_acked = 0;
|
||||
if (link->ka_sleep_announce_timer) {
|
||||
uasync_cancel_timeout(link->etcp->instance->ua, link->ka_sleep_announce_timer);
|
||||
link->ka_sleep_announce_timer = NULL;
|
||||
}
|
||||
link->ka_fail_count = 0;
|
||||
link->ka_announce_attempts = 0;
|
||||
ka_cancel_announce_retry(link);
|
||||
link->ka_period_ms = (uint16_t)link->keepalive_interval;
|
||||
link->keepalive_timeout = (uint32_t)link->keepalive_interval * KA_TIMEOUT_MULT;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] woke up, back to normal (interval=%dms)",
|
||||
link->etcp->log_name, link->keepalive_interval);
|
||||
etcp_link_send_keepalive(link);
|
||||
restart_keepalive_timer_ms(link, link->ka_period_ms);
|
||||
if (!link->ka_peer_sleeping)
|
||||
restart_keepalive_timer_ms(link, link->ka_period_ms);
|
||||
}
|
||||
|
||||
typedef void (*ka_link_fn)(struct ETCP_LINK* link);
|
||||
@@ -334,10 +401,10 @@ static void ka_for_each_link(struct UTUN_INSTANCE* inst, ka_link_fn fn) {
|
||||
}
|
||||
|
||||
#ifdef UTUN_HAVE_STANDBY
|
||||
// Вход в ACTIVE-окно — отправить keepalive с active_remaining>0 (флаг стоит).
|
||||
// Вход в ACTIVE-окно — анонс-серия с ретраями до ACK (active_remaining>0).
|
||||
static void ka_active_announce(struct ETCP_LINK* link) {
|
||||
if (!link->ka_sleeping) return;
|
||||
ka_send_sleep_keepalive(link);
|
||||
if (!link->ka_my_sleeping) return;
|
||||
ka_announce_burst(link);
|
||||
}
|
||||
|
||||
// Фаза duty-cycle: SLEEP → переанонс спячки; ACTIVE → анонс активности (флаг всегда стоит).
|
||||
@@ -345,8 +412,11 @@ static void ka_phase_cb(int sleeping, void* arg) {
|
||||
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)arg;
|
||||
if (!inst || inst->client_type != CLIENT_TYPE_MOBILE) return;
|
||||
if (sleeping) {
|
||||
ka_for_each_link(inst, ka_reannounce);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "keepalive: duty-cycle SLEEP phase, re-announce (node=%016llx)",
|
||||
/* SLEEP-фаза не анонсируется отдельно — её тайминг уже передан в ACTIVE-окне
|
||||
(active_remaining→0 + sleep_duration), пир выведет переход по своему фазовому
|
||||
таймеру. Гасим ретраи анонса окна, чтобы не дослали лишний анонс в SLEEP. */
|
||||
ka_for_each_link(inst, ka_cancel_announce_retry);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "keepalive: duty-cycle SLEEP phase (no announce, window retries cancelled, node=%016llx)",
|
||||
(unsigned long long)inst->node_id);
|
||||
} else {
|
||||
ka_for_each_link(inst, ka_active_announce);
|
||||
@@ -409,24 +479,24 @@ int etcp_keepalive_on_recv(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt,
|
||||
uint8_t flags = (pkt->data_len >= 2) ? pkt->data[1] : 0;
|
||||
|
||||
if (flags & KA_FLAG_SLEEP_ACK) {
|
||||
/* Ответ на мой анонс сна — команда дошла, можно спать без ретраев. */
|
||||
/* Ответ на мой анонс сна — команда дошла, ретраи сняты, счётчик провалов обнулён. */
|
||||
link->ka_sleep_acked = 1;
|
||||
if (link->ka_sleep_announce_timer) {
|
||||
uasync_cancel_timeout(e_sock->instance->ua, link->ka_sleep_announce_timer);
|
||||
link->ka_sleep_announce_timer = NULL;
|
||||
}
|
||||
link->ka_fail_count = 0;
|
||||
link->ka_announce_attempts = 0;
|
||||
ka_cancel_announce_retry(link);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_KEEPALIVE, "[%s] sleep ack received", link->etcp->log_name);
|
||||
} else if (flags & KA_FLAG_SLEEP) {
|
||||
/* Пир объявил сон — заткнуться и вести фазу троттлинга по таймингу. */
|
||||
/* Пир объявил сон — заткнуться (гасим свой normal-keepalive) и вести фазу троттлинга. */
|
||||
uint16_t active_remaining = (pkt->data_len >= 6)
|
||||
? (pkt->data[4] | ((uint16_t)pkt->data[5] << 8)) : 0;
|
||||
uint16_t sleep_duration = (pkt->data_len >= 8)
|
||||
? (pkt->data[6] | ((uint16_t)pkt->data[7] << 8)) : 0;
|
||||
link->ka_sleeping = 1;
|
||||
link->ka_peer_sleeping = 1;
|
||||
link->ka_sleep_period_ms = peer_period;
|
||||
link->ka_active_remaining = active_remaining;
|
||||
link->ka_sleep_duration = sleep_duration;
|
||||
link->ka_period_ms = peer_period;
|
||||
ka_stop_keepalive_timer(link);
|
||||
if (link->etcp) {
|
||||
link->etcp->peer_active_remaining = active_remaining;
|
||||
link->etcp->peer_sleep_duration = sleep_duration;
|
||||
@@ -438,10 +508,9 @@ int etcp_keepalive_on_recv(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt,
|
||||
(int)(active_remaining / 10), (int)(active_remaining % 10),
|
||||
(int)(sleep_duration / 10), (int)(sleep_duration % 10));
|
||||
ka_send_sleep_ack(link);
|
||||
restart_keepalive_timer_ms(link, peer_period);
|
||||
} else if (link->ka_sleeping) {
|
||||
/* Обычный keepalive (без флагов): пир проснулся — вернуться в обычный режим. */
|
||||
link->ka_sleeping = 0;
|
||||
} else if (link->ka_peer_sleeping) {
|
||||
/* Обычный keepalive (без флагов): пир проснулся — вернуться в normal-режим. */
|
||||
link->ka_peer_sleeping = 0;
|
||||
link->ka_period_ms = (uint16_t)link->keepalive_interval;
|
||||
link->keepalive_timeout = (uint32_t)link->keepalive_interval * KA_TIMEOUT_MULT;
|
||||
if (link->etcp) {
|
||||
@@ -453,7 +522,8 @@ int etcp_keepalive_on_recv(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt,
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] peer woke up, back to normal (interval=%dms)",
|
||||
link->etcp->log_name, link->keepalive_interval);
|
||||
restart_keepalive_timer_ms(link, link->ka_period_ms);
|
||||
if (!link->ka_my_sleeping)
|
||||
restart_keepalive_timer_ms(link, link->ka_period_ms);
|
||||
}
|
||||
|
||||
link->keepalive_recv_count++;
|
||||
@@ -472,13 +542,15 @@ static void etcp_keepalive_on_activity(struct UTUN_INSTANCE* inst, int active, v
|
||||
active ? "ACTIVE(wake)" : "STANDBY(sleep)", (unsigned long long)inst->node_id);
|
||||
}
|
||||
|
||||
// Переанонс спячки на только что (пере)инициализированном линке (рестарт пира).
|
||||
void etcp_keepalive_announce_mode(struct ETCP_LINK* link) {
|
||||
if (!link || !link->etcp || !link->etcp->instance) return;
|
||||
// На только что (пере)инициализированном линке: если я в standby — анонсировать спячку
|
||||
// (возвращает 1, обычный flagless в этом случае слать НЕ надо); иначе 0.
|
||||
int etcp_keepalive_announce_mode(struct ETCP_LINK* link) {
|
||||
if (!link || !link->etcp || !link->etcp->instance) return 0;
|
||||
struct UTUN_INSTANCE* inst = link->etcp->instance;
|
||||
if (inst->client_type != CLIENT_TYPE_MOBILE) return;
|
||||
if (!inst->standby_enabled) return; /* не в standby — обычный режим */
|
||||
if (inst->client_type != CLIENT_TYPE_MOBILE) return 0;
|
||||
if (!inst->standby_enabled) return 0; /* не в standby — обычный режим */
|
||||
ka_enter_sleep(link);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void etcp_keepalive_register(struct UTUN_INSTANCE* inst) {
|
||||
|
||||
@@ -12,9 +12,20 @@ extern "C" {
|
||||
|
||||
/* ─── Протокол спячки поверх keepalive-пакета (code ETCP_KEEPALIVE = 0x08) ───
|
||||
*
|
||||
* Режим спячки (standby) передаётся флагом в самом keepalive (персистентный,
|
||||
* стоит весь фон). Тайминг duty-cycle (SLEEP 120с / ACTIVE 2с) — двумя полями:
|
||||
* Обычный keepalive (normal-режим) и спячка (standby) — раздельные механизмы
|
||||
* (разные таймеры, разный код):
|
||||
*
|
||||
* normal: периодический flagless-keepalive (адаптивный период) + dead-link
|
||||
* по keepalive_timeout. Активен только когда бодрствуют обе стороны.
|
||||
*
|
||||
* standby (спящий узел): периодический flagless НЕ шлётся вовсе. На фазовых
|
||||
* переходах duty-cycle (SLEEP 120с / ACTIVE 2с) шлётся анонс
|
||||
* KA_FLAG_SLEEP с таймингом. В ACTIVE-окне анонс ретраится каждые
|
||||
* keepalive_interval до ACK (лимит: сколько влезет в окно, но ≥
|
||||
* KA_SLEEP_MIN_ATTEMPTS). Кумулятивные провалы (сброс по ACK) — при
|
||||
* KA_SLEEP_MAX_FAILS → link down (детект мёртвого пира в спячке).
|
||||
*
|
||||
* Формат пакета:
|
||||
* data[0] = ETCP_KEEPALIVE (0x08)
|
||||
* data[1] = flags (bit0 = KA_FLAG_SLEEP — «я сплю», bit1 = KA_FLAG_SLEEP_ACK — ответ)
|
||||
* data[2..3] = ka_period_ms (LE 16-bit) — база dead-link таймаута, мс
|
||||
@@ -22,18 +33,15 @@ extern "C" {
|
||||
* data[6..7] = sleep_duration (LE 16-bit, x0.1с) — длительность SLEEP-фазы
|
||||
*
|
||||
* Протокол полностью симметричный (без привязки к STCP-роли is_server):
|
||||
* спящий узел шлёт анонс KA_FLAG_SLEEP (с таймингом), пир на это «затыкается» —
|
||||
* перестаёт слать периодические keepalive — и отвечает одним ACK (KA_FLAG_SLEEP_ACK).
|
||||
* Спящий, получив ACK, снимает ретраи анонса и спит молча (NAT держит сам сон,
|
||||
* sleep ≤ NAT-таймаута). В standby обе стороны НЕ шлют периодические keepalive.
|
||||
* спящий шлёт анонс KA_FLAG_SLEEP, пир «затыкается» (гасит свой normal-keepalive)
|
||||
* и отвечает KA_FLAG_SLEEP_ACK. Спящий, получив ACK, снимает ретраи. В standby
|
||||
* обе стороны НЕ шлют периодические flagless-keepalive.
|
||||
*
|
||||
* По таймингу анонса пир ведёт фазу троттлинга duty-cycle (SLEEP-фаза → тормозим
|
||||
* протоколы, ACTIVE-окно → обмен). Анонс повторяется на фазовых переходах: вход
|
||||
* в SLEEP (active_remaining=0, sleep_duration=полная) и вход в ACTIVE
|
||||
* (active_remaining=остаток окна).
|
||||
* протоколы, ACTIVE-окно → обмен).
|
||||
*
|
||||
* Пробуждение (форграунд): спящий шлёт обычный keepalive (без флагов) — пир снимает
|
||||
* standby и фазу троттлинга, возвращается в обычный режим.
|
||||
* Пробуждение (форграунд): спящий шлёт flagless-keepalive — пир снимает standby
|
||||
* и фазу троттлинга, возвращается в normal-режим.
|
||||
*/
|
||||
|
||||
// Флаги keepalive-пакета (data[1])
|
||||
@@ -41,8 +49,8 @@ extern "C" {
|
||||
#define KA_FLAG_SLEEP_ACK 0x02 // «подтверждаю твой сон» (ответ на анонс)
|
||||
|
||||
// Параметры анонса спячки
|
||||
#define KA_SLEEP_RETRY_MS 200 // ретрай анонса спячки (клиент)
|
||||
#define KA_SLEEP_ANNOUNCE_TIMEOUT_MS 4000 // суммарный таймаут анонса спячки
|
||||
#define KA_SLEEP_MIN_ATTEMPTS 5 // минимум попыток анонса в ACTIVE-окне
|
||||
#define KA_SLEEP_MAX_FAILS 10 // провалов анонса подряд → link down
|
||||
|
||||
// --- перенесены из etcp_connections.c (имена сохранены) ---
|
||||
uint16_t negotiate_keepalive(uint8_t my_type, uint16_t my_ka,
|
||||
@@ -60,9 +68,10 @@ int etcp_keepalive_on_recv(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt,
|
||||
// Вызывается из init_connections.
|
||||
void etcp_keepalive_register(struct UTUN_INSTANCE* inst);
|
||||
|
||||
// Переанонсировать спячку на только что (пере)инициализированном линке.
|
||||
// Вызывается из etcp_link_enter_ready_tcp / handle_init_response_client.
|
||||
void etcp_keepalive_announce_mode(struct ETCP_LINK* link);
|
||||
// На только что (пере)инициализированном линке: если я в standby — анонсировать
|
||||
// спячку (возвращает 1, обычный flagless в этом случае слать НЕ надо); иначе 0.
|
||||
// Вызывается из etcp_link_enter_ready_tcp / handle_init_response_client / TRY_RECONNECT.
|
||||
int etcp_keepalive_announce_mode(struct ETCP_LINK* link);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user