From 4862c3026157786a2afb563e0ef2f06fc3898c0c Mon Sep 17 00:00:00 2001 From: Evgeny Date: Thu, 2 Jul 2026 19:30:08 +0300 Subject: [PATCH] add CFG_SERVER_TYPE_LOCAL (type=local) and UNKNOWN semantics: PRIVATE/LOCAL hidden from nodeinfo, PRIVATE blocks incoming INIT, LOCAL accepts; UNKNOWN treated as PUBLIC; self-detection of PUBLIC IP on incoming INIT; filter LOCAL from NAT checks and connectivity --- doc/etcp_protocol.txt | 2 +- src/config_parser.c | 2 ++ src/config_parser.h | 5 +++-- src/conn_mgr.c | 20 ++++++++++++-------- src/etcp_connections.c | 32 ++++++++++++++++++++++++++++++++ src/route_bgp.c | 7 ++++--- src/route_connectivity.c | 17 ++++++++++------- src/route_node.c | 11 +++++++++-- tools/etcpmon/etcpmon_gui.c | 4 +++- tools/etcpmon/etcpmon_protocol.h | 1 + 10 files changed, 77 insertions(+), 24 deletions(-) diff --git a/doc/etcp_protocol.txt b/doc/etcp_protocol.txt index c5a53e40..36cff99f 100644 --- a/doc/etcp_protocol.txt +++ b/doc/etcp_protocol.txt @@ -136,7 +136,7 @@ link_state: 0=just init, 1=handshake, 2=reconnect, 3=connected. - local_link_id: uint8_t (0-255), назначается отправителем - sock_id: uint8_t, уникальный ID сокета отправителя - only_local: uint8_t, флаг "только локальные подключения" - - type: uint8_t, тип сокета (CFG_SERVER_TYPE_PUBLIC/NAT/PRIVATE) + - type: uint8_t, тип сокета (CFG_SERVER_TYPE_UNKNOWN=0/PUBLIC=1/NAT=2/PRIVATE=3/LOCAL=4) - random_padding: случайные байты до размера handshake (защита от fingerprinting) - salt + obfuscated_pubkey: публичный ключ с обфускацией (не шифруется AES, но скрывается XOR с salt и собственным публичным ключом). Передаётся в конце пакета diff --git a/src/config_parser.c b/src/config_parser.c index 4b0bb8b0..350f2481 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -650,6 +650,8 @@ static int parse_server(const char *key, const char *value, struct CFG_SERVER *s srv->type = CFG_SERVER_TYPE_NAT; } else if (strcmp(value, "private") == 0) { srv->type = CFG_SERVER_TYPE_PRIVATE; + } else if (strcmp(value, "local") == 0) { + srv->type = CFG_SERVER_TYPE_LOCAL; } else { srv->type = CFG_SERVER_TYPE_UNKNOWN; } diff --git a/src/config_parser.h b/src/config_parser.h index 26086617..e416ff48 100644 --- a/src/config_parser.h +++ b/src/config_parser.h @@ -24,9 +24,10 @@ struct IP { }; #define CFG_SERVER_TYPE_UNKNOWN 0 -#define CFG_SERVER_TYPE_PUBLIC 1 -#define CFG_SERVER_TYPE_NAT 2 +#define CFG_SERVER_TYPE_PUBLIC 1 +#define CFG_SERVER_TYPE_NAT 2 #define CFG_SERVER_TYPE_PRIVATE 3 +#define CFG_SERVER_TYPE_LOCAL 4 #define CFG_IPV6_MODE_NONE 0 // normal explicit address #define CFG_IPV6_MODE_TEMPORARY 1 // temporary IPv6 from interface diff --git a/src/conn_mgr.c b/src/conn_mgr.c index 9c267822..857fb6fa 100644 --- a/src/conn_mgr.c +++ b/src/conn_mgr.c @@ -336,10 +336,10 @@ static void cm_idle_timer_cb(void* arg) { static int cm_nat_compatible(struct ETCP_SOCKET* our, uint8_t target_config_type, uint8_t target_nat_type) { uint8_t tt = target_nat_type >= NAT_VERIFIED_UNKNOWN ? CFG_SERVER_TYPE_PUBLIC : target_config_type; uint8_t ot = our->type; - if (ot == CFG_SERVER_TYPE_PUBLIC) return 1; + if (ot == CFG_SERVER_TYPE_PUBLIC || ot == CFG_SERVER_TYPE_UNKNOWN) return 1; if (ot == CFG_SERVER_TYPE_NAT && our->nat_type == NAT_VERIFIED_EIM && tt == CFG_SERVER_TYPE_PUBLIC) return 1; if (ot == CFG_SERVER_TYPE_NAT && our->nat_type == NAT_VERIFIED_EIM && tt == CFG_SERVER_TYPE_NAT) return 1; - if (ot == CFG_SERVER_TYPE_PRIVATE && tt == CFG_SERVER_TYPE_PRIVATE) return 2; + if ((ot == CFG_SERVER_TYPE_PRIVATE || ot == CFG_SERVER_TYPE_LOCAL) && tt == CFG_SERVER_TYPE_PRIVATE) return 2; return 0; } @@ -376,7 +376,8 @@ static int cm_has_direct_ip(struct NODEINFO_Q* nq) { if (addrs[i].type == ADDR_TYPE_REAL || addrs[i].type == ADDR_TYPE_NAT) { for (int j = 0; j < meta_count; j++) { if (metas[j].id == addrs[i].socket_id && - (metas[j].config_type == CFG_SERVER_TYPE_PUBLIC || metas[j].nat_type == NAT_VERIFIED_EIM || metas[j].nat_type == NAT_VERIFIED_DIRECT)) + (metas[j].config_type == CFG_SERVER_TYPE_PUBLIC || metas[j].config_type == CFG_SERVER_TYPE_UNKNOWN + || metas[j].nat_type == NAT_VERIFIED_EIM || metas[j].nat_type == NAT_VERIFIED_DIRECT)) return 1; } } @@ -393,7 +394,8 @@ static int cm_has_local_addr(struct NODEINFO_Q* nq) { for (int i = 0; i < addr_count; i++) { if (addrs[i].type == ADDR_TYPE_INTERFACE) { for (int j = 0; j < meta_count; j++) { - if (metas[j].id == addrs[i].socket_id && metas[j].config_type == CFG_SERVER_TYPE_PRIVATE) + if (metas[j].id == addrs[i].socket_id && + (metas[j].config_type == CFG_SERVER_TYPE_PRIVATE || metas[j].config_type == CFG_SERVER_TYPE_LOCAL)) return 1; } } @@ -424,10 +426,11 @@ static void cm_start_local_scan(struct CONN_MGR_ENTRY* entry) { for (int i = 0; i < addr_count; i++) { if (addrs[i].type != ADDR_TYPE_INTERFACE) continue; for (int j = 0; j < meta_count; j++) { - if (metas[j].id != addrs[i].socket_id || metas[j].config_type != CFG_SERVER_TYPE_PRIVATE) continue; + if (metas[j].id != addrs[i].socket_id || + (metas[j].config_type != CFG_SERVER_TYPE_PRIVATE && metas[j].config_type != CFG_SERVER_TYPE_LOCAL)) continue; struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets; while (s) { - if (s->type == CFG_SERVER_TYPE_PRIVATE && s->local_addr.ss_family == AF_INET) { + if ((s->type == CFG_SERVER_TYPE_PRIVATE || s->type == CFG_SERVER_TYPE_LOCAL) && s->local_addr.ss_family == AF_INET) { struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; @@ -589,7 +592,8 @@ static void cm_start_phase_reverse(struct CONN_MGR_ENTRY* entry) { if (addrs[i].type != ADDR_TYPE_REAL && addrs[i].type != ADDR_TYPE_NAT) continue; for (int j = 0; j < meta_count; j++) { if (metas[j].id == addrs[i].socket_id && - (metas[j].config_type == CFG_SERVER_TYPE_PUBLIC || metas[j].nat_type == NAT_VERIFIED_EIM || metas[j].nat_type == NAT_VERIFIED_DIRECT)) { + (metas[j].config_type == CFG_SERVER_TYPE_PUBLIC || metas[j].config_type == CFG_SERVER_TYPE_UNKNOWN + || metas[j].nat_type == NAT_VERIFIED_EIM || metas[j].nat_type == NAT_VERIFIED_DIRECT)) { out_addrs[direct_count].type = addrs[i].type; memcpy(out_addrs[direct_count].ip, addrs[i].addr, 4); out_addrs[direct_count].port = htons(addrs[i].port); @@ -852,7 +856,7 @@ static void cm_handle_direct_req(struct ETCP_CONN* conn, const uint8_t* data, si (void)sock_id; (void)type; struct ETCP_SOCKET* s = conn->instance->etcp_sockets; while (s) { - if (s->type == CFG_SERVER_TYPE_PUBLIC && s->local_addr.ss_family == AF_INET) { + if ((s->type == CFG_SERVER_TYPE_PUBLIC || s->type == CFG_SERVER_TYPE_UNKNOWN) && s->local_addr.ss_family == AF_INET) { struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; memcpy(&sin.sin_addr.s_addr, ip, 4); sin.sin_port = port; diff --git a/src/etcp_connections.c b/src/etcp_connections.c index fc397898..0d1c966a 100644 --- a/src/etcp_connections.c +++ b/src/etcp_connections.c @@ -1294,6 +1294,33 @@ static void send_init_response(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pk link->etcp->log_name); } } + /* Self-detection: if my IP is non-local, I'm on a public IP */ + if (e_sock->nat_type != NAT_VERIFIED_DIRECT && e_sock->local_addr.ss_family == AF_INET) { + uint32_t my_ip = ((struct sockaddr_in*)&e_sock->local_addr)->sin_addr.s_addr; + if (my_ip != 0 && !is_local_subnet(my_ip)) { + e_sock->nat_type = NAT_VERIFIED_DIRECT; + struct sockaddr_in* nat_sin = (struct sockaddr_in*)&e_sock->nat_addr; + nat_sin->sin_family = AF_INET; + nat_sin->sin_addr.s_addr = my_ip; + nat_sin->sin_port = ((struct sockaddr_in*)&e_sock->local_addr)->sin_port; + if (link->etcp->instance->bgp) { + route_bgp_update_my_nodeinfo(link->etcp->instance->bgp->instance, link->etcp->instance->bgp); + struct ROUTE_BGP* bgp = link->etcp->instance->bgp; + if (bgp->local_node && bgp->senders_list) { + bgp->local_node->node.ver = (bgp->local_node->node.ver % 255) + 1; + bgp->local_node->dirty = 1; + struct ll_entry* se = bgp->senders_list->head; + while (se) { + struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)se->data; + if (item && item->conn) route_bgp_send_nodeinfo(bgp->local_node, item->conn); + se = se->next; + } + } + } + DEBUG_INFO(DEBUG_CATEGORY_BGP, "Self-detected PUBLIC IP: %s on socket %s (id=%d)", + ip_to_str(&my_ip, AF_INET).str, e_sock->name, e_sock->sock_id); + } + } pkt->noencrypt_len=0; pkt->link=link; link->recv_keepalive = 1; @@ -1565,6 +1592,11 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { errorcode=4; goto ec_fr; }// не init + if (e_sock->type == CFG_SERVER_TYPE_PRIVATE) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "INIT rejected: socket %s (type=PRIVATE) does not accept incoming connections", e_sock->name); + errorcode=8; + goto ec_fr; + } if (pkt_len < ETCP_INIT_REQ_V1_SIZE) { DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "INIT REQUEST too short: pkt_len=%zu from %s", pkt_len, sockaddr_storage_to_str(&addr).str); errorcode=7; diff --git a/src/route_bgp.c b/src/route_bgp.c index 27aef472..a5dd59c6 100644 --- a/src/route_bgp.c +++ b/src/route_bgp.c @@ -645,7 +645,8 @@ static struct ETCP_CONN* route_bgp_find_third_node(struct ROUTE_BGP* bgp, struct struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data; if (item && item->conn && item->conn != exclude && item->conn->links && item->conn->links->remote_only_local==0) { // Skip nodes with private socket type - they can't help with NAT detection - if (item->conn->links->remote_type != CFG_SERVER_TYPE_PRIVATE) return item->conn; + if (item->conn->links->remote_type != CFG_SERVER_TYPE_PRIVATE + && item->conn->links->remote_type != CFG_SERVER_TYPE_LOCAL) return item->conn; } e = e->next; } @@ -671,8 +672,8 @@ void route_bgp_start_link_nat_check(struct ROUTE_BGP* bgp, struct ETCP_LINK* lin if (link->nat_type == NAT_TYPE_DIRECT) return; if (link->nat_check_status == NAT_CHECK_IN_PROGRESS) return; // Skip NAT check for private sockets - they are not reachable from outside - if (link->conn->type == CFG_SERVER_TYPE_PRIVATE) { - DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "skip nat check for private socket link"); + if (link->conn->type == CFG_SERVER_TYPE_PRIVATE || link->conn->type == CFG_SERVER_TYPE_LOCAL) { + DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "skip nat check for private/local socket link"); return; } // Find third node to send ping through diff --git a/src/route_connectivity.c b/src/route_connectivity.c index e3b2e88b..296c18e2 100644 --- a/src/route_connectivity.c +++ b/src/route_connectivity.c @@ -124,14 +124,15 @@ static int conn_match_candidate_sockets(struct UTUN_INSTANCE* instance, uint32_t if_ip = if_sin->sin_addr.s_addr; if (addr_type == ADDR_TYPE_INTERFACE) { - // предпочитаем PRIVATE сокеты в той же /24 подсети - if (e_sock->type == CFG_SERVER_TYPE_PRIVATE && (if_ip & 0x00FFFFFF) == (target_ip & 0x00FFFFFF)) + // предпочитаем PRIVATE/LOCAL сокеты в той же /24 подсети + if ((e_sock->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL) + && (if_ip & 0x00FFFFFF) == (target_ip & 0x00FFFFFF)) match = 1; } else { - // NAT или REAL: предпочитаем DIRECT > EIM > PUBLIC + // NAT или REAL: предпочитаем DIRECT > EIM > PUBLIC/UNKNOWN if (e_sock->nat_type == NAT_VERIFIED_DIRECT) match = 1; else if (e_sock->nat_type == NAT_VERIFIED_EIM) match = 1; - else if (e_sock->type == CFG_SERVER_TYPE_PUBLIC) match = 1; + else if (e_sock->type == CFG_SERVER_TYPE_PUBLIC || e_sock->type == CFG_SERVER_TYPE_UNKNOWN) match = 1; } if (match) { @@ -150,10 +151,12 @@ static int conn_match_candidate_sockets(struct UTUN_INSTANCE* instance, for (int i = 0; i < found; i++) if (out_sockets[i] == e_sock) { dup = 1; break; } if (!dup) { if (addr_type == ADDR_TYPE_INTERFACE) { - if (e_sock->type == CFG_SERVER_TYPE_PRIVATE) out_sockets[found++] = e_sock; + if (e_sock->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL) + out_sockets[found++] = e_sock; } else { - // любой не-private - if (e_sock->type != CFG_SERVER_TYPE_PRIVATE) out_sockets[found++] = e_sock; + // любой не-private и не-local + if (e_sock->type != CFG_SERVER_TYPE_PRIVATE && e_sock->type != CFG_SERVER_TYPE_LOCAL) + out_sockets[found++] = e_sock; } } e_sock = e_sock->next; diff --git a/src/route_node.c b/src/route_node.c index 9f35c92e..24a724e2 100644 --- a/src/route_node.c +++ b/src/route_node.c @@ -444,6 +444,7 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG int sock6_count = 0, addr6_count = 0; struct ETCP_SOCKET* e_sock = instance->etcp_sockets; while (e_sock) { + if (e_sock->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL) { e_sock = e_sock->next; continue; } if (e_sock->local_addr.ss_family == AF_INET) { sock_count++; addr_count++; // INTERFACE @@ -454,7 +455,8 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG } } if (e_sock->nat_type == NAT_VERIFIED_DIRECT || - (e_sock->type == CFG_SERVER_TYPE_PUBLIC && e_sock->nat_type < NAT_VERIFIED_UNKNOWN)) addr_count++; + ((e_sock->type == CFG_SERVER_TYPE_PUBLIC || e_sock->type == CFG_SERVER_TYPE_UNKNOWN) + && e_sock->nat_type < NAT_VERIFIED_UNKNOWN)) addr_count++; } else if (e_sock->local_addr.ss_family == AF_INET6) { sock6_count++; addr6_count++; // INTERFACE only (no NAT for IPv6) @@ -516,6 +518,7 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG e_sock = instance->etcp_sockets; int midx = 0; while (e_sock) { + if (e_sock->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL) { e_sock = e_sock->next; continue; } if (e_sock->local_addr.ss_family == AF_INET) { meta[midx].id = e_sock->sock_id; meta[midx].config_type = e_sock->type; @@ -531,6 +534,7 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG int aidx = 0; e_sock = instance->etcp_sockets; while (e_sock) { + if (e_sock->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL) { e_sock = e_sock->next; continue; } if (e_sock->local_addr.ss_family == AF_INET) { struct sockaddr_in* if_sin = (struct sockaddr_in*)&e_sock->interface_addr; struct sockaddr_in* nat_sin = (struct sockaddr_in*)&e_sock->nat_addr; @@ -551,7 +555,8 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG } } if (e_sock->nat_type == NAT_VERIFIED_DIRECT || - (e_sock->type == CFG_SERVER_TYPE_PUBLIC && e_sock->nat_type < NAT_VERIFIED_UNKNOWN)) { + ((e_sock->type == CFG_SERVER_TYPE_PUBLIC || e_sock->type == CFG_SERVER_TYPE_UNKNOWN) + && e_sock->nat_type < NAT_VERIFIED_UNKNOWN)) { memcpy(addrs[aidx].addr, &if_sin->sin_addr.s_addr, 4); addrs[aidx].port = ntohs(if_sin->sin_port); addrs[aidx].type = ADDR_TYPE_REAL; @@ -568,6 +573,7 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG e_sock = instance->etcp_sockets; int m6idx = 0; while (e_sock) { + if (e_sock->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL) { e_sock = e_sock->next; continue; } if (e_sock->local_addr.ss_family == AF_INET6) { meta6[m6idx].id = e_sock->sock_id; meta6[m6idx].config_type = e_sock->type; @@ -583,6 +589,7 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG int a6idx = 0; e_sock = instance->etcp_sockets; while (e_sock) { + if (e_sock->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL) { e_sock = e_sock->next; continue; } if (e_sock->local_addr.ss_family == AF_INET6) { struct sockaddr_in6* if_sin6 = (struct sockaddr_in6*)&e_sock->interface_addr; memcpy(addrs6[a6idx].addr, &if_sin6->sin6_addr, 16); diff --git a/tools/etcpmon/etcpmon_gui.c b/tools/etcpmon/etcpmon_gui.c index e52c3a7f..01eb9e54 100644 --- a/tools/etcpmon/etcpmon_gui.c +++ b/tools/etcpmon/etcpmon_gui.c @@ -1535,7 +1535,9 @@ static const char* config_type_to_string(uint8_t config_type) { case CFG_SERVER_TYPE_PUBLIC: return "PUBLIC"; case CFG_SERVER_TYPE_NAT: return "NAT"; case CFG_SERVER_TYPE_PRIVATE: return "Private"; - default: return "Unknown"; + case CFG_SERVER_TYPE_LOCAL: return "Local"; + case CFG_SERVER_TYPE_UNKNOWN: return "Unknown"; + default: return "?"; } } diff --git a/tools/etcpmon/etcpmon_protocol.h b/tools/etcpmon/etcpmon_protocol.h index c54c23e7..fae0f711 100644 --- a/tools/etcpmon/etcpmon_protocol.h +++ b/tools/etcpmon/etcpmon_protocol.h @@ -38,6 +38,7 @@ extern "C" { #define CFG_SERVER_TYPE_PUBLIC 1 #define CFG_SERVER_TYPE_NAT 2 #define CFG_SERVER_TYPE_PRIVATE 3 +#define CFG_SERVER_TYPE_LOCAL 4 #define ETCPMON_MAX_MSG_SIZE 32768 #define ETCPMON_MAX_CONN_NAME 32 #define ETCPMON_MAX_CONNECTIONS 250