Browse Source

keepalive: симметричный sleep-протокол (без is_server) + отдельный ACK-бит

proxy
evgeny 2 weeks ago
parent
commit
a3df1720b4
  1. 167
      src/transport_layer/etcp_keepalive.c
  2. 26
      src/transport_layer/etcp_keepalive.h

167
src/transport_layer/etcp_keepalive.c

@ -15,9 +15,10 @@
static void keepalive_timer_cb(void* arg); static void keepalive_timer_cb(void* arg);
static int etcp_all_links_down(struct ETCP_CONN* etcp); static int etcp_all_links_down(struct ETCP_CONN* etcp);
static void ka_send_sleep_keepalive(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_retry_cb(void* arg);
static void ka_client_enter_sleep(struct ETCP_LINK* link); static void ka_enter_sleep(struct ETCP_LINK* link);
static void ka_client_wake(struct ETCP_LINK* link); static void ka_wake(struct ETCP_LINK* link);
// === перенесено из etcp_connections.c без изменений === // === перенесено из etcp_connections.c без изменений ===
@ -35,7 +36,7 @@ uint16_t negotiate_keepalive(uint8_t my_type, uint16_t my_ka,
} }
// Send keepalive packet: code(0x08) + flags + ka_period_ms + [active_remaining + sleep_duration] // Send keepalive packet: code(0x08) + flags + ka_period_ms + [active_remaining + sleep_duration]
void etcp_link_send_keepalive(struct ETCP_LINK* link) { static void etcp_link_send_keepalive_ex(struct ETCP_LINK* link, uint8_t flags) {
DEBUG_TRACE(DEBUG_CATEGORY_KEEPALIVE, ""); DEBUG_TRACE(DEBUG_CATEGORY_KEEPALIVE, "");
if (!link || !link->etcp || !link->etcp->instance) return; if (!link || !link->etcp || !link->etcp->instance) return;
@ -47,10 +48,10 @@ void etcp_link_send_keepalive(struct ETCP_LINK* link) {
dgram->link = link; dgram->link = link;
dgram->data[0] = ETCP_KEEPALIVE; dgram->data[0] = ETCP_KEEPALIVE;
dgram->data[1] = link->ka_sleeping ? KA_FLAG_SLEEP : 0; dgram->data[1] = flags;
dgram->data[2] = link->ka_period_ms & 0xFF; dgram->data[2] = link->ka_period_ms & 0xFF;
dgram->data[3] = link->ka_period_ms >> 8; dgram->data[3] = link->ka_period_ms >> 8;
if (link->ka_sleeping) { if (flags & KA_FLAG_SLEEP) {
dgram->data[4] = link->ka_active_remaining & 0xFF; dgram->data[4] = link->ka_active_remaining & 0xFF;
dgram->data[5] = link->ka_active_remaining >> 8; dgram->data[5] = link->ka_active_remaining >> 8;
dgram->data[6] = link->ka_sleep_duration & 0xFF; dgram->data[6] = link->ka_sleep_duration & 0xFF;
@ -69,6 +70,11 @@ void etcp_link_send_keepalive(struct ETCP_LINK* link) {
u_free(dgram); u_free(dgram);
} }
// Обычный keepalive (без sleep-флагов): «жив» / «проснулся».
void etcp_link_send_keepalive(struct ETCP_LINK* link) {
etcp_link_send_keepalive_ex(link, 0);
}
// Check if all links for an ETCP_CONN are down // Check if all links for an ETCP_CONN are down
// Returns 1 if all links are down or no links exist, 0 otherwise // Returns 1 if all links are down or no links exist, 0 otherwise
static int etcp_all_links_down(struct ETCP_CONN* etcp) { static int etcp_all_links_down(struct ETCP_CONN* etcp) {
@ -124,9 +130,10 @@ static void keepalive_timer_cb(void* arg) {
link->keepalive_timer = NULL; link->keepalive_timer = NULL;
// Клиент в standby: ничего не делаем (recovery/dead-link/отправка) — только переарм. // В режиме спячки (я сплю ИЛИ пир спит) обе стороны молчат: только переарм —
// Фазу сигналят фазовые переходы (ka_phase_cb), NAT держит сам сон (sleep ≤ NAT-таймаут). // никакого recovery/dead-link/отправки. Фазу сигналят фазовые переходы (ka_phase_cb),
if (link->ka_sleeping && !link->is_server) { // NAT держит сам сон (sleep ≤ NAT-таймаут).
if (link->ka_sleeping) {
link->keepalive_timer = uasync_set_timeout(link->etcp->instance->ua, link->keepalive_timer = uasync_set_timeout(link->etcp->instance->ua,
link->ka_period_ms * 10, link, keepalive_timer_cb, "link_keepalive"); link->ka_period_ms * 10, link, keepalive_timer_cb, "link_keepalive");
return; return;
@ -241,17 +248,22 @@ static uint16_t ka_sleep_duration_01s(void) {
#endif #endif
} }
// Отправить keepalive спячки (обновить поля тайминга) + при необходимости заармить ретрай. // Отправить анонс спячки (обновить поля тайминга) + при необходимости заармить ретрай.
static void ka_send_sleep_keepalive(struct ETCP_LINK* link) { static void ka_send_sleep_keepalive(struct ETCP_LINK* link) {
link->ka_active_remaining = ka_active_remaining_01s(); link->ka_active_remaining = ka_active_remaining_01s();
link->ka_sleep_duration = ka_sleep_duration_01s(); link->ka_sleep_duration = ka_sleep_duration_01s();
etcp_link_send_keepalive(link); etcp_link_send_keepalive_ex(link, KA_FLAG_SLEEP);
if (!link->ka_sleep_acked && link->ka_sleep_announce_timer == NULL) { if (!link->ka_sleep_acked && link->ka_sleep_announce_timer == NULL) {
link->ka_sleep_announce_timer = uasync_set_timeout(link->etcp->instance->ua, 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"); KA_SLEEP_RETRY_MS * 10, link, ka_announce_retry_cb, "ka_sleep_announce");
} }
} }
// Ответ на анонс спячки пира: подтверждение доставки (без тайминга).
static void ka_send_sleep_ack(struct ETCP_LINK* link) {
etcp_link_send_keepalive_ex(link, KA_FLAG_SLEEP_ACK);
}
static void ka_announce_retry_cb(void* arg) { static void ka_announce_retry_cb(void* arg) {
struct ETCP_LINK* link = (struct ETCP_LINK*)arg; struct ETCP_LINK* link = (struct ETCP_LINK*)arg;
link->ka_sleep_announce_timer = NULL; link->ka_sleep_announce_timer = NULL;
@ -261,8 +273,8 @@ static void ka_announce_retry_cb(void* arg) {
ka_send_sleep_keepalive(link); // ретрай ka_send_sleep_keepalive(link); // ретрай
} }
// Клиент: вход в спячку (анонс с ретраями). // Вход в спячку (анонс с ретраями) — моя сторона.
static void ka_client_enter_sleep(struct ETCP_LINK* link) { static void ka_enter_sleep(struct ETCP_LINK* link) {
if (!link || !link->etcp || !link->etcp->instance) return; if (!link || !link->etcp || !link->etcp->instance) return;
uint16_t period = ka_sleep_period_ms(link->etcp->instance); uint16_t period = ka_sleep_period_ms(link->etcp->instance);
link->ka_sleeping = 1; link->ka_sleeping = 1;
@ -271,19 +283,19 @@ static void ka_client_enter_sleep(struct ETCP_LINK* link) {
link->ka_period_ms = period; link->ka_period_ms = period;
link->keepalive_timeout = (uint32_t)period * KA_TIMEOUT_MULT; 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; link->ka_sleep_announce_end_tb = get_time_tb() + (uint64_t)KA_SLEEP_ANNOUNCE_TIMEOUT_MS * 10;
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] client entering sleep (period=%dms)", link->etcp->log_name, period); DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] entering sleep (period=%dms)", link->etcp->log_name, period);
ka_send_sleep_keepalive(link); ka_send_sleep_keepalive(link);
restart_keepalive_timer_ms(link, period); restart_keepalive_timer_ms(link, period);
} }
// Клиент: переанонс на входе в очередной цикл спячки (обновить фазу, без ре-ack). // Переанонс на входе в очередной цикл спячки (обновить фазу, без ре-ack).
static void ka_client_reannounce(struct ETCP_LINK* link) { static void ka_reannounce(struct ETCP_LINK* link) {
if (!link->ka_sleeping) return; if (!link->ka_sleeping) return;
ka_send_sleep_keepalive(link); ka_send_sleep_keepalive(link);
} }
// Клиент: пробуждение (форграунд) — обычный keepalive, возврат к базовому периоду. // Пробуждение (форграунд) — обычный keepalive, возврат к базовому периоду.
static void ka_client_wake(struct ETCP_LINK* link) { static void ka_wake(struct ETCP_LINK* link) {
if (!link || !link->etcp || !link->etcp->instance) return; if (!link || !link->etcp || !link->etcp->instance) return;
if (!link->ka_sleeping) return; if (!link->ka_sleeping) return;
link->ka_sleeping = 0; link->ka_sleeping = 0;
@ -294,7 +306,7 @@ static void ka_client_wake(struct ETCP_LINK* link) {
} }
link->ka_period_ms = (uint16_t)link->keepalive_interval; link->ka_period_ms = (uint16_t)link->keepalive_interval;
link->keepalive_timeout = (uint32_t)link->keepalive_interval * KA_TIMEOUT_MULT; link->keepalive_timeout = (uint32_t)link->keepalive_interval * KA_TIMEOUT_MULT;
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] client woke up, back to normal (interval=%dms)", DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] woke up, back to normal (interval=%dms)",
link->etcp->log_name, link->keepalive_interval); link->etcp->log_name, link->keepalive_interval);
etcp_link_send_keepalive(link); etcp_link_send_keepalive(link);
restart_keepalive_timer_ms(link, link->ka_period_ms); restart_keepalive_timer_ms(link, link->ka_period_ms);
@ -322,8 +334,8 @@ static void ka_for_each_link(struct UTUN_INSTANCE* inst, ka_link_fn fn) {
} }
#ifdef UTUN_HAVE_STANDBY #ifdef UTUN_HAVE_STANDBY
// Клиент: вход в ACTIVE-окно — отправить keepalive с active_remaining>0 (флаг стоит). // Вход в ACTIVE-окно — отправить keepalive с active_remaining>0 (флаг стоит).
static void ka_client_active_announce(struct ETCP_LINK* link) { static void ka_active_announce(struct ETCP_LINK* link) {
if (!link->ka_sleeping) return; if (!link->ka_sleeping) return;
ka_send_sleep_keepalive(link); ka_send_sleep_keepalive(link);
} }
@ -333,37 +345,37 @@ static void ka_phase_cb(int sleeping, void* arg) {
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)arg; struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)arg;
if (!inst || inst->client_type != CLIENT_TYPE_MOBILE) return; if (!inst || inst->client_type != CLIENT_TYPE_MOBILE) return;
if (sleeping) { if (sleeping) {
ka_for_each_link(inst, ka_client_reannounce); ka_for_each_link(inst, ka_reannounce);
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "keepalive: duty-cycle SLEEP phase, re-announce (node=%016llx)", DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "keepalive: duty-cycle SLEEP phase, re-announce (node=%016llx)",
(unsigned long long)inst->node_id); (unsigned long long)inst->node_id);
} else { } else {
ka_for_each_link(inst, ka_client_active_announce); ka_for_each_link(inst, ka_active_announce);
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "keepalive: duty-cycle ACTIVE phase, announce (node=%016llx)", DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "keepalive: duty-cycle ACTIVE phase, announce (node=%016llx)",
(unsigned long long)inst->node_id); (unsigned long long)inst->node_id);
} }
} }
#endif #endif
/* ── Сервер: машина фаз троттлинга (по active_remaining/sleep_duration) ── */ /* ── Пир: машина фаз троттлинга (по active_remaining/sleep_duration) ── */
static void ka_server_arm_phase(struct ETCP_CONN* etcp); static void ka_peer_arm_phase(struct ETCP_CONN* etcp);
static void ka_server_set_phase(struct ETCP_CONN* etcp, int sleeping) { static void ka_peer_set_phase(struct ETCP_CONN* etcp, int sleeping) {
if (!etcp) return; if (!etcp) return;
if (etcp->peer_sleep_phase == (uint8_t)(sleeping ? 1 : 0)) return; if (etcp->peer_sleep_phase == (uint8_t)(sleeping ? 1 : 0)) return;
etcp->peer_sleep_phase = sleeping ? 1 : 0; etcp->peer_sleep_phase = sleeping ? 1 : 0;
utun_fire_peer_sleep_cbk(etcp->instance, etcp->peer_node_id, sleeping); utun_fire_peer_sleep_cbk(etcp->instance, etcp->peer_node_id, sleeping);
} }
static void ka_server_phase_timer_cb(void* arg) { static void ka_peer_phase_timer_cb(void* arg) {
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg; struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
etcp->peer_phase_timer = NULL; etcp->peer_phase_timer = NULL;
if (!etcp || etcp->state == 2) return; if (!etcp || etcp->state == 2) return;
ka_server_set_phase(etcp, etcp->peer_sleep_phase ? 0 : 1); ka_peer_set_phase(etcp, etcp->peer_sleep_phase ? 0 : 1);
ka_server_arm_phase(etcp); ka_peer_arm_phase(etcp);
} }
static void ka_server_arm_phase(struct ETCP_CONN* etcp) { static void ka_peer_arm_phase(struct ETCP_CONN* etcp) {
if (!etcp || !etcp->instance) return; if (!etcp || !etcp->instance) return;
uint16_t dur_01s = etcp->peer_sleep_phase ? etcp->peer_sleep_duration : etcp->peer_active_remaining; uint16_t dur_01s = etcp->peer_sleep_phase ? etcp->peer_sleep_duration : etcp->peer_active_remaining;
if (etcp->peer_phase_timer) { if (etcp->peer_phase_timer) {
@ -372,12 +384,12 @@ static void ka_server_arm_phase(struct ETCP_CONN* etcp) {
} }
/* гвард: нулевой тайминг (битый/legacy пакет) → не армать, иначе timer=0 даёт busy-loop */ /* гвард: нулевой тайминг (битый/legacy пакет) → не армать, иначе timer=0 даёт busy-loop */
if (dur_01s == 0) { if (dur_01s == 0) {
DEBUG_WARN(DEBUG_CATEGORY_KEEPALIVE, "[%s] server: zero phase duration (phase=%d), skip arming", DEBUG_WARN(DEBUG_CATEGORY_KEEPALIVE, "[%s] peer: zero phase duration (phase=%d), skip arming",
etcp->log_name, etcp->peer_sleep_phase); etcp->log_name, etcp->peer_sleep_phase);
return; return;
} }
etcp->peer_phase_timer = uasync_set_timeout(etcp->instance->ua, (int)((uint32_t)dur_01s * 1000), etcp->peer_phase_timer = uasync_set_timeout(etcp->instance->ua, (int)((uint32_t)dur_01s * 1000),
etcp, ka_server_phase_timer_cb, "peer_phase"); etcp, ka_peer_phase_timer_cb, "peer_phase");
} }
int etcp_keepalive_on_recv(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt, int etcp_keepalive_on_recv(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt,
@ -396,61 +408,52 @@ 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; uint8_t flags = (pkt->data_len >= 2) ? pkt->data[1] : 0;
if (flags & KA_FLAG_SLEEP) { 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;
}
DEBUG_DEBUG(DEBUG_CATEGORY_KEEPALIVE, "[%s] sleep ack received", link->etcp->log_name);
} else if (flags & KA_FLAG_SLEEP) {
/* Пир объявил сон — заткнуться и вести фазу троттлинга по таймингу. */
uint16_t active_remaining = (pkt->data_len >= 6) uint16_t active_remaining = (pkt->data_len >= 6)
? (pkt->data[4] | ((uint16_t)pkt->data[5] << 8)) : 0; ? (pkt->data[4] | ((uint16_t)pkt->data[5] << 8)) : 0;
uint16_t sleep_duration = (pkt->data_len >= 8) uint16_t sleep_duration = (pkt->data_len >= 8)
? (pkt->data[6] | ((uint16_t)pkt->data[7] << 8)) : 0; ? (pkt->data[6] | ((uint16_t)pkt->data[7] << 8)) : 0;
if (link->is_server) { link->ka_sleeping = 1;
/* Сервер: пир в спячке — зеркалим флаг/поля и ведём фазу троттлинга. link->ka_sleep_period_ms = peer_period;
ack-эхо шлём только на анонсе (переход 0→1), дальше в спячке молчим. */ link->ka_active_remaining = active_remaining;
int was_sleeping = link->ka_sleeping; link->ka_sleep_duration = sleep_duration;
link->ka_sleeping = 1; link->ka_period_ms = peer_period;
link->ka_sleep_period_ms = peer_period; if (link->etcp) {
link->ka_active_remaining = active_remaining; link->etcp->peer_active_remaining = active_remaining;
link->ka_sleep_duration = sleep_duration; link->etcp->peer_sleep_duration = sleep_duration;
link->ka_period_ms = peer_period; ka_peer_set_phase(link->etcp, active_remaining == 0);
if (link->etcp) { ka_peer_arm_phase(link->etcp);
link->etcp->peer_active_remaining = active_remaining;
link->etcp->peer_sleep_duration = sleep_duration;
ka_server_set_phase(link->etcp, active_remaining == 0);
ka_server_arm_phase(link->etcp);
}
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] server: peer sleeping (period=%dms active_rem=%d.%ds sleep_dur=%d.%ds), echo=%s",
link->etcp->log_name, peer_period,
(int)(active_remaining / 10), (int)(active_remaining % 10),
(int)(sleep_duration / 10), (int)(sleep_duration % 10),
was_sleeping ? "no" : "ack");
if (!was_sleeping)
etcp_link_send_keepalive(link);
restart_keepalive_timer_ms(link, peer_period);
} else {
/* Клиент: 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;
}
DEBUG_DEBUG(DEBUG_CATEGORY_KEEPALIVE, "[%s] client: sleep ack received", link->etcp->log_name);
} }
} else { DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] peer sleeping (period=%dms active_rem=%d.%ds sleep_dur=%d.%ds)",
/* Обычный keepalive (без флага). */ link->etcp->log_name, peer_period,
if (link->is_server && link->ka_sleeping) { (int)(active_remaining / 10), (int)(active_remaining % 10),
/* Пир проснулся (форграунд) — вернуться в обычный режим. */ (int)(sleep_duration / 10), (int)(sleep_duration % 10));
link->ka_sleeping = 0; ka_send_sleep_ack(link);
link->ka_period_ms = (uint16_t)link->keepalive_interval; restart_keepalive_timer_ms(link, peer_period);
link->keepalive_timeout = (uint32_t)link->keepalive_interval * KA_TIMEOUT_MULT; } else if (link->ka_sleeping) {
if (link->etcp) { /* Обычный keepalive (без флагов): пир проснулся — вернуться в обычный режим. */
if (link->etcp->peer_phase_timer) { link->ka_sleeping = 0;
uasync_cancel_timeout(link->etcp->instance->ua, link->etcp->peer_phase_timer); link->ka_period_ms = (uint16_t)link->keepalive_interval;
link->etcp->peer_phase_timer = NULL; link->keepalive_timeout = (uint32_t)link->keepalive_interval * KA_TIMEOUT_MULT;
} if (link->etcp) {
ka_server_set_phase(link->etcp, 0); if (link->etcp->peer_phase_timer) {
uasync_cancel_timeout(link->etcp->instance->ua, link->etcp->peer_phase_timer);
link->etcp->peer_phase_timer = NULL;
} }
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] server: peer woke up, back to normal (interval=%dms)", ka_peer_set_phase(link->etcp, 0);
link->etcp->log_name, link->keepalive_interval);
restart_keepalive_timer_ms(link, link->ka_period_ms);
} }
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);
} }
link->keepalive_recv_count++; link->keepalive_recv_count++;
@ -464,18 +467,18 @@ int etcp_keepalive_on_recv(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt,
static void etcp_keepalive_on_activity(struct UTUN_INSTANCE* inst, int active, void* arg) { static void etcp_keepalive_on_activity(struct UTUN_INSTANCE* inst, int active, void* arg) {
(void)arg; (void)arg;
if (!inst || inst->client_type != CLIENT_TYPE_MOBILE) return; if (!inst || inst->client_type != CLIENT_TYPE_MOBILE) return;
ka_for_each_link(inst, active ? ka_client_wake : ka_client_enter_sleep); ka_for_each_link(inst, active ? ka_wake : ka_enter_sleep);
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "keepalive activity change: %s -> applied to links (node=%016llx)", DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "keepalive activity change: %s -> applied to links (node=%016llx)",
active ? "ACTIVE(wake)" : "STANDBY(sleep)", (unsigned long long)inst->node_id); active ? "ACTIVE(wake)" : "STANDBY(sleep)", (unsigned long long)inst->node_id);
} }
// Переанонс спячки на только что (пере)инициализированном линке (рестарт сервера). // Переанонс спячки на только что (пере)инициализированном линке (рестарт пира).
void etcp_keepalive_announce_mode(struct ETCP_LINK* link) { void etcp_keepalive_announce_mode(struct ETCP_LINK* link) {
if (!link || !link->etcp || !link->etcp->instance) return; if (!link || !link->etcp || !link->etcp->instance) return;
struct UTUN_INSTANCE* inst = link->etcp->instance; struct UTUN_INSTANCE* inst = link->etcp->instance;
if (inst->client_type != CLIENT_TYPE_MOBILE) return; if (inst->client_type != CLIENT_TYPE_MOBILE) return;
if (!inst->standby_enabled) return; /* не в standby — обычный режим */ if (!inst->standby_enabled) return; /* не в standby — обычный режим */
ka_client_enter_sleep(link); ka_enter_sleep(link);
} }
void etcp_keepalive_register(struct UTUN_INSTANCE* inst) { void etcp_keepalive_register(struct UTUN_INSTANCE* inst) {

26
src/transport_layer/etcp_keepalive.h

@ -16,27 +16,29 @@ extern "C" {
* стоит весь фон). Тайминг duty-cycle (SLEEP 120с / ACTIVE 2с) — двумя полями: * стоит весь фон). Тайминг duty-cycle (SLEEP 120с / ACTIVE 2с) — двумя полями:
* *
* data[0] = ETCP_KEEPALIVE (0x08) * data[0] = ETCP_KEEPALIVE (0x08)
* data[1] = flags (bit0 = KA_FLAG_SLEEP — standby) * data[1] = flags (bit0 = KA_FLAG_SLEEP — «я сплю», bit1 = KA_FLAG_SLEEP_ACK — ответ)
* data[2..3] = ka_period_ms (LE 16-bit) — база dead-link таймаута, мс * data[2..3] = ka_period_ms (LE 16-bit) — база dead-link таймаута, мс
* data[4..5] = active_remaining (LE 16-bit, x0.1с) — до SLEEP-фазы (0 = уже спим) * data[4..5] = active_remaining (LE 16-bit, x0.1с) — до SLEEP-фазы (0 = уже спим)
* data[6..7] = sleep_duration (LE 16-bit, x0.1с) — длительность SLEEP-фазы * data[6..7] = sleep_duration (LE 16-bit, x0.1с) — длительность SLEEP-фазы
* *
* В standby обе стороны НЕ шлют периодические keepalive: standby-время само * Протокол полностью симметричный (без привязки к STCP-роли is_server):
* удерживает NAT (sleep ≤ NAT-таймаута). Keepalive шлёт только клиент, и только * спящий узел шлёт анонс KA_FLAG_SLEEP (с таймингом), пир на это «затыкается» —
* на фазовых переходах duty-cycle: вход в SLEEP (active_remaining=0, * перестаёт слать периодические keepalive — и отвечает одним ACK (KA_FLAG_SLEEP_ACK).
* sleep_duration=полная) и вход в ACTIVE (active_remaining=остаток окна). Сервер * Спящий, получив ACK, снимает ретраи анонса и спит молча (NAT держит сам сон,
* по этим полям ведёт фазу троттлинга (SLEEP-фаза → тормозим протоколы, * sleep ≤ NAT-таймаута). В standby обе стороны НЕ шлют периодические keepalive.
* ACTIVE-окно → обмен) и ack-эхом отвечает один раз на анонсе (переход флага 0→1).
* *
* Засыпание (клиент): анонс + ретраи KA_SLEEP_RETRY_MS до ack (эхо с флагом), * По таймингу анонса пир ведёт фазу троттлинга duty-cycle (SLEEP-фаза → тормозим
* окно KA_SLEEP_ANNOUNCE_TIMEOUT_MS, после чего спит anyway. * протоколы, ACTIVE-окно → обмен). Анонс повторяется на фазовых переходах: вход
* в SLEEP (active_remaining=0, sleep_duration=полная) и вход в ACTIVE
* (active_remaining=остаток окна).
* *
* Пробуждение (форграунд): клиент шлёт обычный keepalive (без флага) — сервер * Пробуждение (форграунд): спящий шлёт обычный keepalive (без флагов) — пир снимает
* снимает standby и фазу троттлинга, возвращается в обычный режим. * standby и фазу троттлинга, возвращается в обычный режим.
*/ */
// Флаги keepalive-пакета (data[1]) // Флаги keepalive-пакета (data[1])
#define KA_FLAG_SLEEP 0x01 // спячка: анонс (клиент) / ack-эхо (сервер) #define KA_FLAG_SLEEP 0x01 // «я сплю» (анонс, с таймингом)
#define KA_FLAG_SLEEP_ACK 0x02 // «подтверждаю твой сон» (ответ на анонс)
// Параметры анонса спячки // Параметры анонса спячки
#define KA_SLEEP_RETRY_MS 200 // ретрай анонса спячки (клиент) #define KA_SLEEP_RETRY_MS 200 // ретрай анонса спячки (клиент)

Loading…
Cancel
Save