diff --git a/tests/Makefile.am b/tests/Makefile.am index d2a05e2c..fa073cad 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -524,6 +524,7 @@ test_nat_detection_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_nat_detection_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) if OS_LINUX +test_nat_detection_LDFLAGS = -Wl,--wrap=etcp_add_conn_status_cbk -Wl,--wrap=etcp_add_socket_cbk -Wl,--wrap=uasync_set_timeout check_PROGRAMS += test_nat_detection_faults test_nat_detection_faults_SOURCES = test_nat_detection_faults.c $(test_clock_sources) test_nat_detection_faults_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib diff --git a/tests/test_nat_detection.c b/tests/test_nat_detection.c index fc8e1ae7..1f542366 100644 --- a/tests/test_nat_detection.c +++ b/tests/test_nat_detection.c @@ -2,6 +2,65 @@ * Настоящие ETCP/NCD/PING/STCP, без групповой автосвязности и без подмены времени. */ #include "nat_fixture.h" #include "ntp_time.h" +#include "transport_layer/etcp_api.h" +#include "routing_layer/etcp_router.h" + +#ifdef __linux__ +static int fail_subscription, fail_nat_timer, fail_ping_timer; + +void __real_etcp_add_conn_status_cbk(struct UTUN_INSTANCE*, etcp_conn_status_fn, void*); +void __wrap_etcp_add_conn_status_cbk(struct UTUN_INSTANCE* inst, etcp_conn_status_fn fn, void* arg) { + if (fail_subscription != 1) __real_etcp_add_conn_status_cbk(inst, fn, arg); +} + +void __real_etcp_add_socket_cbk(struct UTUN_INSTANCE*, etcp_socket_cbk_fn, void*, int); +void __wrap_etcp_add_socket_cbk(struct UTUN_INSTANCE* inst, etcp_socket_cbk_fn fn, void* arg, int events) { + if (fail_subscription != 2) __real_etcp_add_socket_cbk(inst, fn, arg, events); +} + +void* __real_uasync_set_timeout(struct UASYNC*, int, void*, timeout_callback_t, const char*); +void* __wrap_uasync_set_timeout(struct UASYNC* ua, int delay, void* arg, timeout_callback_t fn, const char* name) { + if ((fail_nat_timer && !strcmp(name, "nat_detection")) || (fail_ping_timer && !strcmp(name, "ping_timeout"))) return NULL; + return __real_uasync_set_timeout(ua, delay, arg, fn, name); +} + +static void unexpected_ping(int success, uint16_t rtt, void* arg, uint64_t nonce, const uint8_t* data, size_t size) { + (void)success; (void)rtt; (void)nonce; (void)data; (void)size; (*(int*)arg)++; +} + +/* Частичный запуск снимает только свои подписки; не принятая проба не оставляет вечного pending. */ +static void test_start_failures(void) { + struct nat_fixture fixture; nat_fixture_init(&fixture); + struct UTUN_INSTANCE* inst = nat_fixture_node(&fixture, "127.0.0.2", NULL, NULL); + nat_detection_destroy(inst->nat_det); inst->nat_det = NULL; + uasync_poll(fixture.ua, 0); + struct etcp_status_cbk_entry* connections = inst->conn_status_cbks; + struct etcp_socket_cbk_entry* sockets = inst->socket_cbks; + size_t allocations = u_get_allocated_count(); + for (int failure = 1; failure <= 3; failure++) { + fail_subscription = failure; fail_nat_timer = failure == 3; + assert(!nat_detection_create(inst)); + fail_subscription = 0; fail_nat_timer = 0; + assert(inst->conn_status_cbks == connections && inst->socket_cbks == sockets); + assert(!inst->router_bindings.callbacks[ETCP_RT_ID_NAT_DETECTION]); + assert(u_get_allocated_count() == allocations); + } + inst->nat_det = nat_detection_create(inst); assert(inst->nat_det); + nat_detection_set_automatic(inst->nat_det, 0); + struct ETCP_SOCKET* socket = test_etcp_bound_socket(inst, "udp"); + struct sockaddr_storage target = socket->local_addr; + ((struct sockaddr_in*)&target)->sin_port = htons(9); + struct PING_CONTEXT* pending = inst->pending_pings; + int callbacks = 0; fail_ping_timer = 1; + assert(etcp_send_ping_to_socket(inst, socket, inst->my_keys.public_key, &target, 1000, + unexpected_ping, &callbacks, NULL, 0, 0) < 0); + fail_ping_timer = 0; + assert(inst->pending_pings == pending); + uasync_poll(fixture.ua, 0); assert(!callbacks); + nat_fixture_cleanup(&fixture); + DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS NAT partial startup rollback: two subscriptions, timer; failed UDP probe leaves no pending"); +} +#endif static void wait_profile(struct nat_fixture *fixture, struct ETCP_SOCKET *socket, uint8_t mapping, uint8_t inbound) { uint64_t end = get_time_tb() + 180000; @@ -255,6 +314,9 @@ static void test_all_groups_tcp(void) { int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_NAT, DEBUG_LEVEL_DEBUG); if (getenv("UTUN_TEST_DEBUG")) debug_set_category_level(DEBUG_CATEGORY_BGP, DEBUG_LEVEL_DEBUG); +#ifdef __linux__ + test_start_failures(); +#endif struct nat_fixture fixture; nat_fixture_init(&fixture); struct SC_MYKEYS first, second; assert(sc_generate_keypair(&first) == SC_OK && sc_generate_keypair(&second) == SC_OK);