Browse Source

test(nat): cover accepted UDP and TCP profiles and LAN exclusions

stream-multiview
evgeny 7 days ago
parent
commit
094a8bb499
  1. 11
      doc/nat_detection.txt
  2. 41
      tests/test_nat_detection.c

11
doc/nat_detection.txt

@ -5,6 +5,13 @@
при обращении к разным IP и проходят ли к нему пакеты от незнакомого IP.
Проверяется именно сокет, через который работает приложение.
Есть быстрый путь без помощников: если рабочий сокет принял подключение
и локальный IP и фактический IP подключившегося узла публичные,
ему сразу ставится direct/open (UNCHANGED + OPEN). Это правило действует
для UDP и TCP, даже если с IP пира раньше уже был исходящий обмен.
Локальные подключения и SOCKS интернет-вход не подтверждают. Для TCP
фактический локальный IP должен совпадать с IP интерфейса сокета.
Нужны три узла: A проверяет себя, B наблюдает его внешний адрес,
C помогает проверить входящие пакеты. Команды и результаты передаются
по уже работающей сети uTun, в том числе через TCP или промежуточные узлы.
@ -96,5 +103,7 @@ TCP-пира: по готовому защищённому соединению
IP совпали — трансляция IP не обнаружена; отличаются — NAT есть, тип UNKNOWN.
Порты при сравнении не учитываются. Публичный внешний IP сохраняется с портом 0:
входящий порт неизвестен, поэтому NCD не использует эту запись для подключения.
Тип NAT и доступность TCP-входа не проверяются. Через SOCKS внешний IP прокси
Активных проб типа NAT и TCP-входа нет; OPEN устанавливается по принятому
публичному подключению и сохраняется при последующих совпавших наблюдениях IP.
Через SOCKS внешний IP прокси
не записывается как собственный адрес.

41
tests/test_nat_detection.c

@ -48,7 +48,41 @@ static void check_snapshot(struct UTUN_INSTANCE *owner, struct ETCP_SOCKET *sock
sqlite3_finalize(statement);
}
/* Один TCP-пир даёт только наличие NAT и IP:0; локальный IP сам по себе достаточен для NAT. */
/* Настоящий входящий UDP/STCP публикует direct/open только у получателя; LAN не подтверждает интернет-вход. */
static void test_incoming_connections(void) {
struct nat_fixture fixture; nat_fixture_init(&fixture);
const char *tcp_config = "[server:tcp]\naddr=127.0.0.2:0\ntype=public\ntransport=tcp\n";
struct UTUN_INSTANCE *client = nat_fixture_node(&fixture, "127.0.0.2", NULL, tcp_config);
struct UTUN_INSTANCE *server = nat_fixture_node(&fixture, "127.0.0.3", NULL,
"[server:tcp]\naddr=127.0.0.3:0\ntype=public\ntransport=tcp\n");
struct UTUN_INSTANCE *lan = nat_fixture_node(&fixture, "127.0.0.4", NULL,
"[server:tcp]\naddr=127.0.0.4:0\ntype=public\ntransport=tcp\n");
nat_detection_set_automatic(client->nat_det, 0); nat_detection_set_automatic(server->nat_det, 0);
nat_detection_set_automatic(lan->nat_det, 0); nat_detection_set_allow_local(lan->nat_det, 0);
struct ETCP_SOCKET *udp = test_etcp_bound_socket(server, "udp"), *tcp = test_etcp_bound_socket(server, "tcp");
nat_fixture_connect(&fixture, client, "udp", server);
assert(udp->address_profile.mapping == ADDRESS_MAP_UNCHANGED && udp->address_profile.inbound == ADDRESS_IN_OPEN);
assert(test_etcp_bound_socket(client, "udp")->address_profile.inbound == ADDRESS_IN_UNKNOWN);
check_snapshot(server, udp);
nat_fixture_connect(&fixture, client, "tcp", server);
assert(tcp->address_profile.mapping == ADDRESS_MAP_UNCHANGED && tcp->address_profile.inbound == ADDRESS_IN_OPEN);
assert(route_ping_addr_equal(&tcp->nat_addr, &tcp->interface_addr, 0));
assert(!((struct sockaddr_in *)&tcp->nat_addr)->sin_port);
assert(test_etcp_bound_socket(client, "tcp")->address_profile.inbound == ADDRESS_IN_UNKNOWN);
check_snapshot(server, tcp);
/* Отчёт пира уже принят по настоящему TCP: OPEN у listener сохранён, исходящий порт остаётся нулём. */
struct nat_check_status status;
assert(!nat_detection_get_status(server->nat_det, tcp, &status) && !status.rounds && !status.helper && !status.reason);
nat_fixture_connect(&fixture, client, "udp", lan);
nat_fixture_connect(&fixture, client, "tcp", lan);
assert(test_etcp_bound_socket(lan, "udp")->address_profile.inbound == ADDRESS_IN_UNKNOWN);
assert(test_etcp_bound_socket(lan, "tcp")->address_profile.inbound == ADDRESS_IN_UNKNOWN);
assert(tcp->address_profile.inbound == ADDRESS_IN_OPEN && udp->address_profile.inbound == ADDRESS_IN_OPEN);
nat_fixture_cleanup(&fixture);
DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS incoming UDP/TCP: receiving socket DIRECT/OPEN, outbound unchanged, IP:0, signed profile, LAN ignored");
}
/* Исходящий TCP даёт только наличие NAT и IP:0; локальный IP сам по себе достаточен для NAT. */
static void test_tcp_presence(void) {
struct nat_fixture fixture; nat_fixture_init(&fixture);
struct UTUN_INSTANCE *owner = nat_fixture_node(&fixture, "127.0.0.2", NULL,
@ -249,6 +283,10 @@ int main(void) {
nat_fixture_connect(&fixture, observer, "udp", owner);
owner->ntp.reachable = 0; /* ни измерение, ни публикация не требуют интернет-NTP */
nat_detection_request_check_all(owner->nat_det);
struct nat_check_status initial_status;
uint64_t initial_end = get_time_tb() + 180000;
do { uasync_poll(fixture.ua, 10); assert(!nat_detection_get_status(owner->nat_det, socket, &initial_status)); }
while (get_time_tb() < initial_end && (!initial_status.rounds || initial_status.phase || initial_status.cycling));
wait_profile(&fixture, socket, ADDRESS_MAP_UNCHANGED, ADDRESS_IN_OPEN);
wait_profile(&fixture, second_socket, ADDRESS_MAP_UNCHANGED, ADDRESS_IN_OPEN);
@ -302,5 +340,6 @@ int main(void) {
nat_fixture_cleanup(&fixture);
test_all_groups_tcp();
test_tcp_presence();
test_incoming_connections();
return 0;
}

Loading…
Cancel
Save