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.
130 lines
8.3 KiB
130 lines
8.3 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.evidence_id && 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 socket=%s mapping=%s inbound=%s phase=%u reason=%s", |
|
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.evidence_id == socket->address_profile.evidence_id && |
|
meta->address_profile.checked_at == socket->address_profile.checked_at && |
|
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.checked_at++; |
|
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_evidence,nat_checked,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((uint64_t)sqlite3_column_int64(statement, 2) == saved.evidence_id); |
|
assert((uint64_t)sqlite3_column_int64(statement, 3) == saved.checked_at); |
|
assert(sqlite3_column_int(statement, 4) == ADDRESS_SOURCE_ETCP); |
|
sqlite3_finalize(statement); |
|
} |
|
|
|
int main(void) { |
|
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_NAT, 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.helper_reselections > 0 && status.helper == helper->node_id && status.observer == observer->node_id); |
|
assert(socket->address_profile.evidence_id != second_socket->address_profile.evidence_id); |
|
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)); |
|
|
|
/* Повторная проверка не объявляет старый контакт независимым. */ |
|
uint64_t old_evidence = socket->address_profile.evidence_id; |
|
nat_detection_request_check_all(owner->nat_det); |
|
uint64_t end = get_time_tb() + 180000; |
|
while (get_time_tb() < end && socket->address_profile.evidence_id == old_evidence) uasync_poll(fixture.ua, 10); |
|
assert(socket->address_profile.evidence_id != old_evidence); |
|
assert(socket->address_profile.mapping == ADDRESS_MAP_UNCHANGED && socket->address_profile.inbound == ADDRESS_IN_UNKNOWN); |
|
|
|
/* Закрытие 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); |
|
return 0; |
|
}
|
|
|