feat(nat): confirm direct open profile from accepted public connections
This commit is contained in:
1 parent
37e092672a
commit
df2f6ff300
3 files changed
+47
-7
No files matched your search
@@ -11,6 +11,7 @@
|
||||
#include "etcp_api.h"
|
||||
#include "etcp.h"
|
||||
#include "etcp_connections.h"
|
||||
#include "stcp_link.h"
|
||||
#include "route_ping.h"
|
||||
#include "topo_node.h"
|
||||
#include "topo_group.h"
|
||||
@@ -1586,7 +1587,38 @@ static void connection_status(struct ETCP_CONN *connection, int status, void *ar
|
||||
void nat_detection_link_ready(struct NAT_DETECTION *detector, struct ETCP_LINK *link) {
|
||||
if (!detector || !link || !link->conn || link->conn->probe_only || detector->stopping) return;
|
||||
struct nat_socket *state = socket_state(detector, link->conn, 1);
|
||||
if (state && !state->phase && !state->completed_tb) state->due = get_time_tb() + 2500;
|
||||
if (!state) return;
|
||||
if (!state->phase && !state->completed_tb) state->due = get_time_tb() + 2500;
|
||||
if (!link->is_server || !link->initialized || link->conn->socks_udp ||
|
||||
link->conn->type == CFG_SERVER_TYPE_PRIVATE || link->conn->type == CFG_SERVER_TYPE_LOCAL) return;
|
||||
const struct sockaddr_storage *local = link->is_tcp ? stcp_link_get_local_addr(link->tcp_link) : &state->local;
|
||||
if (!local) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT incoming connection cannot read local address socket=%s", link->conn->name); return;
|
||||
}
|
||||
if (local->ss_family != link->remote_addr.ss_family || !public_address(detector, local) ||
|
||||
!public_address(detector, &link->remote_addr) || !route_ping_addr_equal(local, &state->local, 0)) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT incoming connection ignored socket=%s protocol=%s local=%s peer=%s interface=%s"
|
||||
" reason=non_public_or_other_interface", link->conn->name, link->is_tcp ? "TCP" : "UDP",
|
||||
sockaddr_storage_to_str(local).str, sockaddr_storage_to_str(&link->remote_addr).str,
|
||||
sockaddr_storage_to_str(&state->local).str); return;
|
||||
}
|
||||
if (state->measured_profile.mapping == ADDRESS_MAP_UNCHANGED && state->measured_profile.inbound == ADDRESS_IN_OPEN &&
|
||||
!state->phase && !state->cycling && !state->forced) return;
|
||||
state->measured_profile = (struct address_profile){ADDRESS_MAP_UNCHANGED, ADDRESS_IN_OPEN, ADDRESS_SOURCE_ETCP};
|
||||
state->measured_address = state->local;
|
||||
memset(&state->observed_address, 0, sizeof(state->observed_address));
|
||||
if (link->is_tcp) {
|
||||
state->observed_address = *local;
|
||||
if (local->ss_family == AF_INET) ((struct sockaddr_in *)&state->observed_address)->sin_port = 0;
|
||||
else ((struct sockaddr_in6 *)&state->observed_address)->sin6_port = 0;
|
||||
}
|
||||
state->completed_tb = get_time_tb(); state->forced = 0;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT incoming connection confirms DIRECT/OPEN socket=%s id=%u generation=%016llx"
|
||||
" protocol=%s local=%s peer=%s evidence=accepted_connection", link->conn->name, link->conn->sock_id,
|
||||
(unsigned long long)link->conn->generation, link->is_tcp ? "TCP" : "UDP",
|
||||
sockaddr_storage_to_str(local).str, sockaddr_storage_to_str(&link->remote_addr).str);
|
||||
owner_finish(state, NAT_REASON_NONE);
|
||||
if (link->is_tcp) state->due = UINT64_MAX;
|
||||
}
|
||||
|
||||
/* Одно TCP-наблюдение сравнивает только IP; нулевой порт не обещает доступный listener. */
|
||||
@@ -1610,11 +1642,14 @@ void nat_detection_tcp_observed(struct NAT_DETECTION *detector, struct ETCP_LINK
|
||||
else ((struct sockaddr_in6 *)&address)->sin6_port = 0;
|
||||
if (state->measured_profile.source == ADDRESS_SOURCE_ETCP && state->measured_profile.mapping == mapping &&
|
||||
route_ping_addr_equal(&state->observed_address, &address, 1)) return;
|
||||
state->measured_profile = (struct address_profile){.mapping = mapping, .source = ADDRESS_SOURCE_ETCP};
|
||||
uint8_t inbound = mapping == ADDRESS_MAP_UNCHANGED && state->measured_profile.mapping == mapping &&
|
||||
route_ping_addr_equal(&state->observed_address, &address, 0) ?
|
||||
state->measured_profile.inbound : ADDRESS_IN_UNKNOWN;
|
||||
state->measured_profile = (struct address_profile){.mapping = mapping, .inbound = inbound, .source = ADDRESS_SOURCE_ETCP};
|
||||
state->observed_address = address; state->completed_tb = get_time_tb(); state->due = UINT64_MAX; state->reason = NAT_REASON_NONE;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "TCP NAT presence socket=%s local=%s observed=%s NAT=%s type=UNKNOWN inbound=UNKNOWN",
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "TCP NAT presence socket=%s local=%s observed=%s NAT=%s type=UNKNOWN inbound=%s",
|
||||
link->conn->name, sockaddr_storage_to_str(local).str, sockaddr_storage_to_str(&address).str,
|
||||
mapping == ADDRESS_MAP_UNCHANGED ? "no" : "yes");
|
||||
mapping == ADDRESS_MAP_UNCHANGED ? "no" : "yes", address_inbound_name(inbound));
|
||||
select_profile(state);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
* Результат — address_profile с двумя независимыми свойствами:
|
||||
* mapping: UNCHANGED — адрес не меняется; EIM — внешний IP:порт одинаков для разных IP назначения;
|
||||
* DEPENDENT (strict) — внешний IP:порт зависит от IP назначения.
|
||||
* inbound: OPEN — пакеты от незнакомого IP проходят; CONTACT — проходят после исходящего контакта (оценка filtered).
|
||||
* inbound: OPEN — подтверждён вход; CONTACT — пакеты проходят после исходящего контакта (оценка filtered).
|
||||
* Принятое UDP/TCP-подключение с публичными локальным и удалённым IP сразу даёт UNCHANGED+OPEN (direct/open).
|
||||
* Для этого правила прежний исходящий контакт с IP пира не препятствие; LAN и SOCKS его не подтверждают.
|
||||
* UNKNOWN в любом свойстве означает, что данных для его определения пока недостаточно.
|
||||
* Одно соединение может подтвердить NAT-адрес: он сохраняется в socket->nat_addr с типом UNKNOWN
|
||||
* и публикуется как NAT-кандидат. Это не адрес интерфейса и не подтверждение открытого входа.
|
||||
* Для UDP сочетания EIM+OPEN и EIM+CONTACT означают eim open и eim filtered.
|
||||
* Для TCP непубличный локальный IP сразу означает NAT; публичный IP сравнивается с наблюдением одного пира.
|
||||
* TCP не определяет тип NAT/входящие: сохраняет внешний IP с портом 0, непригодным для подключения.
|
||||
* TCP не запускает активных проб входа: OPEN даёт только принятое подключение; внешний IP сохраняется с портом 0.
|
||||
* EIM+UNKNOWN отображается как EIM?: внешний адрес совпал, незнакомый вход не удалось проверить.
|
||||
* Если своего измерения mapping нет, совпадение у двух STUN-серверов на том же IP/интерфейсе даёт предварительный EIM?.
|
||||
* Его source=STUN, inbound=UNKNOWN; внешний порт берётся только из наблюдения рабочего сокета, а не из STUN.
|
||||
@@ -95,7 +97,9 @@ int nat_detection_ping_allowed(struct NAT_DETECTION *detector, struct ETCP_SOCKE
|
||||
const uint8_t peer_key[32], const struct sockaddr_storage *source,
|
||||
uint8_t flags, const uint8_t *tag, size_t length);
|
||||
|
||||
/* Сообщить о готовности link; при отсутствии результата запланировать измерение его локального сокета. */
|
||||
/* Сообщить о готовности link после UDP INIT или STCP handshake. Принятый вход с двумя публичными IP
|
||||
* подтверждает direct/open у принимающего сокета; иначе при отсутствии результата планируется измерение.
|
||||
* Для TCP фактический локальный IP должен совпадать с IP интерфейса этого сокета. */
|
||||
void nat_detection_link_ready(struct NAT_DETECTION *detector, struct ETCP_LINK *link);
|
||||
|
||||
/* Сообщить об утрате/перезапуске connection; запланировать пересмотр маршрутов и отмену зависимых проверок при их потере. */
|
||||
|
||||
@@ -1320,6 +1320,7 @@ void etcp_link_enter_ready_tcp(struct ETCP_LINK *link) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] TCP link %d UP (mtu=%d init=%d up=%d tcp_links=%d)",
|
||||
etcp->log_name, link->local_link_id, link->mtu,
|
||||
etcp->initialized, etcp->links_up, etcp->tcp_link_count);
|
||||
if (etcp->instance->nat_det) nat_detection_link_ready(etcp->instance->nat_det, link);
|
||||
tcp_send_observed_ip(link);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user