|
|
|
|
@ -4,7 +4,8 @@
|
|
|
|
|
#include "test_clock.h" |
|
|
|
|
|
|
|
|
|
enum fault_mode { FAULT_NONE, FAULT_NO_PONG, FAULT_FILTERED, FAULT_LATE_PRE, FAULT_CONTACT, |
|
|
|
|
FAULT_DEPENDENT, FAULT_UNSTABLE, FAULT_NO_REPORT, FAULT_INGRESS_BLOCKED, FAULT_NO_ANCHOR }; |
|
|
|
|
FAULT_DEPENDENT, FAULT_UNSTABLE, FAULT_NO_REPORT, FAULT_INGRESS_BLOCKED, FAULT_NO_ANCHOR, |
|
|
|
|
FAULT_LOCAL_INGRESS, FAULT_LOCAL_OBSERVER, FAULT_LOCAL_HELPER_MAPPING }; |
|
|
|
|
|
|
|
|
|
static struct { |
|
|
|
|
struct nat_fixture *fixture; |
|
|
|
|
@ -23,6 +24,8 @@ static struct {
|
|
|
|
|
ssize_t __real_socket_sendto(socket_t, const void *, size_t, const struct sockaddr *, socklen_t); |
|
|
|
|
void __real_nat_detection_record_observation(struct NAT_DETECTION *, struct ETCP_SOCKET *, const uint8_t *, |
|
|
|
|
const struct sockaddr_storage *, const uint8_t *, size_t); |
|
|
|
|
void __real_nat_detection_receive_ingress(struct NAT_DETECTION *, struct ETCP_SOCKET *, const uint8_t *, |
|
|
|
|
const struct sockaddr_storage *, const uint8_t *, size_t); |
|
|
|
|
|
|
|
|
|
static void ignored_ping(int success, uint16_t rtt, void *arg, uint64_t nonce, const uint8_t *data, size_t length) { |
|
|
|
|
(void)success; (void)rtt; (void)arg; (void)nonce; (void)data; (void)length; |
|
|
|
|
@ -59,6 +62,7 @@ ssize_t __wrap_socket_sendto(socket_t fd, const void *packet, size_t length, con
|
|
|
|
|
int to_owner = route_ping_addr_equal(&destination, &fault.socket->local_addr, 1); |
|
|
|
|
if (to_owner && source->instance == fault.alias && plain[3] == ETCP_PING && |
|
|
|
|
(plain[4] & ETCP_PING_FLAG_ADDRESS_CHECK)) { |
|
|
|
|
if (fault.mode == FAULT_LOCAL_HELPER_MAPPING) { fault.dropped++; return length; } |
|
|
|
|
fault.altered++; |
|
|
|
|
return __real_socket_sendto(fault.alias_source->fd, packet, length, target, target_length); |
|
|
|
|
} |
|
|
|
|
@ -99,6 +103,15 @@ ssize_t __wrap_socket_sendto(socket_t fd, const void *packet, size_t length, con
|
|
|
|
|
|
|
|
|
|
void __wrap_nat_detection_record_observation(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket, |
|
|
|
|
const uint8_t *key, const struct sockaddr_storage *source, const uint8_t *tag, size_t length) { |
|
|
|
|
if (fault.fixture && socket->instance == fault.observer && length == ROUTE_OBSERVE_TAG_SIZE + 9 && |
|
|
|
|
((fault.mode == FAULT_LOCAL_OBSERVER && !memcmp(key, fault.owner->my_keys.public_key, 32)) || |
|
|
|
|
(fault.mode == FAULT_LOCAL_HELPER_MAPPING && !memcmp(key, fault.alias->my_keys.public_key, 32)))) { |
|
|
|
|
/* Настоящий source — loopback; в этом callback включаем промышленную проверку публичности. */ |
|
|
|
|
fault.injected++; |
|
|
|
|
nat_detection_set_allow_local(detector, 0); |
|
|
|
|
__real_nat_detection_record_observation(detector, socket, key, source, tag, length); |
|
|
|
|
nat_detection_set_allow_local(detector, 1); return; |
|
|
|
|
} |
|
|
|
|
if (!fault.fixture || socket->instance != fault.helper || memcmp(key, fault.owner->my_keys.public_key, 32) || |
|
|
|
|
length != ROUTE_OBSERVE_TAG_SIZE + 9) { |
|
|
|
|
__real_nat_detection_record_observation(detector, socket, key, source, tag, length); return; |
|
|
|
|
@ -116,6 +129,31 @@ void __wrap_nat_detection_record_observation(struct NAT_DETECTION *detector, str
|
|
|
|
|
__real_nat_detection_record_observation(detector, socket, key, &observed, tag, length); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Подлинная проба приходит с локального IP, несмотря на пригодный предварительный адрес помощника. */ |
|
|
|
|
void __wrap_nat_detection_receive_ingress(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket, |
|
|
|
|
const uint8_t *key, const struct sockaddr_storage *source, const uint8_t *tag, size_t length) { |
|
|
|
|
if (fault.fixture && fault.mode == FAULT_LOCAL_INGRESS && detector == fault.owner->nat_det && |
|
|
|
|
!memcmp(key, fault.alias->my_keys.public_key, 32)) { |
|
|
|
|
struct nat_check_status before, after; |
|
|
|
|
assert(!nat_detection_get_status(detector, socket, &before)); |
|
|
|
|
nat_detection_set_allow_local(detector, 0); |
|
|
|
|
/* Неверный token не должен позволять постороннему пакету сменить кандидата. */ |
|
|
|
|
uint8_t stale[ROUTE_OBSERVE_TAG_SIZE + 9]; assert(length == sizeof(stale)); |
|
|
|
|
memcpy(stale, tag, length); stale[length - 1] ^= 1; |
|
|
|
|
__real_nat_detection_receive_ingress(detector, socket, key, source, stale, length); |
|
|
|
|
assert(!nat_detection_get_status(detector, socket, &after)); |
|
|
|
|
assert(after.phase == before.phase && after.helper_reselections == before.helper_reselections); |
|
|
|
|
__real_nat_detection_receive_ingress(detector, socket, key, source, tag, length); |
|
|
|
|
assert(!nat_detection_get_status(detector, socket, &after)); |
|
|
|
|
if (before.phase) { |
|
|
|
|
assert(!after.phase && after.helper_reselections == before.helper_reselections + 1); |
|
|
|
|
assert(after.reason == NAT_REASON_NO_HELPER); fault.injected++; |
|
|
|
|
} |
|
|
|
|
nat_detection_set_allow_local(detector, 1); return; |
|
|
|
|
} |
|
|
|
|
__real_nat_detection_receive_ingress(detector, socket, key, source, tag, length); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void run_case(enum fault_mode mode, uint8_t mapping, uint8_t inbound) { |
|
|
|
|
struct nat_fixture fixture; nat_fixture_init(&fixture); |
|
|
|
|
struct UTUN_INSTANCE *owner = nat_fixture_node(&fixture, "127.0.0.2", NULL, NULL); |
|
|
|
|
@ -141,12 +179,13 @@ static void run_case(enum fault_mode mode, uint8_t mapping, uint8_t inbound) {
|
|
|
|
|
if (mode == FAULT_NO_PONG || mode == FAULT_FILTERED || mode == FAULT_LATE_PRE || mode == FAULT_NO_REPORT) assert(fault.dropped); |
|
|
|
|
if (mode == FAULT_LATE_PRE || mode == FAULT_CONTACT) assert(fault.injected); |
|
|
|
|
if (mode == FAULT_DEPENDENT || mode == FAULT_UNSTABLE) assert(fault.altered); |
|
|
|
|
if (mode == FAULT_LOCAL_OBSERVER) assert(fault.injected && status.helper_reselections && !fault.observations); |
|
|
|
|
fault.fixture = NULL; nat_fixture_cleanup(&fixture); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS NAT fault mode=%d", mode); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Разные ключи не означают разные IP: неподходящего помощника отклоняет получатель. */ |
|
|
|
|
static void run_alias(const char *alias_ip) { |
|
|
|
|
static void run_alias(const char *alias_ip, enum fault_mode mode) { |
|
|
|
|
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)) { |
|
|
|
|
@ -164,6 +203,7 @@ static void run_alias(const char *alias_ip) {
|
|
|
|
|
struct ETCP_SOCKET *socket = test_etcp_bound_socket(owner, "udp"); |
|
|
|
|
memset(&fault, 0, sizeof(fault)); fault.fixture = &fixture; fault.owner = owner; fault.socket = socket; |
|
|
|
|
fault.observer = observer; fault.helper = helper; fault.alias = alias; fault.alias_source = test_etcp_bound_socket(alias, "alias"); |
|
|
|
|
fault.mode = mode; |
|
|
|
|
struct nat_check_status status = {0}; uint64_t end = get_time_tb() + 400000; |
|
|
|
|
nat_detection_get_status(owner->nat_det, socket, &status); uint32_t rounds = status.rounds; |
|
|
|
|
assert(!nat_detection_request_check_socket(owner->nat_det, socket, observer->node_id)); |
|
|
|
|
@ -173,11 +213,34 @@ static void run_alias(const char *alias_ip) {
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_NAT, "alias fixture result source=%s helper=%016llx expected=%016llx reselections=%u remapped=%u", |
|
|
|
|
alias_ip, (unsigned long long)status.helper, (unsigned long long)helper->node_id, |
|
|
|
|
status.helper_reselections, fault.altered); |
|
|
|
|
assert(status.helper == helper->node_id && status.helper_reselections && fault.altered); |
|
|
|
|
assert(status.helper == helper->node_id && status.helper_reselections); |
|
|
|
|
if (mode == FAULT_LOCAL_HELPER_MAPPING) assert(fault.injected && fault.dropped); |
|
|
|
|
else assert(fault.altered); |
|
|
|
|
if (mode == FAULT_LOCAL_INGRESS) assert(fault.injected); |
|
|
|
|
fault.fixture = NULL; nat_fixture_cleanup(&fixture); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS NAT receiver rejects helper source=%s", alias_ip); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* LAN INIT_RESPONSE описывает свой путь и не инвалидирует уже известный интернет-профиль. */ |
|
|
|
|
static void run_lan_observation(void) { |
|
|
|
|
struct nat_fixture fixture; nat_fixture_init(&fixture); |
|
|
|
|
struct UTUN_INSTANCE *owner = nat_fixture_node(&fixture, "127.0.0.2", NULL, NULL); |
|
|
|
|
struct UTUN_INSTANCE *peer = nat_fixture_node(&fixture, "127.0.0.3", NULL, NULL); |
|
|
|
|
nat_detection_set_automatic(owner->nat_det, 0); nat_detection_set_automatic(peer->nat_det, 0); |
|
|
|
|
struct ETCP_CONN *connection = nat_fixture_connect(&fixture, owner, "udp", peer); |
|
|
|
|
struct ETCP_SOCKET *socket = test_etcp_bound_socket(owner, "udp"); |
|
|
|
|
struct address_profile profile = {ADDRESS_MAP_EIM, ADDRESS_IN_OPEN, ADDRESS_SOURCE_ETCP}; |
|
|
|
|
struct sockaddr_storage external = socket->local_addr; |
|
|
|
|
assert(inet_pton(AF_INET, "8.8.8.8", &((struct sockaddr_in *)&external)->sin_addr) == 1); |
|
|
|
|
socket->address_profile = profile; socket->nat_addr = external; |
|
|
|
|
nat_detection_set_allow_local(owner->nat_det, 0); |
|
|
|
|
nat_detection_observed_by_peer(owner->nat_det, connection->links, &socket->local_addr); |
|
|
|
|
assert(!memcmp(&socket->address_profile, &profile, sizeof(profile))); |
|
|
|
|
assert(route_ping_addr_equal(&socket->nat_addr, &external, 1)); |
|
|
|
|
nat_fixture_cleanup(&fixture); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS LAN observation preserves internet mapping and inbound profile"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* У наблюдателя первый UDP listener недоступен, но управление по TCP и второй IP работают. */ |
|
|
|
|
static void run_endpoint_retry(void) { |
|
|
|
|
struct nat_fixture fixture; nat_fixture_init(&fixture); |
|
|
|
|
@ -219,6 +282,9 @@ int main(void) {
|
|
|
|
|
run_case(FAULT_NO_REPORT, ADDRESS_MAP_UNKNOWN, ADDRESS_IN_UNKNOWN); |
|
|
|
|
run_case(FAULT_INGRESS_BLOCKED, ADDRESS_MAP_UNCHANGED, ADDRESS_IN_UNKNOWN); |
|
|
|
|
run_case(FAULT_NO_ANCHOR, ADDRESS_MAP_UNCHANGED, ADDRESS_IN_UNKNOWN); |
|
|
|
|
run_alias("127.0.0.3"); run_alias("127.0.0.2"); run_endpoint_retry(); |
|
|
|
|
run_case(FAULT_LOCAL_OBSERVER, ADDRESS_MAP_UNKNOWN, ADDRESS_IN_UNKNOWN); |
|
|
|
|
run_alias("127.0.0.3", FAULT_NONE); run_alias("127.0.0.2", FAULT_NONE); |
|
|
|
|
run_alias("127.0.0.9", FAULT_LOCAL_INGRESS); run_alias("127.0.0.9", FAULT_LOCAL_HELPER_MAPPING); |
|
|
|
|
run_lan_observation(); run_endpoint_retry(); |
|
|
|
|
test_clock_finish(); return 0; |
|
|
|
|
} |
|
|
|
|
|