You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

180 lines
9.7 KiB

/* Проверка режима и фаз standby на реальных keepalive-пакетах до шифрования.
* Управляемая отправка исключает сеть; отказ таймера проверяет сохранение рабочего режима. */
#include <assert.h>
#include <string.h>
#include "utun_instance.h"
#include "etcp.h"
#include "etcp_keepalive.h"
#include "standby.h"
#include "debug_config.h"
static struct UASYNC* ua;
static struct UTUN_INSTANCE inst;
static struct ETCP_CONN udp, tcp;
static struct ETCP_LINK links[3];
static struct ll_queue udp_queue, tcp_queue;
static struct ll_entry *udp_entry, *tcp_entry;
static struct { unsigned count; uint8_t flags; uint16_t active, sleep; } sent[3];
static int fail_phase_timer, activity_events, wait_calls;
static unsigned wait_after_packets;
/* Перехватывает сформированный keepalive, сохраняя его реальные флаги и тайминг. */
int __wrap_etcp_encrypt_send(struct ETCP_DGRAM* pkt) {
assert(pkt && pkt->data[0] == ETCP_KEEPALIVE && pkt->data_len >= 4);
int i;
for (i = 0; i < 3 && pkt->link != &links[i]; i++);
assert(i < 3);
sent[i].count++;
sent[i].flags = pkt->data[1];
sent[i].active = pkt->data_len >= 6 ? pkt->data[4] | ((uint16_t)pkt->data[5] << 8) : 0;
sent[i].sleep = pkt->data_len >= 8 ? pkt->data[6] | ((uint16_t)pkt->data[7] << 8) : 0;
return pkt->data_len;
}
void* __real_uasync_set_timeout(struct UASYNC* ua, int tb, void* arg, timeout_callback_t cb, const char* name);
/* Отказывает только фазовому таймеру, остальные таймеры выполняются настоящим uasync. */
void* __wrap_uasync_set_timeout(struct UASYNC* loop, int tb, void* arg, timeout_callback_t cb, const char* name) {
if (fail_phase_timer && name && !strcmp(name, "standby_phase")) {
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "[TEST] injected standby phase timer failure");
return NULL;
}
return __real_uasync_set_timeout(loop, tb, arg, cb, name);
}
/* Подписчики активности видят уже согласованные режимы, без промежуточного состояния. */
static void activity_changed(struct UTUN_INSTANCE* owner, int active, void* arg) {
(void)arg;
assert(owner == &inst && inst.client_activity == active);
assert(standby_is_enabled() == !active);
for (int i = 0; i < 3; i++) assert(links[i].ka_my_sleeping == !active);
activity_events++;
}
/* Работа возобновляется после отправки новой фазы пирам или выхода из standby. */
static void waited(void* arg) {
(void)arg;
assert(sent[0].count > wait_after_packets);
if (standby_is_enabled()) {
assert(standby_get_active_remaining_tb() > 0 && sent[0].flags == KA_FLAG_SLEEP && sent[0].active > 0);
} else {
assert(inst.client_activity == CLIENT_ACTIVITY_ACTIVE && !links[0].ka_my_sleeping && sent[0].flags == 0);
}
wait_calls++;
}
/* Мобильный узел с исходящим UDP, входящим UDP и TCP линками. */
static void setup(void) {
ua = uasync_create(); assert(ua);
memset(&inst, 0, sizeof(inst)); memset(&udp, 0, sizeof(udp)); memset(&tcp, 0, sizeof(tcp));
memset(links, 0, sizeof(links)); memset(sent, 0, sizeof(sent));
activity_events = wait_calls = fail_phase_timer = 0;
inst.ua = ua; inst.client_type = CLIENT_TYPE_MOBILE; inst.client_activity = CLIENT_ACTIVITY_STANDBY;
udp.instance = tcp.instance = &inst;
strcpy(udp.log_name, "standby-test-udp"); strcpy(tcp.log_name, "standby-test-tcp");
for (int i = 0; i < 3; i++) {
links[i].etcp = i == 2 ? &tcp : &udp;
links[i].local_link_id = i + 1; links[i].initialized = links[i].link_status = 1;
links[i].keepalive_interval = links[i].ka_period_ms = 2000;
links[i].keepalive_timeout = 20000; links[i].last_recv_local_time = get_time_tb();
}
links[0].next = &links[1]; links[1].is_server = 1; links[2].is_tcp = 1;
udp.links = &links[0]; tcp.links = &links[2];
udp_entry = queue_entry_new(sizeof(struct conn_queue_entry)); assert(udp_entry);
tcp_entry = queue_entry_new(sizeof(struct tcp_conn_entry)); assert(tcp_entry);
((struct conn_queue_entry*)udp_entry->data)->conn = &udp;
((struct tcp_conn_entry*)tcp_entry->data)->etcp_conn = &tcp;
memset(&udp_queue, 0, sizeof(udp_queue)); memset(&tcp_queue, 0, sizeof(tcp_queue));
udp_queue.head = udp_entry; tcp_queue.head = tcp_entry;
inst.connections = &udp_queue; inst.tcp_connections = &tcp_queue;
standby_set_intervals_ms(200, 300, 0);
standby_init(ua); etcp_keepalive_register(&inst);
utun_add_activity_cbk(&inst, activity_changed, NULL);
for (int i = 0; i < 3; i++) etcp_keepalive_link_ready(&links[i]);
}
/* Отменяет таймеры и подписки перед повторной инициализацией ядра. */
static void teardown(void) {
standby_deinit();
for (int i = 0; i < 3; i++) {
if (links[i].keepalive_timer) uasync_cancel_timeout(ua, links[i].keepalive_timer);
if (links[i].ka_sleep_announce_timer) uasync_cancel_timeout(ua, links[i].ka_sleep_announce_timer);
}
while (inst.activity_cbks) utun_remove_activity_cbk(&inst, inst.activity_cbks->fn, inst.activity_cbks->arg);
queue_entry_free(udp_entry); queue_entry_free(tcp_entry);
uasync_poll(ua, 0);
assert(ua->timer_alloc_count == ua->timer_free_count);
uasync_destroy(ua, 0);
}
/* Первый фон при начальном STANDBY, повторы, новые линки, обе причины wake и выход в ACTIVE. */
static void check_transitions(void) {
setup();
assert(!standby_is_enabled());
for (int i = 0; i < 3; i++) assert(sent[i].count == 1 && sent[i].flags == 0 && links[i].keepalive_timer);
assert(utun_set_client_activity(&inst, 0) == 0 && standby_is_enabled() && activity_events == 0);
for (int i = 0; i < 3; i++) {
assert(links[i].ka_my_sleeping && !links[i].keepalive_timer);
assert(sent[i].count == 2 && sent[i].flags == KA_FLAG_SLEEP && sent[i].active > 0 && sent[i].sleep == 3);
}
uint64_t timers = ua->timer_alloc_count;
assert(utun_set_client_activity(&inst, 0) == 0 && ua->timer_alloc_count == timers);
assert(sent[0].count == 2 && sent[1].count == 2 && sent[2].count == 2);
uasync_poll(ua, -1); assert(standby_get_sleep_tb() > 0);
for (int i = 0; i < 3; i++) {
etcp_keepalive_link_ready(&links[i]);
assert(sent[i].flags == KA_FLAG_SLEEP && sent[i].active == 0 && sent[i].sleep > 0 && !links[i].keepalive_timer);
}
assert(links[0].ka_sleep_announce_timer && links[1].ka_sleep_announce_timer && !links[2].ka_sleep_announce_timer);
timers = ua->timer_alloc_count; wait_after_packets = sent[0].count;
assert(standby_wait(NULL, waited)); assert(ua->timer_alloc_count == timers);
fail_phase_timer = 1;
standby_notify_network_activity();
assert(standby_get_sleep_tb() > 0 && wait_calls == 0 && sent[0].count == wait_after_packets);
fail_phase_timer = 0;
standby_notify_network_activity(); assert(wait_calls == 1);
uasync_poll(ua, -1); assert(standby_get_sleep_tb() > 0);
wait_after_packets = sent[0].count;
void* cancelled = standby_wait(NULL, waited); assert(cancelled);
standby_wait_cancel(cancelled);
assert(standby_wait(NULL, waited));
uasync_poll(ua, -1); assert(wait_calls == 2 && standby_get_active_remaining_tb() > 0);
uasync_poll(ua, -1); assert(standby_get_sleep_tb() > 0);
wait_after_packets = sent[0].count; assert(standby_wait(NULL, waited));
links[0].ka_peer_sleeping = 1;
assert(utun_set_client_activity(&inst, 1) == 0 && activity_events == 1 && wait_calls == 3);
for (int i = 0; i < 3; i++) {
assert(!links[i].ka_my_sleeping && sent[i].flags == 0 && !links[i].ka_sleep_announce_timer);
}
assert(!links[0].keepalive_timer && links[1].keepalive_timer && links[2].keepalive_timer);
timers = ua->timer_alloc_count; unsigned packets = sent[0].count;
assert(utun_set_client_activity(&inst, 1) == 0 && ua->timer_alloc_count == timers);
assert(activity_events == 1 && sent[0].count == packets);
assert(utun_set_client_activity(&inst, 0) == 0 && activity_events == 2);
teardown();
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[PASS] first background, repeated commands, UDP/TCP ready, phase-before-work, peer sleep and cleanup");
}
/* Ошибка ENTER сохраняет normal; потеря фазового таймера возвращает normal, без зависания сна. */
static void check_timer_failures(void) {
setup(); assert(utun_set_client_activity(&inst, 1) == 0);
fail_phase_timer = 1;
assert(utun_set_client_activity(&inst, 0) < 0);
assert(inst.client_activity == CLIENT_ACTIVITY_ACTIVE && !standby_is_enabled() && activity_events == 1);
for (int i = 0; i < 3; i++) assert(!links[i].ka_my_sleeping && links[i].keepalive_timer);
fail_phase_timer = 0;
assert(utun_set_client_activity(&inst, 0) == 0);
fail_phase_timer = 1;
uasync_poll(ua, -1);
assert(!standby_is_enabled() && inst.client_activity == CLIENT_ACTIVITY_ACTIVE && activity_events == 3);
for (int i = 0; i < 3; i++) assert(!links[i].ka_my_sleeping && links[i].keepalive_timer);
fail_phase_timer = 0; teardown();
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[PASS] timer failure rollback and safe normal fallback");
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO);
check_transitions(); check_transitions(); check_timer_failures();
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "PASS: standby transport regression tests");
return 0;
}