nat: reject local probe sources and preserve internet profiles on LAN
This commit is contained in:
1 parent
308eb07742
commit
83efa229ed
3 files changed
+112
-18
No files matched your search
@@ -517,6 +517,21 @@ void nat_detection_record_observation(struct NAT_DETECTION *detector, struct ETC
|
|||||||
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT observation has invalid group/identity group=%016llx owner=%016llx",
|
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT observation has invalid group/identity group=%016llx owner=%016llx",
|
||||||
(unsigned long long)group_id, (unsigned long long)owner); return;
|
(unsigned long long)group_id, (unsigned long long)owner); return;
|
||||||
}
|
}
|
||||||
|
int eligible = public_address(detector, source);
|
||||||
|
if (!eligible) DEBUG_WARN(DEBUG_CATEGORY_NAT,
|
||||||
|
"NAT observation rejected by receiver group=%016llx owner=%016llx round=%016llx source=%s reason=non_public_source",
|
||||||
|
(unsigned long long)group_id, (unsigned long long)owner, (unsigned long long)round, sockaddr_storage_to_str(source).str);
|
||||||
|
if (length == NAT_QUERY_TAG_SIZE) {
|
||||||
|
struct nat_wire report = {.command = NAT_OBSERVATION, .owner_socket = tag[8], .step = tag[ROUTE_OBSERVE_TAG_SIZE],
|
||||||
|
.reason = eligible ? NAT_REASON_NONE : NAT_REASON_INVALID_OBSERVATION};
|
||||||
|
write64(report.owner, owner); write64(report.generation, generation); write64(report.round, round);
|
||||||
|
write64(report.token, read64(tag + ROUTE_OBSERVE_TAG_SIZE + 1));
|
||||||
|
write64(report.observer, detector->instance->node_id); write64(report.helper, detector->instance->node_id);
|
||||||
|
if (route_ping_addr_encode(report.target, source) || send_control(detector, group_id, owner, &report))
|
||||||
|
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT routed observation delivery failed group=%016llx owner=%016llx round=%016llx",
|
||||||
|
(unsigned long long)group_id, (unsigned long long)owner, (unsigned long long)round);
|
||||||
|
}
|
||||||
|
if (!eligible) return;
|
||||||
struct nat_binding *binding = binding_find(detector, group_id, owner, generation, round);
|
struct nat_binding *binding = binding_find(detector, group_id, owner, generation, round);
|
||||||
if (!binding) {
|
if (!binding) {
|
||||||
unsigned count = 0;
|
unsigned count = 0;
|
||||||
@@ -532,15 +547,6 @@ void nat_detection_record_observation(struct NAT_DETECTION *detector, struct ETC
|
|||||||
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT observation group=%016llx owner=%016llx socket=%u generation=%016llx round=%016llx observed=%s via=%s",
|
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT observation group=%016llx owner=%016llx socket=%u generation=%016llx round=%016llx observed=%s via=%s",
|
||||||
(unsigned long long)group_id, (unsigned long long)owner, tag[8], (unsigned long long)generation, (unsigned long long)round,
|
(unsigned long long)group_id, (unsigned long long)owner, tag[8], (unsigned long long)generation, (unsigned long long)round,
|
||||||
sockaddr_storage_to_str(source).str, socket->name);
|
sockaddr_storage_to_str(source).str, socket->name);
|
||||||
if (length == NAT_QUERY_TAG_SIZE) {
|
|
||||||
struct nat_wire report = {.command = NAT_OBSERVATION, .owner_socket = tag[8], .step = tag[ROUTE_OBSERVE_TAG_SIZE]};
|
|
||||||
write64(report.owner, owner); write64(report.generation, generation); write64(report.round, round);
|
|
||||||
write64(report.token, read64(tag + ROUTE_OBSERVE_TAG_SIZE + 1));
|
|
||||||
write64(report.observer, detector->instance->node_id); write64(report.helper, detector->instance->node_id);
|
|
||||||
if (route_ping_addr_encode(report.target, source) || send_control(detector, group_id, owner, &report))
|
|
||||||
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT routed observation delivery failed group=%016llx owner=%016llx round=%016llx",
|
|
||||||
(unsigned long long)group_id, (unsigned long long)owner, (unsigned long long)round);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clear_profile(struct nat_socket *state, uint8_t reason) {
|
static void clear_profile(struct nat_socket *state, uint8_t reason) {
|
||||||
@@ -669,7 +675,8 @@ static void owner_heartbeat_cb(int success, uint16_t rtt, void *arg, uint64_t no
|
|||||||
struct nat_socket *state = arg; state->heartbeat_active = 0;
|
struct nat_socket *state = arg; state->heartbeat_active = 0;
|
||||||
if (!state->phase) return;
|
if (!state->phase) return;
|
||||||
struct sockaddr_storage observed;
|
struct sockaddr_storage observed;
|
||||||
if (success && !route_ping_observation(data, length, state->socket, state->group_id, state->round, &observed)) {
|
if (success && !route_ping_observation(data, length, state->socket, state->group_id, state->round, &observed) &&
|
||||||
|
public_address(state->detector, &observed)) {
|
||||||
if (state->baseline.ss_family && !route_ping_addr_equal(&state->baseline, &observed, 1)) {
|
if (state->baseline.ss_family && !route_ping_addr_equal(&state->baseline, &observed, 1)) {
|
||||||
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT anchor mapping changed socket=%s round=%016llx previous=%s current=%s",
|
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT anchor mapping changed socket=%s round=%016llx previous=%s current=%s",
|
||||||
state->socket->name, (unsigned long long)state->round,
|
state->socket->name, (unsigned long long)state->round,
|
||||||
@@ -697,6 +704,11 @@ static void owner_ping_cb(int success, uint16_t rtt, void *arg, uint64_t nonce,
|
|||||||
if (++state->retries < NAT_PROBE_COUNT) { state->due = get_time_tb() + 1000; return; }
|
if (++state->retries < NAT_PROBE_COUNT) { state->due = get_time_tb() + 1000; return; }
|
||||||
owner_finish(state, success ? NAT_REASON_INVALID_OBSERVATION : NAT_REASON_TIMEOUT); return;
|
owner_finish(state, success ? NAT_REASON_INVALID_OBSERVATION : NAT_REASON_TIMEOUT); return;
|
||||||
}
|
}
|
||||||
|
if (!public_address(state->detector, &observed)) {
|
||||||
|
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT observation unusable socket=%s phase=%u round=%016llx observed=%s reason=non_public_source",
|
||||||
|
state->socket->name, state->phase, (unsigned long long)state->round, sockaddr_storage_to_str(&observed).str);
|
||||||
|
owner_finish(state, NAT_REASON_INVALID_OBSERVATION); return;
|
||||||
|
}
|
||||||
state->retries = 0;
|
state->retries = 0;
|
||||||
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT own observation socket=%s phase=%u round=%016llx observed=%s",
|
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT own observation socket=%s phase=%u round=%016llx observed=%s",
|
||||||
state->socket->name, state->phase, (unsigned long long)state->round, sockaddr_storage_to_str(&observed).str);
|
state->socket->name, state->phase, (unsigned long long)state->round, sockaddr_storage_to_str(&observed).str);
|
||||||
@@ -757,11 +769,20 @@ void nat_detection_receive_ingress(struct NAT_DETECTION *detector, struct ETCP_S
|
|||||||
if (!step || memcmp(tag, expected, sizeof(expected)) || tag[ROUTE_OBSERVE_TAG_SIZE] != step ||
|
if (!step || memcmp(tag, expected, sizeof(expected)) || tag[ROUTE_OBSERVE_TAG_SIZE] != step ||
|
||||||
read64(tag + ROUTE_OBSERVE_TAG_SIZE + 1) != state->token ||
|
read64(tag + ROUTE_OBSERVE_TAG_SIZE + 1) != state->token ||
|
||||||
sc_derive_node_id_from_pubkey(peer_key) != state->helper || memcmp(peer_key, state->helper_key, 32) ||
|
sc_derive_node_id_from_pubkey(peer_key) != state->helper || memcmp(peer_key, state->helper_key, 32) ||
|
||||||
source->ss_family != state->local.ss_family || !public_address(detector, source) ||
|
source->ss_family != state->local.ss_family) {
|
||||||
((step == NAT_STEP_POST || state->pre_received) && !route_ping_addr_equal(source, &state->helper_address, 1))) {
|
|
||||||
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT ingress correlation/source mismatch socket=%s phase=%u round=%016llx source=%s",
|
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT ingress correlation/source mismatch socket=%s phase=%u round=%016llx source=%s",
|
||||||
socket->name, state->phase, (unsigned long long)state->round, sockaddr_storage_to_str(source).str); return;
|
socket->name, state->phase, (unsigned long long)state->round, sockaddr_storage_to_str(source).str); return;
|
||||||
}
|
}
|
||||||
|
if (!public_address(detector, source)) {
|
||||||
|
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT helper rejected by receiver socket=%s round=%016llx helper=%016llx source=%s reason=non_public_source",
|
||||||
|
socket->name, (unsigned long long)state->round, (unsigned long long)state->helper, sockaddr_storage_to_str(source).str);
|
||||||
|
owner_finish(state, NAT_REASON_NO_HELPER); return;
|
||||||
|
}
|
||||||
|
if ((step == NAT_STEP_POST || state->pre_received) && !route_ping_addr_equal(source, &state->helper_address, 1)) {
|
||||||
|
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT ingress endpoint mismatch socket=%s phase=%u round=%016llx source=%s expected=%s",
|
||||||
|
socket->name, state->phase, (unsigned long long)state->round,
|
||||||
|
sockaddr_storage_to_str(source).str, sockaddr_storage_to_str(&state->helper_address).str); return;
|
||||||
|
}
|
||||||
if (route_ping_addr_equal(source, &state->observer_address, 0) || route_ping_addr_equal(source, &state->baseline, 0)) {
|
if (route_ping_addr_equal(source, &state->observer_address, 0) || route_ping_addr_equal(source, &state->baseline, 0)) {
|
||||||
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT helper rejected by receiver socket=%s round=%016llx helper=%016llx source=%s reason=%s",
|
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT helper rejected by receiver socket=%s round=%016llx helper=%016llx source=%s reason=%s",
|
||||||
socket->name, (unsigned long long)state->round, (unsigned long long)state->helper,
|
socket->name, (unsigned long long)state->round, (unsigned long long)state->helper,
|
||||||
@@ -859,7 +880,7 @@ static void helper_ping_cb(int success, uint16_t rtt, void *arg, uint64_t nonce,
|
|||||||
struct ETCP_SOCKET *socket = find_socket(probe->detector, probe->io_socket, probe->io_generation);
|
struct ETCP_SOCKET *socket = find_socket(probe->detector, probe->io_socket, probe->io_generation);
|
||||||
struct sockaddr_storage mapped;
|
struct sockaddr_storage mapped;
|
||||||
if (success && socket && !route_ping_observation(data, length, socket, probe->group_id,
|
if (success && socket && !route_ping_observation(data, length, socket, probe->group_id,
|
||||||
read64(probe->message.round), &mapped)) {
|
read64(probe->message.round), &mapped) && public_address(probe->detector, &mapped)) {
|
||||||
if (route_ping_addr_encode(probe->message.primary, &mapped)) { helper_finish(probe, NAT_REASON_INVALID_OBSERVATION); return; }
|
if (route_ping_addr_encode(probe->message.primary, &mapped)) { helper_finish(probe, NAT_REASON_INVALID_OBSERVATION); return; }
|
||||||
probe->message.received++;
|
probe->message.received++;
|
||||||
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT helper endpoint measured group=%016llx owner=%016llx step=%u socket=%s mapped=%s fallback=%u",
|
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT helper endpoint measured group=%016llx owner=%016llx step=%u socket=%s mapped=%s fallback=%u",
|
||||||
@@ -1089,6 +1110,7 @@ static void receive_observation(struct NAT_DETECTION *detector, uint64_t group,
|
|||||||
probe->io_generation != socket->generation || read64(probe->message.round) != read64(message->round) ||
|
probe->io_generation != socket->generation || read64(probe->message.round) != read64(message->round) ||
|
||||||
read64(probe->message.token) != read64(message->token) || read64(probe->message.observer) != sender) continue;
|
read64(probe->message.token) != read64(message->token) || read64(probe->message.observer) != sender) continue;
|
||||||
etcp_cancel_pings(detector->instance, helper_ping_cb, probe);
|
etcp_cancel_pings(detector->instance, helper_ping_cb, probe);
|
||||||
|
if (message->reason) { helper_finish(probe, message->reason); return; }
|
||||||
helper_ping_cb(1, 0, probe, 0, data, sizeof(data)); return;
|
helper_ping_cb(1, 0, probe, 0, data, sizeof(data)); return;
|
||||||
}
|
}
|
||||||
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT stale helper observation report ignored"); return;
|
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT stale helper observation report ignored"); return;
|
||||||
@@ -1101,6 +1123,7 @@ static void receive_observation(struct NAT_DETECTION *detector, uint64_t group,
|
|||||||
(unsigned long long)group, (unsigned long long)sender, message->step); return;
|
(unsigned long long)group, (unsigned long long)sender, message->step); return;
|
||||||
}
|
}
|
||||||
etcp_cancel_pings(detector->instance, owner_ping_cb, state);
|
etcp_cancel_pings(detector->instance, owner_ping_cb, state);
|
||||||
|
if (message->reason) { owner_finish(state, message->reason); return; }
|
||||||
owner_ping_cb(1, 0, state, 0, data, sizeof(data));
|
owner_ping_cb(1, 0, state, 0, data, sizeof(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1514,6 +1537,10 @@ void nat_detection_link_ready(struct NAT_DETECTION *detector, struct ETCP_LINK *
|
|||||||
void nat_detection_observed_by_peer(struct NAT_DETECTION *detector, struct ETCP_LINK *link,
|
void nat_detection_observed_by_peer(struct NAT_DETECTION *detector, struct ETCP_LINK *link,
|
||||||
const struct sockaddr_storage *observed) {
|
const struct sockaddr_storage *observed) {
|
||||||
if (!detector || !link || !link->conn || link->is_tcp || link->conn->socks_udp || !observed->ss_family) return;
|
if (!detector || !link || !link->conn || link->is_tcp || link->conn->socks_udp || !observed->ss_family) return;
|
||||||
|
if (!public_address(detector, observed)) {
|
||||||
|
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT ignores LAN observation socket=%s peer=%016llx observed=%s",
|
||||||
|
link->conn->name, (unsigned long long)link->etcp->peer_node_id, sockaddr_storage_to_str(observed).str); return;
|
||||||
|
}
|
||||||
struct nat_socket *state = socket_state(detector, link->conn, 1);
|
struct nat_socket *state = socket_state(detector, link->conn, 1);
|
||||||
if (!state) return;
|
if (!state) return;
|
||||||
if ((state->measured_profile.mapping == ADDRESS_MAP_UNCHANGED &&
|
if ((state->measured_profile.mapping == ADDRESS_MAP_UNCHANGED &&
|
||||||
|
|||||||
+2
-1
@@ -450,7 +450,8 @@ check_PROGRAMS += test_nat_detection_faults
|
|||||||
test_nat_detection_faults_SOURCES = test_nat_detection_faults.c $(test_clock_sources)
|
test_nat_detection_faults_SOURCES = test_nat_detection_faults.c $(test_clock_sources)
|
||||||
test_nat_detection_faults_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
|
test_nat_detection_faults_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
|
||||||
test_nat_detection_faults_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
test_nat_detection_faults_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||||
test_nat_detection_faults_LDFLAGS = $(test_clock_ldflags) -Wl,--wrap=socket_sendto -Wl,--wrap=nat_detection_record_observation
|
test_nat_detection_faults_LDFLAGS = $(test_clock_ldflags) -Wl,--wrap=socket_sendto \
|
||||||
|
-Wl,--wrap=nat_detection_record_observation -Wl,--wrap=nat_detection_receive_ingress
|
||||||
endif
|
endif
|
||||||
|
|
||||||
test_stun_nat_SOURCES = test_stun_nat.c $(test_clock_sources)
|
test_stun_nat_SOURCES = test_stun_nat.c $(test_clock_sources)
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
#include "test_clock.h"
|
#include "test_clock.h"
|
||||||
|
|
||||||
enum fault_mode { FAULT_NONE, FAULT_NO_PONG, FAULT_FILTERED, FAULT_LATE_PRE, FAULT_CONTACT,
|
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 {
|
static struct {
|
||||||
struct nat_fixture *fixture;
|
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);
|
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 *,
|
void __real_nat_detection_record_observation(struct NAT_DETECTION *, struct ETCP_SOCKET *, const uint8_t *,
|
||||||
const struct sockaddr_storage *, const uint8_t *, size_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) {
|
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;
|
(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);
|
int to_owner = route_ping_addr_equal(&destination, &fault.socket->local_addr, 1);
|
||||||
if (to_owner && source->instance == fault.alias && plain[3] == ETCP_PING &&
|
if (to_owner && source->instance == fault.alias && plain[3] == ETCP_PING &&
|
||||||
(plain[4] & ETCP_PING_FLAG_ADDRESS_CHECK)) {
|
(plain[4] & ETCP_PING_FLAG_ADDRESS_CHECK)) {
|
||||||
|
if (fault.mode == FAULT_LOCAL_HELPER_MAPPING) { fault.dropped++; return length; }
|
||||||
fault.altered++;
|
fault.altered++;
|
||||||
return __real_socket_sendto(fault.alias_source->fd, packet, length, target, target_length);
|
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,
|
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) {
|
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) ||
|
if (!fault.fixture || socket->instance != fault.helper || memcmp(key, fault.owner->my_keys.public_key, 32) ||
|
||||||
length != ROUTE_OBSERVE_TAG_SIZE + 9) {
|
length != ROUTE_OBSERVE_TAG_SIZE + 9) {
|
||||||
__real_nat_detection_record_observation(detector, socket, key, source, tag, length); return;
|
__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);
|
__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) {
|
static void run_case(enum fault_mode mode, uint8_t mapping, uint8_t inbound) {
|
||||||
struct nat_fixture fixture; nat_fixture_init(&fixture);
|
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 *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_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_LATE_PRE || mode == FAULT_CONTACT) assert(fault.injected);
|
||||||
if (mode == FAULT_DEPENDENT || mode == FAULT_UNSTABLE) assert(fault.altered);
|
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);
|
fault.fixture = NULL; nat_fixture_cleanup(&fixture);
|
||||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS NAT fault mode=%d", mode);
|
DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS NAT fault mode=%d", mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Разные ключи не означают разные IP: неподходящего помощника отклоняет получатель. */
|
/* Разные ключи не означают разные 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 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);
|
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)) {
|
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");
|
struct ETCP_SOCKET *socket = test_etcp_bound_socket(owner, "udp");
|
||||||
memset(&fault, 0, sizeof(fault)); fault.fixture = &fixture; fault.owner = owner; fault.socket = socket;
|
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.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;
|
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;
|
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));
|
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",
|
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,
|
alias_ip, (unsigned long long)status.helper, (unsigned long long)helper->node_id,
|
||||||
status.helper_reselections, fault.altered);
|
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);
|
fault.fixture = NULL; nat_fixture_cleanup(&fixture);
|
||||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS NAT receiver rejects helper source=%s", alias_ip);
|
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 работают. */
|
/* У наблюдателя первый UDP listener недоступен, но управление по TCP и второй IP работают. */
|
||||||
static void run_endpoint_retry(void) {
|
static void run_endpoint_retry(void) {
|
||||||
struct nat_fixture fixture; nat_fixture_init(&fixture);
|
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_NO_REPORT, ADDRESS_MAP_UNKNOWN, ADDRESS_IN_UNKNOWN);
|
||||||
run_case(FAULT_INGRESS_BLOCKED, ADDRESS_MAP_UNCHANGED, 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_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;
|
test_clock_finish(); return 0;
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user