Browse Source

Verify STUN cache expiry and preserve partial ETCP measurement priority

master
evgeny 23 hours ago
parent
commit
3735b78328
  1. 13
      src/transport_layer/stun_nat.c
  2. 30
      tests/test_stun_nat.c

13
src/transport_layer/stun_nat.c

@ -727,7 +727,7 @@ static void scan_sources(struct STUN_NAT* mod) {
while (p && !key_equal(&p->source.key, &source.key)) p = p->next;
if (p) {
if (!p->seen) {
p->changed = !policy_equal(&p->source, &source);
p->changed = !policy_equal(&p->source, &source) || (!p->generation && get_time_tb() >= p->due);
p->source = source;
} else if (!policy_equal(&p->source, &source)) p->scan_conflict = 1;
p->seen = 1; p->members++;
@ -756,7 +756,10 @@ static void scan_sources(struct STUN_NAT* mod) {
if (p->changed || p->conflict != p->scan_conflict) {
close_probe_io(p); memset(&p->cached, 0, sizeof(p->cached));
p->conflict = p->scan_conflict;
if (random_id(&p->generation)) { p->generation = 0; p->due = get_time_tb() + STUN_RETRY_TB; }
if (random_id(&p->generation)) {
p->generation = 0; memset(&p->result, 0, sizeof(p->result)); p->result.key = p->source.key;
p->result.status = STUN_NAT_FAILED; p->result.mapping_reason = STUN_REASON_IO; finish_probe(p);
}
else restart_probe(p);
DEBUG_INFO(DEBUG_CATEGORY_NAT, "STUN group source=%s IP=%s netif=%u members=%u generation=%016llx conflict=%d",
p->source.name, ip_to_str(&p->source.addr.sin_addr, AF_INET).str, p->source.netif_index,
@ -812,8 +815,12 @@ void stun_nat_recheck(struct UTUN_INSTANCE* inst) {
DEBUG_INFO(DEBUG_CATEGORY_NAT, "STUN rechecking interfaces using fresh test ports");
scan_sources(mod);
for (struct stun_probe* p = mod->probes; p; p = p->next) {
close_probe_io(p);
memset(&p->cached, 0, sizeof(p->cached));
if (random_id(&p->generation)) continue;
if (random_id(&p->generation)) {
p->generation = 0; memset(&p->result, 0, sizeof(p->result)); p->result.key = p->source.key;
p->result.status = STUN_NAT_FAILED; p->result.mapping_reason = STUN_REASON_IO; finish_probe(p); continue;
}
restart_probe(p);
}
}

30
tests/test_stun_nat.c

@ -325,11 +325,35 @@ static void test_groups(void) {
assert(!stun_nat_get_socket_result(&f.inst, &duplicate, &second));
assert(second.mapping_reason == STUN_REASON_ROUTE);
} else DEBUG_WARN(DEBUG_CATEGORY_NAT, "STUN routing conflict subtest unavailable: bind-to-device denied");
socket_close_wrapper(duplicate.fd); duplicate = extra_socket(&f, "another-ip", "127.0.0.5");
test_clock_run_for(f.ua, 30001); end = get_time_tb() + 3000000;
do { assert(!stun_nat_get_socket_result(&f.inst, &duplicate, &second));
if (second.status != STUN_NAT_PENDING) break; test_clock_poll(f.ua, 10000);
} while (get_time_tb() < end);
assert(!stun_nat_get_socket_result(&f.inst, &f.source, &first));
assert(second.key.address[3] == 5 && first.key.address[3] == 1 && second.evidence_id != first.evidence_id);
socket_close_wrapper(duplicate.fd); f.source.next = NULL; f.original = f.source;
fixture_destroy(&f);
DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS STUN groups: duplicate ports, representative removal, separate netif, policy conflict");
}
/* Штатный refresh сохраняет прежний результат до TTL, не продлевая его возраст. */
static void test_cache(void) {
struct fixture f = {.rfc = 1, .mapping = STUN_MAP_EIM, .filtering = STUN_FILTER_EIF,
.multiple_clients = 1}; fixture_init(&f); fixture_start(&f, 0, 0, 0);
struct stun_nat_result first = wait_result(&f), current;
assert(first.status == STUN_NAT_COMPLETE);
f.drop_primary = 1;
test_clock_run_for(f.ua, 1830000);
assert(!stun_nat_get_socket_result(&f.inst, &f.source, &current));
assert(current.evidence_id == first.evidence_id && current.measured_at_us == first.measured_at_us && current.refreshing);
test_clock_run_for(f.ua, 1300000);
assert(!stun_nat_get_socket_result(&f.inst, &f.source, &current));
assert(current.evidence_id != first.evidence_id && current.mapping == STUN_MAP_UNKNOWN && current.filtering == STUN_FILTER_UNKNOWN);
fixture_destroy(&f);
DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS STUN cache: automatic refresh, preserved age, failed refresh and expiry");
}
static void wait_source(struct nat_fixture* nf, struct ETCP_SOCKET* socket, uint8_t source) {
uint64_t end = get_time_tb() + 3000000;
while (get_time_tb() < end) {
@ -382,6 +406,11 @@ static void test_fallback(void) {
while (get_time_tb() < end && next.status == STUN_NAT_PENDING);
assert(next.mapping == STUN_MAP_EIM && next.evidence_id != prior.evidence_id);
assert(socket->address_profile.source == ADDRESS_SOURCE_ETCP && socket->address_profile.evidence_id == evidence);
nat_detection_request_check_all(owner->nat_det); end = get_time_tb() + 300000;
do { test_clock_poll(nf.ua, 10000); }
while (get_time_tb() < end && socket->address_profile.evidence_id == evidence);
assert(socket->address_profile.source == ADDRESS_SOURCE_ETCP && socket->address_profile.evidence_id != evidence);
assert(socket->address_profile.mapping == ADDRESS_MAP_UNCHANGED && socket->address_profile.inbound == ADDRESS_IN_UNKNOWN);
for (unsigned i = 0; i < 6; i++) {
assert(!uasync_remove_socket_t(nf.ua, server.endpoints[i].fd)); socket_close_wrapper(server.endpoints[i].fd);
}
@ -397,6 +426,7 @@ int main(void) {
test_clock_init(10000);
test_config();
test_groups();
test_cache();
struct fixture ordinary = { .mapping = STUN_MAP_EIM };
run_case("ordinary equal", &ordinary, STUN_MAP_MATCH, STUN_FILTER_UNKNOWN);
struct fixture dependent = { .mapping = STUN_MAP_ADM };

Loading…
Cancel
Save