From 5eb3526186add9c79defdccb917cb4791603d818 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 17 Oct 2026 19:46:02 +0200 Subject: [PATCH] etcp: exchange observed IPs on established TCP links --- src/transport_layer/etcp_connections.c | 51 ++++++++++++++++++++++++++ src/transport_layer/etcp_connections.h | 1 + 2 files changed, 52 insertions(+) diff --git a/src/transport_layer/etcp_connections.c b/src/transport_layer/etcp_connections.c index deec78ca..0d9065d8 100644 --- a/src/transport_layer/etcp_connections.c +++ b/src/transport_layer/etcp_connections.c @@ -1262,6 +1262,29 @@ void etcp_link_close(struct ETCP_LINK* link) { // ====== TCP link helpers ====== +/* По готовому STCP сообщает пиру его фактический IP; исходящий порт не является портом listener. */ +static void tcp_send_observed_ip(struct ETCP_LINK *link) { + struct UTUN_INSTANCE *inst = link->etcp->instance; + if (!link->tcp_link || (!link->is_server && inst->config && inst->config->global.socks_enabled)) return; + const struct sockaddr_storage *peer = stcp_link_get_peer_addr(link->tcp_link); + if (!peer) { DEBUG_WARN(DEBUG_CATEGORY_NAT, "TCP IP report cannot read peer socket=%s", link->etcp->log_name); return; } + struct ETCP_DGRAM *pkt = memory_pool_alloc(inst->pkt_pool); + if (!pkt) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "TCP IP report allocation failed peer=%s", link->etcp->log_name); return; } + pkt->link = link; pkt->data_len = 18; pkt->noencrypt_len = 0; + memset(pkt->data, 0, pkt->data_len); pkt->data[0] = ETCP_TCP_ADDRESS; + if (peer->ss_family == AF_INET) { + pkt->data[1] = 4; memcpy(pkt->data + 2, &((const struct sockaddr_in *)peer)->sin_addr, 4); + } else if (peer->ss_family == AF_INET6) { + pkt->data[1] = 6; memcpy(pkt->data + 2, &((const struct sockaddr_in6 *)peer)->sin6_addr, 16); + } else { + DEBUG_WARN(DEBUG_CATEGORY_NAT, "TCP IP report unsupported family=%d", peer->ss_family); + memory_pool_free(inst->pkt_pool, pkt); return; + } + DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "TCP IP report peer=%s observed=%s", link->etcp->log_name, sockaddr_storage_to_str(peer).str); + if (etcp_encrypt_send(pkt) < 0) DEBUG_WARN(DEBUG_CATEGORY_NAT, "TCP IP report send failed"); + memory_pool_free(inst->pkt_pool, pkt); +} + // Поднять TCP-линк в ready: синхронизация reset-эпохи (gop/rid), keepalive, ed25519, // mark initialized + link_status=1, уведомить коннект и loadbalancer. void etcp_link_enter_ready_tcp(struct ETCP_LINK *link) { @@ -1297,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); + tcp_send_observed_ip(link); } // Повторная попытка STCP-подключения с экспоненциальным backoff (до 30 с). @@ -2636,6 +2660,33 @@ int etcp_packet_decrypted(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt, uint8_t pkt_code = pkt->data[0]; + if (pkt_code == ETCP_TCP_ADDRESS) { + struct UTUN_INSTANCE *inst = link->etcp->instance; + static const uint8_t zero[12]; + if (!link->is_tcp || pkt->data_len != 18 || + (pkt->data[1] != 4 && pkt->data[1] != 6) || + (pkt->data[1] == 4 && memcmp(pkt->data + 6, zero, sizeof(zero)))) { + DEBUG_WARN(DEBUG_CATEGORY_NAT, "TCP IP report invalid transport/format peer=%s size=%u", + link->etcp->log_name, pkt->data_len); + memory_pool_free(inst->pkt_pool, pkt); return 0; + } + if (!link->is_server && inst->config && inst->config->global.socks_enabled) { + DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "TCP IP report ignored through SOCKS peer=%s", link->etcp->log_name); + memory_pool_free(inst->pkt_pool, pkt); return 0; + } + struct sockaddr_storage observed = {0}; + if (pkt->data[1] == 4) { + observed.ss_family = AF_INET; memcpy(&((struct sockaddr_in *)&observed)->sin_addr, pkt->data + 2, 4); + } else { + observed.ss_family = AF_INET6; memcpy(&((struct sockaddr_in6 *)&observed)->sin6_addr, pkt->data + 2, 16); + } + link->local_as_seen_by_peer = observed; + const struct sockaddr_storage *local = stcp_link_get_local_addr(link->tcp_link); + if (local && inst->nat_det && link->conn) nat_detection_tcp_observed(inst->nat_det, link, local, &observed); + else if (!local) DEBUG_WARN(DEBUG_CATEGORY_NAT, "TCP IP report cannot read local socket peer=%s", link->etcp->log_name); + memory_pool_free(inst->pkt_pool, pkt); return 0; + } + #ifdef UTUN_HAVE_STANDBY { char who[256]; diff --git a/src/transport_layer/etcp_connections.h b/src/transport_layer/etcp_connections.h index 04f5a248..39f8cb83 100644 --- a/src/transport_layer/etcp_connections.h +++ b/src/transport_layer/etcp_connections.h @@ -44,6 +44,7 @@ extern "C" { #define ETCP_PING 0x06 #define ETCP_PONG 0x07 #define ETCP_KEEPALIVE 0x08 +#define ETCP_TCP_ADDRESS 0x0d // STCP control: code + family(4/6) + observed IP(16), без порта #define ETCP_PING_FLAG_WANT_RTT 0x01 // в PONG: хочу получить RTT от пингера #define ETCP_PING_FLAG_SEND_RTT 0x02 // в PING: несу RTT для получателя