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.
 
 
 
 
 
 

196 lines
13 KiB

/* Детектор владеет профилем сокета; помощник с IP наблюдателя заменяется.
* Настоящие ETCP/NCD/PING/STCP, без групповой автосвязности и без подмены времени. */
#include "nat_fixture.h"
#include "ntp_time.h"
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;
while (get_time_tb() < end) {
if (socket->address_profile.source && socket->address_profile.mapping == mapping &&
socket->address_profile.inbound == inbound) return;
uasync_poll(fixture->ua, 10);
}
struct nat_check_status status = {0};
nat_detection_get_status(socket->instance->nat_det, socket, &status);
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "profile wait failed node=%016llx socket=%s mapping=%s inbound=%s phase=%u reason=%s",
(unsigned long long)socket->instance->node_id, socket->name, address_mapping_name(socket->address_profile.mapping),
address_inbound_name(socket->address_profile.inbound), status.phase, nat_check_reason_name(status.reason));
abort();
}
static void check_snapshot(struct UTUN_INSTANCE *owner, struct ETCP_SOCKET *socket) {
struct TOPO_NODE *node = topo_node_registry_find(owner->topo_groups, owner->node_id);
assert(node && topo_node_verify(node) == 0);
struct TOPO_SOCKMETA4 *meta = node->v4_sock_meta;
while (meta && meta->id != socket->sock_id) meta = meta->next;
assert(meta && meta->address_profile.mapping == socket->address_profile.mapping &&
meta->address_profile.inbound == socket->address_profile.inbound &&
meta->address_profile.source == ADDRESS_SOURCE_ETCP);
struct address_profile saved = meta->address_profile;
meta->address_profile.mapping = ADDRESS_MAP_DEPENDENT;
assert(topo_node_verify(node) < 0);
meta->address_profile = saved;
meta->address_profile.source = ADDRESS_SOURCE_STUN;
assert(topo_node_verify(node) < 0);
meta->address_profile = saved;
meta->address_profile.inbound = ADDRESS_IN_CONTACT;
assert(topo_node_verify(node) < 0);
meta->address_profile = saved; assert(topo_node_verify(node) == 0);
sqlite3_stmt *statement = NULL;
assert(sqlite3_prepare_v2(owner->topo_sqlite_db,
"SELECT nat_mapping,nat_inbound,nat_source FROM node_addresses WHERE node_id=? AND socket_id=?",
-1, &statement, NULL) == SQLITE_OK);
sqlite3_bind_int64(statement, 1, (sqlite3_int64)owner->node_id); sqlite3_bind_int(statement, 2, socket->sock_id);
assert(sqlite3_step(statement) == SQLITE_ROW);
assert(sqlite3_column_int(statement, 0) == saved.mapping && sqlite3_column_int(statement, 1) == saved.inbound);
assert(sqlite3_column_int(statement, 2) == ADDRESS_SOURCE_ETCP);
sqlite3_finalize(statement);
}
/* Недостаточная UTUN-группа и достаточная CHAT-группа; helper доступен только через TCP next hop. */
static void test_all_groups_tcp(void) {
struct nat_fixture fixture; nat_fixture_init(&fixture);
struct UTUN_INSTANCE *owner = nat_fixture_node(&fixture, "127.0.0.2", NULL, "[chatserver]\ngroup_autoconnect=0\n");
struct UTUN_INSTANCE *observer = nat_fixture_node(&fixture, "127.0.0.3", NULL,
"[server:tcp]\naddr=127.0.0.3:0\ntype=public\ntransport=tcp\n[chatserver]\ngroup_autoconnect=0\n");
struct UTUN_INSTANCE *helper = nat_fixture_node(&fixture, "127.0.0.4", NULL,
"[server:tcp]\naddr=127.0.0.4:0\ntype=public\ntransport=tcp\n[chatserver]\ngroup_autoconnect=0\n");
nat_detection_set_automatic(observer->nat_det, 0); nat_detection_set_automatic(helper->nat_det, 0);
struct ETCP_CONN *ab = nat_fixture_connect(&fixture, owner, "udp", observer);
struct ETCP_SOCKET *socket = test_etcp_bound_socket(owner, "udp");
nat_detection_request_check_all(owner->nat_det);
struct nat_check_status status = {0}; uint64_t end = get_time_tb() + 30000;
do { uasync_poll(fixture.ua, 10); assert(!nat_detection_get_status(owner->nat_det, socket, &status)); }
while (get_time_tb() < end && status.reason != NAT_REASON_NO_HELPER);
assert(status.reason == NAT_REASON_NO_HELPER && !socket->address_profile.source);
struct UTUN_INSTANCE *nodes[] = {owner, observer, helper};
struct TOPO_GROUP *groups[3]; uint8_t signature[64] = {1};
for (unsigned i = 0; i < 3; i++) {
sqlite3 *db = nodes[i]->topo_sqlite_db;
assert(!topo_node_sqlite_channel_put(db, "42", "NAT group test", owner->node_id, owner->my_keys.public_key,
NULL, owner->my_ed25519_pubkey, NULL, signature));
for (unsigned j = 0; j < 3; j++)
assert(!topo_node_sqlite_member_block_put(db, "42", nodes[j]->node_id, signature, 1, signature, 1,
nodes[j]->my_keys.public_key, nodes[j]->my_ed25519_pubkey, "{}", NULL, 0, signature));
groups[i] = topo_groups_create_group(nodes[i]->topo_groups, 42, TOPO_GROUP_TYPE_CHAT, "42");
assert(groups[i]);
}
assert(!topo_group_new_conn(groups[0], ab));
struct TOPO_NODE *node = topo_node_registry_find(helper->topo_groups, helper->node_id);
struct NODE_CONN_DIRECT **handle = &fixture.handles[fixture.handle_count++];
assert(node_conn_direct_open_node(observer, helper->node_id, NULL, NULL, handle, node,
test_etcp_bound_socket(observer, "tcp")) != NCD_ERR);
struct ETCP_CONN *bc = NULL; end = get_time_tb() + 50000;
do { uasync_poll(fixture.ua, 10); bc = node_conn_direct_get_conn(*handle); }
while (get_time_tb() < end && (!bc || !bc->links_up));
assert(bc && bc->links_up && !topo_group_new_conn(groups[1], bc));
for (struct ETCP_LINK *link = bc->links; link; link = link->next) assert(link->is_tcp);
end = get_time_tb() + 50000;
while (get_time_tb() < end && (!topo_group_peer_ready(groups[0], observer->node_id) ||
!topo_group_peer_ready(groups[1], helper->node_id) || !topo_node_best_path(topo_node_find_by_id(groups[0], helper->node_id))))
uasync_poll(fixture.ua, 10);
assert(topo_group_peer_ready(groups[0], observer->node_id) && topo_group_peer_ready(groups[1], helper->node_id));
assert(!instance_find_conn(owner, helper->node_id));
wait_profile(&fixture, socket, ADDRESS_MAP_UNCHANGED, ADDRESS_IN_OPEN);
assert(!nat_detection_get_status(owner->nat_det, socket, &status) && status.group_id == 42 && status.helper == helper->node_id);
assert(!instance_find_conn(owner, helper->node_id));
uint64_t old_round = status.round;
assert(!nat_detection_request_check_socket(owner->nat_det, socket, observer->node_id));
end = get_time_tb() + 30000;
do { uasync_poll(fixture.ua, 10); assert(!nat_detection_get_status(owner->nat_det, socket, &status)); }
while (get_time_tb() < end && (status.round == old_round || !status.phase));
assert(status.phase && status.group_id == 42);
topo_groups_remove_group(owner->topo_groups, 42);
assert(!nat_detection_get_status(owner->nat_det, socket, &status) && !status.phase && !status.group_id);
assert(socket->address_profile.mapping == ADDRESS_MAP_UNCHANGED && socket->address_profile.inbound == ADDRESS_IN_OPEN);
assert(ab->links_up && topo_group_peer_ready(topo_groups_find(owner->topo_groups, TOPO_GROUP_UTUN), observer->node_id));
nat_fixture_cleanup(&fixture);
}
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);
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);
if (sc_derive_node_id_from_pubkey(first.public_key) > sc_derive_node_id_from_pubkey(second.public_key)) {
struct SC_MYKEYS swap = first; first = second; second = swap;
}
struct UTUN_INSTANCE *owner = nat_fixture_node(&fixture, "127.0.0.2", NULL,
"[server:second]\naddr=127.0.0.5:0\ntype=public\n"
"[server:tcp]\naddr=127.0.0.2:0\ntype=public\ntransport=tcp\n");
struct UTUN_INSTANCE *observer = nat_fixture_node(&fixture, "127.0.0.3", NULL, NULL);
struct UTUN_INSTANCE *same_ip = nat_fixture_node(&fixture, "127.0.0.3", &first, NULL);
struct UTUN_INSTANCE *helper = nat_fixture_node(&fixture, "127.0.0.4", &second, NULL);
struct ETCP_SOCKET *socket = test_etcp_bound_socket(owner, "udp");
struct ETCP_SOCKET *second_socket = test_etcp_bound_socket(owner, "second");
struct ETCP_SOCKET *tcp = test_etcp_bound_socket(owner, "tcp");
struct ETCP_SOCKET *observer_socket = test_etcp_bound_socket(observer, "udp");
assert(socket->sock_id != observer_socket->sock_id);
/* B уже знает обоих помощниках, но A не отправлял UDP ни одному из них. */
nat_fixture_connect(&fixture, observer, "udp", same_ip);
nat_fixture_connect(&fixture, observer, "udp", helper);
struct ETCP_CONN *connection = nat_fixture_connect(&fixture, owner, "udp", observer);
assert(connection->links && connection->links->remote_socket_id == observer_socket->sock_id);
assert(connection->links->local_as_seen_by_peer.ss_family == AF_INET);
assert(route_ping_addr_equal(&connection->links->local_as_seen_by_peer, &socket->local_addr, 1));
/* Подписанная запись A нужна независимому испытанию настоящего TCP listener. */
nat_fixture_connect(&fixture, observer, "udp", owner);
owner->ntp.reachable = 0; /* ни измерение, ни публикация не требуют интернет-NTP */
nat_detection_request_check_all(owner->nat_det);
wait_profile(&fixture, socket, ADDRESS_MAP_UNCHANGED, ADDRESS_IN_OPEN);
wait_profile(&fixture, second_socket, ADDRESS_MAP_UNCHANGED, ADDRESS_IN_OPEN);
wait_profile(&fixture, tcp, ADDRESS_MAP_UNCHANGED, ADDRESS_IN_OPEN);
struct nat_check_status status;
assert(nat_detection_get_status(owner->nat_det, socket, &status) == 0);
assert(status.group_id == TOPO_GROUP_UTUN && status.helper == helper->node_id);
assert(status.observer == observer->node_id || status.observer == same_ip->node_id);
struct nat_check_status second_status;
assert(nat_detection_get_status(owner->nat_det, second_socket, &second_status) == 0);
assert(status.round != second_status.round);
check_snapshot(owner, socket);
check_snapshot(owner, second_socket);
check_snapshot(owner, tcp);
/* Профиль B не заимствует профиль A, даже когда транспорт переиспользуется в обе стороны. */
assert(!route_ping_addr_equal(&socket->local_addr, &observer_socket->local_addr, 1));
assert(!route_ping_addr_equal(&socket->local_addr, &test_etcp_bound_socket(helper, "udp")->local_addr, 1));
/* Неполный повторный ingress сохраняет успех и не изменяет подписанный NODEINFO. */
uint64_t old_round = status.round;
struct TOPO_NODE *snapshot = topo_node_registry_find(owner->topo_groups, owner->node_id);
uint64_t old_timestamp = snapshot->timestamp;
nat_detection_request_check_all(owner->nat_det);
uint64_t end = get_time_tb() + 180000;
do { uasync_poll(fixture.ua, 10); assert(!nat_detection_get_status(owner->nat_det, socket, &status)); }
while (get_time_tb() < end && (status.round == old_round || status.phase || status.cycling));
assert(status.round != old_round && !status.phase && status.reason == NAT_REASON_CONTACT_HISTORY);
assert(socket->address_profile.mapping == ADDRESS_MAP_UNCHANGED && socket->address_profile.inbound == ADDRESS_IN_OPEN);
assert(snapshot->timestamp == old_timestamp && topo_node_verify(snapshot) == 0);
/* Закрытие endpoint посреди запроса и переиспользование ID/порта: старые ответы не применяются. */
nat_detection_request_check_all(owner->nat_det);
end = get_time_tb() + 20000;
do { uasync_poll(fixture.ua, 0); nat_detection_get_status(owner->nat_det, socket, &status); }
while (!status.phase && get_time_tb() < end);
assert(status.phase);
uint8_t old_id = socket->sock_id; uint64_t old_generation = socket->generation;
struct CFG_SERVER recreated; memset(&recreated, 0, sizeof(recreated));
recreated.ip = socket->local_addr; recreated.type = CFG_SERVER_TYPE_PUBLIC; recreated.mtu = 1500; recreated.fib = -1;
snprintf(recreated.name, sizeof(recreated.name), "recreated");
etcp_socket_remove(socket);
owner->next_socket_id = old_id;
socket = etcp_socket_add(owner, &recreated);
assert(socket && socket->sock_id == old_id && socket->generation != old_generation);
end = get_time_tb() + 2000;
while (get_time_tb() < end) uasync_poll(fixture.ua, 10);
assert(socket->address_profile.mapping == ADDRESS_MAP_UNKNOWN && socket->address_profile.inbound == ADDRESS_IN_UNKNOWN);
nat_fixture_cleanup(&fixture);
test_all_groups_tcp();
return 0;
}