Browse Source

Prefer untouched helper IPs and exercise NAT through real groups

master
evgeny 1 week ago
parent
commit
0ee431b490
  1. 12
      src/routing_layer/nat_detection.c
  2. 20
      tests/nat_fixture.h
  3. 13
      tests/test_route_ping.c

12
src/routing_layer/nat_detection.c

@ -63,7 +63,7 @@ struct nat_socket {
uint8_t contacts[NAT_MAX_CONTACTS][17];
unsigned contact_count, phase, reason, retries;
uint8_t history_loaded, history_complete, history_incomplete, filter_clean, pre_received, post_received, pre_healthy;
uint8_t helper_socket, active_ping, forced, cycling, using_fallback;
uint8_t helper_socket, active_ping, forced, cycling, using_fallback, prefer_fresh;
uint32_t rounds, helper_reselections;
};
@ -104,6 +104,7 @@ static void owner_finish(struct nat_socket *, uint8_t);
static void helper_remove(struct nat_probe *);
static int helper_send(struct nat_probe *);
static void owner_start(struct nat_socket *);
static int contacted(struct nat_socket *, const struct sockaddr_storage *);
static uint64_t read64(const uint8_t *data) {
uint64_t value = 0;
@ -231,6 +232,7 @@ static int select_pair(struct nat_socket *state) {
const struct TOPO_NODE *cn = group_node(detector, group->group_id, helper);
struct sockaddr_storage ca = {0}; uint8_t cs = 0;
if (!state->socket->is_tcp && !node_udp_endpoint(detector, cn, state->local.ss_family, 1, &ca, &cs)) continue;
if (!state->socket->is_tcp && state->prefer_fresh && !state->using_fallback && contacted(state, &ca)) continue;
struct nat_pair candidate = {group->group_id, observer, helper};
if (state->using_fallback) {
if (memcmp(&candidate, &state->fallback, sizeof(candidate))) continue;
@ -242,6 +244,9 @@ static int select_pair(struct nat_socket *state) {
}
}
if (!best.group) {
if (state->prefer_fresh && !state->using_fallback) {
state->prefer_fresh = 0; state->cursor = (struct nat_pair){0}; return select_pair(state);
}
if (!state->using_fallback && state->fallback.group) { state->using_fallback = 1; return select_pair(state); }
state->reason = observers ? NAT_REASON_NO_HELPER : NAT_REASON_NO_OBSERVER;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT candidate cycle exhausted socket=%s groups=%u observers=%u reason=%s",
@ -1051,11 +1056,12 @@ static void owner_start(struct nat_socket *state) {
state->socket->name, sockaddr_storage_to_str(&state->local).str); return;
}
if (!state->cycling) {
state->cycling = 1; state->cursor = (struct nat_pair){0}; state->fallback = (struct nat_pair){0}; state->using_fallback = 0;
state->cycling = 1; state->cursor = (struct nat_pair){0}; state->fallback = (struct nat_pair){0};
state->using_fallback = 0; state->prefer_fresh = 1;
}
history_load(state);
if (select_pair(state)) { owner_finish(state, state->reason); return; }
if (random_id(&state->round)) { owner_finish(state, NAT_REASON_SEND_ERROR); return; }
history_load(state);
state->rounds++;
memset(&state->helper_seen, 0, sizeof(state->helper_seen)); memset(&state->alternate_seen, 0, sizeof(state->alternate_seen));
state->retries = 0; state->pre_received = 0; state->post_received = 0; state->filter_clean = 0; state->pre_healthy = 0;

20
tests/nat_fixture.h

@ -44,11 +44,11 @@ static inline struct UTUN_INSTANCE *nat_fixture_node(struct nat_fixture *fixture
struct UTUN_INSTANCE *node = utun_instance_create_from_str(fixture->ua, config);
assert(node && fixture->node_count < 8 && utun_core_start(node) == 0);
nat_detection_set_allow_local(node->nat_det, 1);
assert(topo_groups_create_group(node->topo_groups, TOPO_GROUP_UTUN, TOPO_GROUP_TYPE_UTUN, ""));
fixture->nodes[fixture->node_count++] = node; return node;
}
/* Без групповой автосвязности: A контактирует только с явно указанным B.
* NCD переносит настоящую подписанную запись, handshake идёт по UDP. */
/* Без автосвязности: заданный UDP-транспорт присоединяется к группе; остальные узлы доступны через BGP. */
static inline struct ETCP_CONN *nat_fixture_connect(struct nat_fixture *fixture, struct UTUN_INSTANCE *from,
const char *source, struct UTUN_INSTANCE *to) {
assert(fixture->handle_count < 24);
@ -60,12 +60,20 @@ static inline struct ETCP_CONN *nat_fixture_connect(struct nat_fixture *fixture,
struct ETCP_CONN *connection = NULL;
while (get_time_tb() < end) {
connection = node_conn_direct_get_conn(*handle);
if (connection && connection->links_up) return connection;
if (connection && connection->links_up) break;
uasync_poll(fixture->ua, 10);
}
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "fixture connection timed out from=%016llx to=%016llx",
(unsigned long long)from->node_id, (unsigned long long)to->node_id);
abort();
assert(connection && connection->links_up);
struct TOPO_GROUP *group = topo_groups_find(from->topo_groups, TOPO_GROUP_UTUN);
struct TOPO_GROUP *remote = topo_groups_find(to->topo_groups, TOPO_GROUP_UTUN);
assert(group && remote && !topo_group_new_conn(group, connection));
end = get_time_tb() + 50000;
while (get_time_tb() < end) {
if (topo_group_peer_ready(group, to->node_id) && topo_group_peer_ready(remote, from->node_id)) return connection;
uasync_poll(fixture->ua, 10);
}
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "fixture group negotiation timed out from=%016llx to=%016llx",
(unsigned long long)from->node_id, (unsigned long long)to->node_id); abort();
}
static inline void nat_fixture_cleanup(struct nat_fixture *fixture) {

13
tests/test_route_ping.c

@ -65,30 +65,31 @@ int main(void) {
struct ETCP_SOCKET *peer = test_etcp_bound_socket(observer, "udp");
const uint64_t round = UINT64_C(0x0102030405060708);
struct result result = {0};
assert(route_ping_observe(socket, observer->my_keys.public_key, &peer->local_addr, round, 500, ping_done, &result) == 0);
assert(route_ping_observe(socket, observer->my_keys.public_key, &peer->local_addr, TOPO_GROUP_UTUN, round, 500, ping_done, &result) == 0);
wait_result(&fixture, &result);
struct sockaddr_storage observed;
assert(result.success && route_ping_observation(result.data, result.length, socket, round, &observed) == 0);
assert(result.success && route_ping_observation(result.data, result.length, socket, TOPO_GROUP_UTUN, round, &observed) == 0);
assert(route_ping_addr_equal(&observed, &socket->local_addr, 1));
assert(route_ping_observation(result.data, result.length, socket, round + 1, &observed) < 0);
assert(route_ping_observation(result.data, result.length, socket, TOPO_GROUP_UTUN, round + 1, &observed) < 0);
assert(route_ping_observation(result.data, result.length, socket, TOPO_GROUP_UTUN + 1, round, &observed) < 0);
assert(socket->address_profile.mapping == ADDRESS_MAP_UNKNOWN); /* один ответ не назначает NAT-тип */
memset(&result, 0, sizeof(result));
struct ETCP_SOCKET *untrusted = test_etcp_bound_socket(foreign, "udp");
assert(route_ping_observe(socket, observer->my_keys.public_key, &untrusted->local_addr,
round + 1, 300, ping_done, &result) == 0);
TOPO_GROUP_UTUN, round + 1, 300, ping_done, &result) == 0);
assert(owner->pending_pings);
send_foreign_pong(foreign, socket, owner->pending_pings->nonce);
wait_result(&fixture, &result);
assert(!result.success && !owner->pending_pings);
memset(&result, 0, sizeof(result));
assert(route_ping_observe(socket, observer->my_keys.public_key, &peer->local_addr, round + 2, 300, ping_done, &result) == 0);
assert(route_ping_observe(socket, observer->my_keys.public_key, &peer->local_addr, TOPO_GROUP_UTUN, round + 2, 300, ping_done, &result) == 0);
uint64_t generation = socket->generation; socket->generation = generation == UINT64_MAX ? generation - 1 : generation + 1;
wait_result(&fixture, &result); assert(!result.success); socket->generation = generation;
memset(&result, 0, sizeof(result));
assert(route_ping_observe(socket, observer->my_keys.public_key, &peer->local_addr, round + 3, 500, ping_done, &result) == 0);
assert(route_ping_observe(socket, observer->my_keys.public_key, &peer->local_addr, TOPO_GROUP_UTUN, round + 3, 500, ping_done, &result) == 0);
etcp_cancel_pings(owner, ping_done, &result); assert(!owner->pending_pings);
uint64_t end = get_time_tb() + 6000;
while (get_time_tb() < end) uasync_poll(fixture.ua, 10);

Loading…
Cancel
Save