Browse Source

sock_match: классификация сокета по адресу, а не по type из конфига (fix Q8/Android→LAN)

ip_is_public() вынесен в lib/platform_compat (единый классификатор IPv4 с ntohl).
auto_socket_reconcile_config: тип сокета выставляется по фактическому адресу.
sock_collect_views: has_priv/has_inet/is_eim — по классификации адреса и NAT-детекции.
is_local_subnet упрощён до !ip_is_public(). Тест test_sock_match обновлён.
router-recv-conn-uaf
evgeny 2 weeks ago
parent
commit
51d6d22a27
  1. 15
      doc/tasks.md
  2. 19
      lib/platform_compat.c
  3. 5
      lib/platform_compat.h
  4. 19
      src/routing_layer/route_lib.c
  5. 44
      src/routing_layer/sock_match.c
  6. 17
      src/routing_layer/sock_match.h
  7. 37
      src/transport_layer/auto_socket.c
  8. 33
      tests/test_sock_match.c

15
doc/tasks.md

@ -3,6 +3,17 @@
Сюда пишется список задач с короткой аннотацией. Если задача большая и имеет ТЗ - то ТЗ оформляется отдельным файлом, а сюда помещается аннотация и ссылка на ТЗ. Сюда пишется список задач с короткой аннотацией. Если задача большая и имеет ТЗ - то ТЗ оформляется отдельным файлом, а сюда помещается аннотация и ссылка на ТЗ.
## Текущая задача ## Текущая задача
[+] **Q8/Android не подключается к локальному чатгуи (LAN)** — сделано. Причина: тип сокета
в Android-конфиге был захардкожен `type=public`, из-за чего `sock_match` ставил
`has_priv=0` и не создавал линки к приватным (LAN) адресам пиров (`create_links … → 0 links`).
Исправлено: (1) `ip_is_public()` вынесен в `lib/platform_compat` (единый классификатор
IPv4 с корректным ntohl); (2) в `auto_socket_reconcile_config` (Android-режим) тип сокета
выставляется по адресу (public→PUBLIC, приватный/link-local→NAT), конфиг `type` игнорируется;
(3) `sock_collect_views` больше не смотрит на `type` из конфига: `has_priv` — по классификации
локального адреса, `has_inet=1` (исходящие считаем доступными), `is_eim` — по NAT-детекции
(hairpin). `is_local_subnet` упрощён до `!ip_is_public()`. Тест `test_sock_match` обновлён
(+ `ip_is_public` кейсы). `check.sh`: 86 passed / 1 failed / 1 skipped.
[ ] **DM: прямой p2p чат с любым пользователем группы** — отдельная DM-подсистема. [ ] **DM: прямой p2p чат с любым пользователем группы** — отдельная DM-подсистема.
ТЗ и архитектура: `/doc/dm_arch.md`. Статус: реализовано (dm_core/dm_mailbox/dm_crypto), ТЗ и архитектура: `/doc/dm_arch.md`. Статус: реализовано (dm_core/dm_mailbox/dm_crypto),
тесты `test_dm` (крипто) и `test_dm_e2e` (интеграция). Остались задачи ниже. тесты `test_dm` (крипто) и `test_dm_e2e` (интеграция). Остались задачи ниже.
@ -175,6 +186,10 @@
`router_no_route`), не влияет на результат — стоит разобрать отдельно. `router_no_route`), не влияет на результат — стоит разобрать отдельно.
## Открытые флаки ## Открытые флаки
[ ] **test_chat_join_e2e** — стабильно падает (2/3 сценария): «J not signed by A»
`get_sign_rc=-1` (signed_by-верификация invite). Не связан с socket-классификацией
(обнаружен при прогоне после фикса Q8/LAN). Скорее всего регресс join-протокола.
[ ] **test_etcp_reconnect** — флаки под параллельной нагрузкой `make check -j4`: [ ] **test_etcp_reconnect** — флаки под параллельной нагрузкой `make check -j4`:
phase 4 (reconnect после server restart) таймаутит (`sent=480 recv=0`), при одиночном phase 4 (reconnect после server restart) таймаутит (`sent=480 recv=0`), при одиночном
запуске стабильно `TEST PASSED`. Наш код (chat_join/chat_sync) его не трогает. запуске стабильно `TEST PASSED`. Наш код (chat_join/chat_sync) его не трогает.

19
lib/platform_compat.c

@ -39,6 +39,25 @@
#endif #endif
#endif #endif
/*
* Классифицирует IPv4-адрес (network byte order):
* 1 = глобальный/публичный (маршрутизируемый в интернете),
* 0 = приватный, CGNAT, loopback, link-local, multicast или зарезервированный.
*/
int ip_is_public(uint32_t addr_be) {
uint32_t a = ntohl(addr_be);
if ((a & 0xFF000000u) == 0x0A000000u) return 0; /* 10.0.0.0/8 */
if ((a & 0xFFF00000u) == 0xAC100000u) return 0; /* 172.16.0.0/12 */
if ((a & 0xFFFF0000u) == 0xC0A80000u) return 0; /* 192.168.0.0/16 */
if ((a & 0xFF000000u) == 0x7F000000u) return 0; /* 127.0.0.0/8 */
if ((a & 0xFFFF0000u) == 0xA9FE0000u) return 0; /* 169.254.0.0/16 */
if ((a & 0xFF000000u) == 0x00000000u) return 0; /* 0.0.0.0/8 */
if ((a & 0xF0000000u) == 0xE0000000u) return 0; /* 224.0.0.0/4 */
if ((a & 0xF0000000u) == 0xF0000000u) return 0; /* 240.0.0.0/4 */
if ((a & 0xFFC00000u) == 0x64400000u) return 0; /* 100.64.0.0/10 (CGNAT) */
return 1;
}
/* /*
* Generate cryptographically secure random bytes * Generate cryptographically secure random bytes
* Returns 0 on success, -1 on error * Returns 0 on success, -1 on error

5
lib/platform_compat.h

@ -201,6 +201,11 @@ int get_interface_ipv6_addr_nl(uint32_t netif_index, int prefer_stable, uint8_t*
// family: AF_INET or AF_INET6, returns ifindex (0 on failure) // family: AF_INET or AF_INET6, returns ifindex (0 on failure)
uint32_t get_default_route_netif_index(int family); uint32_t get_default_route_netif_index(int family);
// Классифицирует IPv4-адрес (network byte order):
// 1 = глобальный/публичный (маршрутизируемый в интернете),
// 0 = приватный, CGNAT, loopback, link-local, multicast или зарезервированный.
int ip_is_public(uint32_t addr_be);
// Cross-platform mkdir // Cross-platform mkdir
#ifdef _WIN32 #ifdef _WIN32

19
src/routing_layer/route_lib.c

@ -267,22 +267,5 @@ int parse_subnet(const char *subnet_str, uint32_t *network, uint8_t *prefix_leng
} }
int is_local_subnet(uint32_t ip) { int is_local_subnet(uint32_t ip) {
uint8_t* bip=(uint8_t*)&ip; return !ip_is_public(ip);
uint8_t first_octet = bip[0];
uint8_t second_octet = bip[1];
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "check ip %d.%d : ", first_octet, second_octet);
if (first_octet == 0) return 1;
if (first_octet == 10) return 1;
if (first_octet == 127) return 1;
if (first_octet == 169 && second_octet == 254) return 1;
if (first_octet >= 224 && first_octet <= 239) return 1;
if (first_octet == 172 && second_octet >= 16 && second_octet <= 31) return 1;
if (first_octet == 100 && second_octet >= 64 && second_octet <= 127) return 1; /* 100.64.0.0/10 (CGNAT) */
if (first_octet == 192 && second_octet == 168) return 1;
if (ip==0xffffffff) return 1;
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "result: global ip %d.%d : ", first_octet, second_octet);
return 0;
} }

44
src/routing_layer/sock_match.c

@ -8,27 +8,11 @@
#include "etcp_connections.h" #include "etcp_connections.h"
#include "config_parser.h" #include "config_parser.h"
#include "topo_node.h" #include "topo_node.h"
#include "ntp_time.h"
/* Нормализация NAT-типа: сырые (NAT_TYPE_*) или проверенные (NAT_VERIFIED_*) значения. */ /* Нормализация NAT-типа: сырые (NAT_TYPE_*) или проверенные (NAT_VERIFIED_*) значения. */
static int nat_is_eim(uint8_t n) { return n == NAT_TYPE_EIM || n == NAT_VERIFIED_EIM; } static int nat_is_eim(uint8_t n) { return n == NAT_TYPE_EIM || n == NAT_VERIFIED_EIM; }
static int nat_is_direct(uint8_t n) { return n == NAT_TYPE_DIRECT || n == NAT_VERIFIED_DIRECT; } static int nat_is_direct(uint8_t n) { return n == NAT_TYPE_DIRECT || n == NAT_VERIFIED_DIRECT; }
uint8_t sock_role_of(uint8_t type, uint8_t nat_type, int ntp_reachable) {
uint8_t role;
if (type == CFG_SERVER_TYPE_LOCAL)
role = SOCK_ROLE_LOCAL; /* без интернета (из конфига) */
else if (type == CFG_SERVER_TYPE_PUBLIC || type == CFG_SERVER_TYPE_UNKNOWN)
role = SOCK_ROLE_DIRECT;
else if (type == CFG_SERVER_TYPE_NAT)
role = (nat_is_eim(nat_type) || nat_is_direct(nat_type)) ? SOCK_ROLE_DIRECT : SOCK_ROLE_NAT;
else
role = SOCK_ROLE_NAT; /* PRIVATE — скрытый, только исходящие */
if (!ntp_reachable && role != SOCK_ROLE_LOCAL)
role = SOCK_ROLE_LOCAL; /* NTP недоступен — интернета нет */
return role;
}
uint8_t sock_target_class(uint8_t a_type, uint8_t tcfg, uint8_t tnat) { uint8_t sock_target_class(uint8_t a_type, uint8_t tcfg, uint8_t tnat) {
if (tcfg == CFG_SERVER_TYPE_PUBLIC || tcfg == CFG_SERVER_TYPE_UNKNOWN) return SOCK_TC_PUB; if (tcfg == CFG_SERVER_TYPE_PUBLIC || tcfg == CFG_SERVER_TYPE_UNKNOWN) return SOCK_TC_PUB;
if (tcfg == CFG_SERVER_TYPE_PRIVATE || tcfg == CFG_SERVER_TYPE_LOCAL) return SOCK_TC_PRIV; if (tcfg == CFG_SERVER_TYPE_PRIVATE || tcfg == CFG_SERVER_TYPE_LOCAL) return SOCK_TC_PRIV;
@ -42,7 +26,7 @@ int sock_match(const struct sock_view* v, uint8_t a_type, uint8_t tcfg, uint8_t
uint8_t tc = sock_target_class(a_type, tcfg, tnat); uint8_t tc = sock_target_class(a_type, tcfg, tnat);
if (tc == SOCK_TC_UNREACH) return 0; /* strict/unknown NAT-адрес — напрямую не пробьём */ if (tc == SOCK_TC_UNREACH) return 0; /* strict/unknown NAT-адрес — напрямую не пробьём */
if (tc == SOCK_TC_PUB) { if (tc == SOCK_TC_PUB) {
if (v->role == SOCK_ROLE_LOCAL) return 0; /* у нашего сокета нет интернета */ if (!v->has_inet) return 0; /* у нашего сокета нет интернета */
if (a_type == TOPO_ADDR_NAT && v->is_eim && nat_is_eim(tnat) if (a_type == TOPO_ADDR_NAT && v->is_eim && nat_is_eim(tnat)
&& v->public_ip4 && v->public_ip4 == target_ip4) return 0; /* оба EIM за одним NAT — hairpin */ && v->public_ip4 && v->public_ip4 == target_ip4) return 0; /* оба EIM за одним NAT — hairpin */
return 1; return 1;
@ -52,27 +36,37 @@ int sock_match(const struct sock_view* v, uint8_t a_type, uint8_t tcfg, uint8_t
int sock_collect_views(struct UTUN_INSTANCE* inst, struct sock_view* out, int max) { int sock_collect_views(struct UTUN_INSTANCE* inst, struct sock_view* out, int max) {
if (!inst || !out || max <= 0) return 0; if (!inst || !out || max <= 0) return 0;
int ntp_reachable = inst->ntp.reachable;
int n = 0; int n = 0;
struct ETCP_SOCKET* s = inst->etcp_sockets; struct ETCP_SOCKET* s = inst->etcp_sockets;
while (s && n < max) { while (s && n < max) {
if (s->local_addr.ss_family != AF_INET) { s = s->next; continue; } if (s->local_addr.ss_family != AF_INET) { s = s->next; continue; }
struct sock_view* v = &out[n++]; struct sock_view* v = &out[n++];
v->sock = s; v->sock = s;
v->role = sock_role_of(s->type, s->nat_type, ntp_reachable);
v->has_inet = (uint8_t)(v->role != SOCK_ROLE_LOCAL);
v->has_priv = (uint8_t)(s->type != CFG_SERVER_TYPE_PUBLIC && s->type != CFG_SERVER_TYPE_UNKNOWN);
v->is_eim = (uint8_t)(s->type == CFG_SERVER_TYPE_NAT && nat_is_eim(s->nat_type));
v->family = AF_INET; v->family = AF_INET;
v->is_tcp = s->is_tcp; v->is_tcp = s->is_tcp;
v->local_ip4 = 0; v->public_ip4 = 0; v->local_ip4 = 0; v->public_ip4 = 0;
/* Локальный адрес (interface, иначе local). */
{ const struct sockaddr_storage* ia = s->interface_addr.ss_family ? &s->interface_addr : &s->local_addr; { const struct sockaddr_storage* ia = s->interface_addr.ss_family ? &s->interface_addr : &s->local_addr;
if (ia && ia->ss_family == AF_INET) v->local_ip4 = ((const struct sockaddr_in*)ia)->sin_addr.s_addr; } if (ia && ia->ss_family == AF_INET) v->local_ip4 = ((const struct sockaddr_in*)ia)->sin_addr.s_addr; }
/* Публичный адрес (NAT): nat_addr, иначе локальный если он публичный. */
{ const struct sockaddr_in* na = (const struct sockaddr_in*)&s->nat_addr; { const struct sockaddr_in* na = (const struct sockaddr_in*)&s->nat_addr;
if (s->nat_addr.ss_family == AF_INET && na->sin_addr.s_addr != 0) if (s->nat_addr.ss_family == AF_INET && na->sin_addr.s_addr != 0)
v->public_ip4 = na->sin_addr.s_addr; v->public_ip4 = na->sin_addr.s_addr; }
else if (s->type == CFG_SERVER_TYPE_PUBLIC || s->type == CFG_SERVER_TYPE_UNKNOWN || nat_is_direct(s->nat_type))
v->public_ip4 = v->local_ip4; } /* Классификация по адресу, а не по type из конфига:
* приватный локальный адрес → можем ходить в LAN (has_priv);
* исходящие считаем доступными (интернет + локалка). */
int local_public = v->local_ip4 && ip_is_public(v->local_ip4);
v->has_priv = (uint8_t)!local_public;
v->has_inet = 1;
/* NAT-результат (главный критерий если есть) — для hairpin-фильтра. */
v->is_eim = (uint8_t)nat_is_eim(s->nat_type);
if (!v->public_ip4 && local_public) v->public_ip4 = v->local_ip4;
v->role = local_public ? SOCK_ROLE_DIRECT : (v->is_eim ? SOCK_ROLE_DIRECT : SOCK_ROLE_NAT);
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "sock_view[%d] sock=%s type=%d nat=%d role=%d inet=%d priv=%d eim=%d tcp=%d local=%08x public=%08x", DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "sock_view[%d] sock=%s type=%d nat=%d role=%d inet=%d priv=%d eim=%d tcp=%d local=%08x public=%08x",
n - 1, s->name, s->type, s->nat_type, v->role, v->has_inet, v->has_priv, v->is_eim, v->is_tcp, n - 1, s->name, s->type, s->nat_type, v->role, v->has_inet, v->has_priv, v->is_eim, v->is_tcp,
v->local_ip4, v->public_ip4); v->local_ip4, v->public_ip4);

17
src/routing_layer/sock_match.h

@ -65,22 +65,25 @@ struct ETCP_SOCKET;
* Плоское представление нашего сокета для сопоставления. * Плоское представление нашего сокета для сопоставления.
* "ip сокета" — local_ip4 (interface), "публичный" — public_ip4 (NAT/прямой), * "ip сокета" — local_ip4 (interface), "публичный" — public_ip4 (NAT/прямой),
* "тип" — role + флаги has_inet/has_priv/is_eim. * "тип" — role + флаги has_inet/has_priv/is_eim.
*
* Флаги вычисляются из КЛАССИФИКАЦИИ АДРЕСА и результата NAT-детекции,
* а не из type сокета в конфиге:
* has_priv — локальный IPv4 приватный/не-маршрутизируемый (можем ходить в LAN);
* has_inet — исходящие считаем доступными в интернет (всегда 1);
* is_eim — результат NAT-детекции EIM (для hairpin-фильтра).
*/ */
struct sock_view { struct sock_view {
struct ETCP_SOCKET* sock; struct ETCP_SOCKET* sock;
uint8_t role; /* SOCK_ROLE_* */ uint8_t role; /* SOCK_ROLE_* (производное, для логов) */
uint8_t has_inet; /* 1 — есть интернет (role != LOCAL) */ uint8_t has_inet; /* 1 — интернет доступен */
uint8_t has_priv; /* 1 — есть приватный интерфейс (type != PUBLIC/UNKNOWN) */ uint8_t has_priv; /* 1 — есть приватный (LAN) интерфейс */
uint8_t is_eim; /* 1 — NAT + EIM (для hairpin) */ uint8_t is_eim; /* 1 — NAT + EIM (для hairpin) */
uint8_t family; /* AF_INET / AF_INET6 */ uint8_t family; /* AF_INET / AF_INET6 */
uint8_t is_tcp; uint8_t is_tcp;
uint32_t local_ip4; /* interface_addr, network order (0 если нет) */ uint32_t local_ip4; /* interface_addr, network order (0 если нет) */
uint32_t public_ip4; /* nat_addr / interface если direct, network order (0 если нет) */ uint32_t public_ip4; /* nat_addr / interface если public, network order (0 если нет) */
}; };
/* Роль нашего сокета по конфиг-типу, NAT-типу и доступности интернета. */
uint8_t sock_role_of(uint8_t type, uint8_t nat_type, int ntp_reachable);
/* Класс адреса пира по типу адреса (TOPO_ADDR_*), конфиг-типу и NAT-типу сокета. */ /* Класс адреса пира по типу адреса (TOPO_ADDR_*), конфиг-типу и NAT-типу сокета. */
uint8_t sock_target_class(uint8_t a_type, uint8_t tcfg, uint8_t tnat); uint8_t sock_target_class(uint8_t a_type, uint8_t tcfg, uint8_t tnat);

37
src/transport_layer/auto_socket.c

@ -87,23 +87,6 @@
#define AS_PROTO_UDP 0 #define AS_PROTO_UDP 0
#define AS_PROTO_TCP 1 #define AS_PROTO_TCP 1
/* Классифицирует IPv4-адрес: 1 = глобальный/публичный (можно использовать как PUBLIC-сокет),
* 0 = приватный, CGNAT, loopback, link-local, multicast или зарезервированный.
* Определяет тип создаваемого сокета по адресам на интерфейсе. */
static int is_ipv4_public(uint32_t addr_be) {
uint32_t a = ntohl(addr_be);
if ((a & 0xFF000000u) == 0x0A000000u) return 0; /* 10.0.0.0/8 */
if ((a & 0xFFF00000u) == 0xAC100000u) return 0; /* 172.16.0.0/12 */
if ((a & 0xFFFF0000u) == 0xC0A80000u) return 0; /* 192.168.0.0/16 */
if ((a & 0xFF000000u) == 0x7F000000u) return 0; /* 127.0.0.0/8 */
if ((a & 0xFFFF0000u) == 0xA9FE0000u) return 0; /* 169.254.0.0/16 */
if ((a & 0xFF000000u) == 0x00000000u) return 0; /* 0.0.0.0/8 */
if ((a & 0xF0000000u) == 0xE0000000u) return 0; /* 224.0.0.0/4 */
if ((a & 0xF0000000u) == 0xF0000000u) return 0; /* 240.0.0.0/4 */
if ((a & 0xFFC00000u) == 0x64400000u) return 0; /* 100.64.0.0/10 (CGNAT) */
return 1;
}
/* Классифицирует IPv6-адрес для определения типа сокета: /* Классифицирует IPv6-адрес для определения типа сокета:
* AS_V6_LL = link-local (fe80::) — создаём NAT-сокет * AS_V6_LL = link-local (fe80::) — создаём NAT-сокет
* AS_V6_LOC = ULA (fc00::/fd00::) — локальный, NAT-сокет * AS_V6_LOC = ULA (fc00::/fd00::) — локальный, NAT-сокет
@ -648,7 +631,7 @@ static int scan_and_classify_iface(uint32_t ifindex, const char* ifname,
struct sockaddr_in* sin = (struct sockaddr_in*)ifa->ifa_addr; struct sockaddr_in* sin = (struct sockaddr_in*)ifa->ifa_addr;
if (sin->sin_addr.s_addr == INADDR_ANY) continue; if (sin->sin_addr.s_addr == INADDR_ANY) continue;
*out_has_v4 = 1; *out_has_v4 = 1;
if (is_ipv4_public(sin->sin_addr.s_addr)) if (ip_is_public(sin->sin_addr.s_addr))
*out_v4_type = CFG_SERVER_TYPE_PUBLIC; *out_v4_type = CFG_SERVER_TYPE_PUBLIC;
} else if (ifa->ifa_addr->sa_family == AF_INET6) { } else if (ifa->ifa_addr->sa_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)ifa->ifa_addr; struct sockaddr_in6* sin6 = (struct sockaddr_in6*)ifa->ifa_addr;
@ -754,7 +737,7 @@ static int scan_and_classify_iface(uint32_t ifindex, const char* ifname,
const struct sockaddr_in* sin = (const struct sockaddr_in*)u->Address.lpSockaddr; const struct sockaddr_in* sin = (const struct sockaddr_in*)u->Address.lpSockaddr;
if (sin->sin_addr.s_addr == INADDR_ANY) continue; if (sin->sin_addr.s_addr == INADDR_ANY) continue;
*out_has_v4 = 1; *out_has_v4 = 1;
if (is_ipv4_public(sin->sin_addr.s_addr)) *out_v4_type = CFG_SERVER_TYPE_PUBLIC; if (ip_is_public(sin->sin_addr.s_addr)) *out_v4_type = CFG_SERVER_TYPE_PUBLIC;
} else if (u->Address.lpSockaddr->sa_family == AF_INET6) { } else if (u->Address.lpSockaddr->sa_family == AF_INET6) {
const struct sockaddr_in6* sin6 = (const struct sockaddr_in6*)u->Address.lpSockaddr; const struct sockaddr_in6* sin6 = (const struct sockaddr_in6*)u->Address.lpSockaddr;
int cls = v6_classify(sin6->sin6_addr.s6_addr); int cls = v6_classify(sin6->sin6_addr.s6_addr);
@ -1437,6 +1420,22 @@ static void auto_socket_reconcile_config(struct AUTO_SOCKET* as) {
struct UTUN_INSTANCE* inst = as->instance; struct UTUN_INSTANCE* inst = as->instance;
if (!inst || !inst->config) return; if (!inst || !inst->config) return;
/* Тип сокета определяем по фактическому адресу (не доверяем type из конфига):
* публичный адрес → PUBLIC, приватный/link-local → NAT. */
for (struct CFG_SERVER* s = inst->config->servers; s; s = s->next) {
uint8_t by_addr = CFG_SERVER_TYPE_NAT;
if (s->ip.ss_family == AF_INET) {
if (ip_is_public(((const struct sockaddr_in*)&s->ip)->sin_addr.s_addr))
by_addr = CFG_SERVER_TYPE_PUBLIC;
} else if (s->ip.ss_family == AF_INET6) {
if (v6_classify(((const struct sockaddr_in6*)&s->ip)->sin6_addr.s6_addr) == AS_V6_DIR)
by_addr = CFG_SERVER_TYPE_PUBLIC;
}
if (s->type != by_addr)
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] server %s type %d → %d (by address)", s->name, s->type, by_addr);
s->type = by_addr;
}
/* Проход 1: удалить сокеты, которых нет в новом списке, либо которые изменились. */ /* Проход 1: удалить сокеты, которых нет в новом списке, либо которые изменились. */
struct ETCP_SOCKET* sock = inst->etcp_sockets; struct ETCP_SOCKET* sock = inst->etcp_sockets;
while (sock) { while (sock) {

33
tests/test_sock_match.c

@ -11,6 +11,7 @@
#include "../src/config_parser.h" #include "../src/config_parser.h"
#include "../src/transport_layer/etcp_connections.h" #include "../src/transport_layer/etcp_connections.h"
#include "../src/routing_layer/topo_node.h" #include "../src/routing_layer/topo_node.h"
#include "../lib/platform_compat.h"
static int fails = 0; static int fails = 0;
static int checks = 0; static int checks = 0;
@ -23,21 +24,21 @@ static struct sock_view mk_view(uint8_t role, uint8_t has_inet, uint8_t has_priv
return v; return v;
} }
static void test_role_of(void) { /* Классификация IPv4-адреса (network byte order): публичный/маршрутизируемый или нет.
/* config -> role, при ntp_reachable=1 */ * Адрес задаём «логически» (старший байт = первый октет) и переводим в s_addr через htonl. */
CHECK(sock_role_of(CFG_SERVER_TYPE_PUBLIC, NAT_TYPE_UNKNOWN, 1) == SOCK_ROLE_DIRECT, "public -> DIRECT"); static void test_ip_is_public(void) {
CHECK(sock_role_of(CFG_SERVER_TYPE_UNKNOWN, NAT_TYPE_UNKNOWN, 1) == SOCK_ROLE_DIRECT, "unknown -> DIRECT"); CHECK(ip_is_public(htonl(0x08080808u)) == 1, "8.8.8.8 public");
CHECK(sock_role_of(CFG_SERVER_TYPE_NAT, NAT_VERIFIED_EIM, 1) == SOCK_ROLE_DIRECT, "nat+EIM -> DIRECT"); CHECK(ip_is_public(htonl(0x01020304u)) == 1, "1.2.3.4 public");
CHECK(sock_role_of(CFG_SERVER_TYPE_NAT, NAT_VERIFIED_DIRECT,1) == SOCK_ROLE_DIRECT, "nat+DIRECT -> DIRECT"); CHECK(ip_is_public(htonl(0xC0A80101u)) == 0, "192.168.1.1 private");
CHECK(sock_role_of(CFG_SERVER_TYPE_NAT, NAT_VERIFIED_STRICT,1) == SOCK_ROLE_NAT, "nat+STRICT -> NAT"); CHECK(ip_is_public(htonl(0x0A000001u)) == 0, "10.0.0.1 private");
CHECK(sock_role_of(CFG_SERVER_TYPE_NAT, NAT_TYPE_UNKNOWN, 1) == SOCK_ROLE_NAT, "nat+unknown -> NAT"); CHECK(ip_is_public(htonl(0xAC100001u)) == 0, "172.16.0.1 private");
CHECK(sock_role_of(CFG_SERVER_TYPE_PRIVATE, NAT_TYPE_UNKNOWN, 1) == SOCK_ROLE_NAT, "private -> NAT"); CHECK(ip_is_public(htonl(0xAC1F0001u)) == 0, "172.31.0.1 private");
CHECK(sock_role_of(CFG_SERVER_TYPE_LOCAL, NAT_TYPE_UNKNOWN, 1) == SOCK_ROLE_LOCAL, "local -> LOCAL"); CHECK(ip_is_public(htonl(0x64400001u)) == 0, "100.64.0.1 CGNAT");
/* NTP offline: все кроме LOCAL демотируются */ CHECK(ip_is_public(htonl(0x7F000001u)) == 0, "127.0.0.1 loopback");
CHECK(sock_role_of(CFG_SERVER_TYPE_PUBLIC, NAT_TYPE_UNKNOWN, 0) == SOCK_ROLE_LOCAL, "offline public -> LOCAL"); CHECK(ip_is_public(htonl(0xA9FE0101u)) == 0, "169.254.1.1 link-local");
CHECK(sock_role_of(CFG_SERVER_TYPE_NAT, NAT_VERIFIED_EIM, 0) == SOCK_ROLE_LOCAL, "offline nat+EIM -> LOCAL"); CHECK(ip_is_public(htonl(0x00000000u)) == 0, "0.0.0.0");
CHECK(sock_role_of(CFG_SERVER_TYPE_PRIVATE, NAT_TYPE_UNKNOWN, 0) == SOCK_ROLE_LOCAL, "offline private -> LOCAL"); CHECK(ip_is_public(htonl(0xE0000001u)) == 0, "224.0.0.1 multicast");
CHECK(sock_role_of(CFG_SERVER_TYPE_LOCAL, NAT_TYPE_UNKNOWN, 0) == SOCK_ROLE_LOCAL, "offline local -> LOCAL"); CHECK(ip_is_public(htonl(0xFFFFFFFFu)) == 0, "255.255.255.255 reserved");
} }
static void test_target_class(void) { static void test_target_class(void) {
@ -78,7 +79,7 @@ static void test_match(void) {
} }
int main(void) { int main(void) {
test_role_of(); test_ip_is_public();
test_target_class(); test_target_class();
test_match(); test_match();
if (fails) { printf("RESULT: %d/%d failed\n", fails, checks); return 1; } if (fails) { printf("RESULT: %d/%d failed\n", fails, checks); return 1; }

Loading…
Cancel
Save