From 7bcdccc4d6cd9ecec32b0299960b99c78dc2fc5b Mon Sep 17 00:00:00 2001 From: evgeny Date: Sun, 25 Oct 2026 16:27:48 +0100 Subject: [PATCH] Verify autosocket protocol modes across IPv4 and IPv6 lifecycle --- src/config_parser_doc.md | 3 ++ tests/test_auto_socket_dynamic.c | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/config_parser_doc.md b/src/config_parser_doc.md index ac95683f..968c9f6d 100644 --- a/src/config_parser_doc.md +++ b/src/config_parser_doc.md @@ -41,6 +41,9 @@ free_config(cfg); - Обработка длинных значений зависит от поля: часть строк обрезается, `assign_string` отклоняет значение без изменения поля. - Ключи клиента `link` требуют, чтобы сервер (`[server:имя]`) был объявлен выше по файлу. Иначе ошибка. - `tun_enabled` по умолчанию `0`. При явном включении и отсутствии имени интерфейса используется `tun0`. +- При `auto_sockets=yes` параметр `[global] auto_socket_protocols=tcp|udp|tcp+udp` выбирает протоколы автосокетов. + По умолчанию `tcp+udp`; неизвестное или пустое значение отклоняется. Ручные `[server]` используют свой `transport`. + В Android выбранные протоколы генерирует frontend в отдельные `[server]` с портами на интерфейс/семейство/протокол. - IPv6-адреса в `[server:...] addr` поддерживают префиксы `temporary_ipv6:` и `permanent_ipv6:`. - HEX декодируется после проверки каждого символа. Ошибочный список REALITY `short_ids` отклоняется целиком и оставляет `short_id_count=0`; ошибочный последний REALITY-ключ сбрасывает соответствующий `has_*_key`. diff --git a/tests/test_auto_socket_dynamic.c b/tests/test_auto_socket_dynamic.c index 955fa4d5..8d3c00ba 100644 --- a/tests/test_auto_socket_dynamic.c +++ b/tests/test_auto_socket_dynamic.c @@ -23,6 +23,7 @@ #include "topo_node.h" #include "secure_channel.h" #include "../src/transport_layer/node_conn_direct.h" +#include "../src/transport_layer/auto_socket.h" #include "../lib/u_async.h" #include "../lib/ll_queue.h" #include "../lib/debug_config.h" @@ -775,6 +776,55 @@ static void cleanup(void) { if (nftw(tdir, cleanup_path, 16, FTW_DEPTH | FTW_PHYS) != 0) fail("temporary directory cleanup failed"); } +/* Проверяет фактический набор TCP/UDP для обоих семейств на одном интерфейсе. */ +static void check_protocol_sockets(struct UTUN_INSTANCE* inst, uint32_t ifindex, int protocols) { + int actual = 0, count = 0; + for (struct ETCP_SOCKET* sock = inst->etcp_sockets; sock; sock = sock->next) { + if (sock->netif_index != ifindex || sock->probe_only) continue; + int bit = sock->is_tcp ? CFG_AUTO_SOCKET_TCP : CFG_AUTO_SOCKET_UDP; + if (sock->local_addr.ss_family == AF_INET6) bit <<= 2; + actual |= bit; count++; + } + int expected = protocols | (protocols << 2); + int expected_count = protocols == CFG_AUTO_SOCKET_BOTH ? 4 : protocols ? 2 : 0; + if (actual != expected || count != expected_count) fail("selected auto socket protocols do not match actual IPv4/IPv6 sockets"); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "auto protocol check: mask=%d actual=%d sockets=%d expected=%d", + protocols, actual, count, expected_count); +} + +/* Проверяет начальное сканирование, явное добавление и повторную сверку во всех трёх режимах. */ +static void test_protocol_modes(void) { + if (system("ip -6 addr add fd42::2/64 nodad dev dummy_cli1")) { fail("cannot add IPv6 protocol test address"); return; } + uint32_t ifindex = if_nametoindex(rounds[0].ifname); + const int modes[] = {CFG_AUTO_SOCKET_TCP, CFG_AUTO_SOCKET_UDP, CFG_AUTO_SOCKET_BOTH}; + for (size_t i = 0; i < sizeof(modes) / sizeof(modes[0]) && !ctx.result; i++) { + struct utun_config* config = parse_config(ccf); + if (!config) { fail("cannot parse protocol test config"); break; } + config->global.auto_socket_protocols = modes[i]; + config->global.auto_socket_skip_no_default_route = 0; + config->global.db_path[0] = '\0'; + struct UTUN_INSTANCE* inst = utun_instance_create_from_config(ctx.ua, config); + if (!inst) { free_config(config); fail("cannot create protocol test instance"); break; } + if (utun_instance_init(inst) < 0) fail("protocol test socket initialization failed"); + if (!ctx.result) { + check_protocol_sockets(inst, ifindex, modes[i]); + auto_socket_remove_interface(inst, ifindex); + check_protocol_sockets(inst, ifindex, 0); + int added = auto_socket_add_interface(inst, ifindex, rounds[0].ifname, 1, 1, + CFG_SERVER_TYPE_NAT, CFG_SERVER_TYPE_NAT); + if (added != (modes[i] == CFG_AUTO_SOCKET_BOTH ? 4 : 2)) fail("explicit auto socket add returned wrong count"); + check_protocol_sockets(inst, ifindex, modes[i]); + auto_socket_on_network_change(inst); + uasync_poll(ctx.ua, 0); + check_protocol_sockets(inst, ifindex, modes[i]); + } + inst->running = 0; + utun_instance_destroy(inst); + uasync_poll(ctx.ua, 0); + } + if (ip_addr_del(rounds[0].ifname, "fd42::2/64")) fail("cannot remove IPv6 protocol test address"); +} + int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); @@ -791,6 +841,8 @@ int main(void) { if (ctx.result) return 1; ctx.ua = uasync_create(); + test_protocol_modes(); + if (ctx.result) goto done; ctx.server = utun_instance_create(ctx.ua, scf); ctx.client = utun_instance_create(ctx.ua, ccf); if (!ctx.server || !ctx.client) goto done;