From 9c509d4c86c935b47615200f3ca2e134a8868ce2 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Sat, 25 Apr 2026 18:41:45 +0300 Subject: [PATCH] add connectivity probing + NAT type rename OPEN->EIM, RESTRICTED->STRICT - Rename NAT types: OPEN->EIM, RESTRICTED->STRICT across all code - Add flat addr list {type,ip,port,socket_id} to NODEINFO (NODEINFO_IPV4_ADDR) - Add NODEINFO_IPV4_SOCKET_META for per-socket metadata - Add NODE_CONNECTIVITY with per-type probe status and min_rtt - New module: route_connectivity.c/h (probing engine with socket fallback) - STRICT verified addresses excluded from NODEINFO broadcast - Trigger probing on new/updated BGP node, cancel on remove/withdraw - Update NAT type names in etcpmon GUI --- src/Makefile.am | 1 + src/etcp_connections.h | 12 +- src/route_bgp.c | 240 ++++++++++--------- src/route_bgp.h | 2 +- src/route_connectivity.c | 348 +++++++++++++++++++++++++++ src/route_connectivity.h | 20 ++ src/route_node.c | 392 ++++++++++++------------------- src/route_node.h | 137 +++++++---- src/route_ping.c | 10 +- tests/Makefile.am | 1 + tests/test_nat_detection.c | 79 ++++--- tests/test_route_ping.c | 16 +- tools/etcpmon/etcpmon_gui.c | 14 +- tools/etcpmon/etcpmon_protocol.h | 8 +- 14 files changed, 807 insertions(+), 473 deletions(-) create mode 100644 src/route_connectivity.c create mode 100644 src/route_connectivity.h diff --git a/src/Makefile.am b/src/Makefile.am index 9e8a366f..ff86e994 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,6 +10,7 @@ utun_CORE_SOURCES = \ route_bgp.c \ route_ping.c \ route_node.c \ + route_connectivity.c \ routing.c \ tun_if.c \ tun_route.c \ diff --git a/src/etcp_connections.h b/src/etcp_connections.h index 3cf9a8d6..53e44b92 100644 --- a/src/etcp_connections.h +++ b/src/etcp_connections.h @@ -89,19 +89,19 @@ struct ETCP_SOCKET { #define NAT_CHECK_NONE 0 #define NAT_CHECK_WAITING 1 #define NAT_CHECK_IN_PROGRESS 2 -#define NAT_CHECK_OPEN 3 -#define NAT_CHECK_RESTRICTED 4 +#define NAT_CHECK_EIM 3 +#define NAT_CHECK_STRICT 4 // NAT type (detected by server during NAT check) #define NAT_TYPE_UNKNOWN 0 -#define NAT_TYPE_OPEN 1 -#define NAT_TYPE_RESTRICTED 2 +#define NAT_TYPE_EIM 1 // Endpoint-Independent Mapping +#define NAT_TYPE_STRICT 2 // Address/Restricted or Symmetric // Verified NAT types (published in nodeinfo after server-side NAT detection) // Values above CFG_SERVER_TYPE_PRIVATE to avoid collision with config types #define NAT_VERIFIED_UNKNOWN 4 // detection failed or inconclusive -#define NAT_VERIFIED_OPEN 5 // open NAT -#define NAT_VERIFIED_RESTRICTED 6 // restricted NAT +#define NAT_VERIFIED_EIM 5 // EIM NAT +#define NAT_VERIFIED_STRICT 6 // strict NAT #define NAT_VERIFIED_DIRECT 7 // real public IP, no NAT diff --git a/src/route_bgp.c b/src/route_bgp.c index 6f5d4d09..0409a30c 100644 --- a/src/route_bgp.c +++ b/src/route_bgp.c @@ -18,6 +18,7 @@ #include "route_lib.h" #include "route_bgp.h" #include "route_ping.h" +#include "route_connectivity.h" // ============================================================================ @@ -58,8 +59,10 @@ static char* nodeinfo_format(const uint8_t* data, size_t len) { if (nl > 0 && nl < 63 && off + nl < len - sizeof(struct BGP_NODEINFO_PACKET)) { memcpy(name_buf, dyn + off, nl); off += nl; } - off += ni->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET); - off += ni->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET); + off += ni->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET_META); + off += ni->local_v4_addrs * sizeof(struct NODEINFO_IPV4_ADDR); + off += ni->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET_META); + off += ni->local_v6_addrs * sizeof(struct NODEINFO_IPV6_ADDR); char subs_buf[512] = {0}; if (ni->local_v4_subnets > 0) { const struct NODEINFO_IPV4_SUBNET* subs = (const struct NODEINFO_IPV4_SUBNET*)(dyn + off); @@ -70,10 +73,10 @@ static char* nodeinfo_format(const uint8_t* data, size_t len) { strcat(subs_buf,tmp); sl += strlen(tmp); } } - int need = snprintf(NULL,0,"NODEINFO nid=%016llx ver=%u name=\"%s\" v4subs=\"%s\" v4s=%u v6s=%u v4subcnt=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v6_sockets,(unsigned)ni->local_v4_subnets,(unsigned)ni->hop_count); + int need = snprintf(NULL,0,"NODEINFO nid=%016llx ver=%u name=\"%s\" v4subs=\"%s\" v4s=%u v4a=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v4_addrs,(unsigned)ni->hop_count); char* buf = u_malloc(need+1); if (!buf) return NULL; - snprintf(buf,need+1,"NODEINFO nid=%016llx ver=%u name=\"%s\" v4subs=\"%s\" v4s=%u v6s=%u v4subcnt=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v6_sockets,(unsigned)ni->local_v4_subnets,(unsigned)ni->hop_count); + snprintf(buf,need+1,"NODEINFO nid=%016llx ver=%u name=\"%s\" v4subs=\"%s\" v4s=%u v4a=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v4_addrs,(unsigned)ni->hop_count); return buf; } @@ -284,6 +287,8 @@ void route_bgp_destroy(struct UTUN_INSTANCE* instance) { etcp_unbind(instance, ETCP_ID_ROUTE_ENTRY); + route_connectivity_cancel_all(instance); + // route_ping_destroy_pending(instance->bgp); struct ll_entry* e; @@ -395,7 +400,6 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) { struct ROUTE_TABLE* rt = conn->instance->rt; // Remove this connection from all nodes' path lists - // and send WITHDRAW if a node becomes unreachable bool need_withdraw = false; struct ll_entry* node_entry = bgp->nodes ? bgp->nodes->head : NULL; while (node_entry) { @@ -403,20 +407,13 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) { struct NODEINFO_Q* nq = (struct NODEINFO_Q*)node_entry; if (route_bgp_remove_path(nq, conn) == 1) { need_withdraw = true; - if (rt) { - route_delete(rt, nq); - } + if (rt) route_delete(rt, nq); nq->dirty = 1; - if (nq->paths) { - queue_free(nq->paths); - nq->paths = NULL; - } + route_connectivity_cancel_node(conn->instance, nq); + if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } uint64_t key = nq->node.node_id; struct ll_entry* entry = node_entry; - if (entry) { - queue_remove_data(bgp->nodes, entry); - queue_entry_free(entry); - } + if (entry) { queue_remove_data(bgp->nodes, entry); queue_entry_free(entry); } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removed node %016llx after link down", (unsigned long long)key); } node_entry = next; @@ -562,11 +559,13 @@ int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn) int nodeinfo_dyn_size(struct NODEINFO* node) { return node->node_name_len + - node->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET) + - node->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET) + + node->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET_META) + + node->local_v4_addrs * sizeof(struct NODEINFO_IPV4_ADDR) + + node->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET_META) + + node->local_v6_addrs * sizeof(struct NODEINFO_IPV6_ADDR) + node->local_v4_subnets * sizeof(struct NODEINFO_IPV4_SUBNET) + node->local_v6_subnets * sizeof(struct NODEINFO_IPV6_SUBNET) + - node->tranzit_nodes * 8 + + node->tranzit_nodes * sizeof(struct NODEINFO_TRANZIT_NODE) + node->hop_count * 8; } @@ -583,9 +582,9 @@ static void nat_link_check_cb(int success, uint16_t avg_rtt, uint8_t count_sent, u_free(na); return; } - uint8_t nat_type = success ? NAT_TYPE_OPEN : NAT_TYPE_RESTRICTED; + uint8_t nat_type = success ? NAT_TYPE_EIM : NAT_TYPE_STRICT; link->nat_type = nat_type; - link->nat_check_status = success ? NAT_CHECK_OPEN : NAT_CHECK_RESTRICTED; + link->nat_check_status = success ? NAT_CHECK_EIM : NAT_CHECK_STRICT; // Send NAT_INFO to peer (via their connection) if (link->etcp) { uint8_t socket_id = link->remote_socket_id; @@ -716,39 +715,52 @@ int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, co from->log_name, new_ver, nodeinfo1->last_ver); return 0; } + int new_data_size=sizeof(struct NODEINFO_Q) - sizeof(struct ll_entry) + dyn_size + 8; struct ll_queue* paths=NULL; int need_alloc=0; - if (nodeinfo1) {// remove old node + int is_new_node = 0; + int socks_changed = 0; + if (nodeinfo1) { paths=nodeinfo1->paths; - if (nodeinfo1->ll.size < new_data_size) { - - need_alloc=1; - } - } else need_alloc=1; + socks_changed = (nodeinfo1->node.local_v4_sockets != ni->local_v4_sockets) || + (nodeinfo1->node.local_v4_addrs != ni->local_v4_addrs); + if (nodeinfo1->ll.size < new_data_size) need_alloc=1; + } else { + need_alloc=1; + is_new_node = 1; + } if (need_alloc) { if (nodeinfo1) { queue_remove_data(bgp->nodes, &nodeinfo1->ll); queue_entry_free(&nodeinfo1->ll); - } + } nodeinfo1 = (struct NODEINFO_Q*)queue_entry_new(new_data_size); paths = queue_new(bgp->instance->ua, 0, "node_paths"); memcpy(&nodeinfo1->node, ni, sizeof(struct NODEINFO) + dyn_size); + nodeinfo1->connectivity.probe_status = PROBE_STATUS_NONE; + nodeinfo1->connectivity.interface_status = PROBE_RESULT_UNKNOWN; + nodeinfo1->connectivity.nat_status = PROBE_RESULT_UNKNOWN; + nodeinfo1->connectivity.real_status = PROBE_RESULT_UNKNOWN; queue_data_put_with_index(bgp->nodes, &nodeinfo1->ll, offsetof(struct NODEINFO_Q, node.node_id)-sizeof(struct ll_entry), 8); + } else { + socks_changed = (nodeinfo1->node.local_v4_sockets != ni->local_v4_sockets) || + (nodeinfo1->node.local_v4_addrs != ni->local_v4_addrs); + memcpy(&nodeinfo1->node, ni, sizeof(struct NODEINFO) + dyn_size); } - else memcpy(&nodeinfo1->node, ni, sizeof(struct NODEINFO) + dyn_size); - + /* Если это наш local_node (по node_id), обновляем его данные */ DEBUG_INFO(DEBUG_CATEGORY_BGP, "Checking local_node update: node_id=%016llx my_id=%016llx local_node=%p", (unsigned long long)node_id, (unsigned long long)bgp->instance->node_id, (void*)bgp->local_node); if (node_id == bgp->instance->node_id && bgp->local_node) { - /* Копируем динамическую часть (сокеты, подсети) из nodeinfo1 в local_node */ uint8_t* dyn_src = (uint8_t*)&nodeinfo1->node + sizeof(struct NODEINFO); uint8_t* dyn_dst = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO); - int dyn_sz = nodeinfo1->node.node_name_len + - nodeinfo1->node.local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET) + - nodeinfo1->node.local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET) + + int dyn_sz = nodeinfo1->node.node_name_len + + nodeinfo1->node.local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET_META) + + nodeinfo1->node.local_v4_addrs * sizeof(struct NODEINFO_IPV4_ADDR) + + nodeinfo1->node.local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET_META) + + nodeinfo1->node.local_v6_addrs * sizeof(struct NODEINFO_IPV6_ADDR) + nodeinfo1->node.local_v4_subnets * sizeof(struct NODEINFO_IPV4_SUBNET) + nodeinfo1->node.local_v6_subnets * sizeof(struct NODEINFO_IPV6_SUBNET); memcpy(dyn_dst, dyn_src, dyn_sz); @@ -783,18 +795,21 @@ int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, co uint64_t id=item->conn->peer_node_id; int found=0; for (int i=0; iconn); - } + if (found==0) route_bgp_send_nodeinfo(nodeinfo1, item->conn); else DEBUG_INFO(DEBUG_CATEGORY_BGP, "Skip send NODEINFO to node %016llx",id); - } e = e->next; } - DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processed NODEINFO from %s (node=%016llx,ver=%d,paths=%d)", + DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processed NODEINFO from %s (node=%016llx,ver=%d,paths=%d,addrs=%d)", from->log_name, (unsigned long long)node_id, new_ver, - nodeinfo1->paths ? queue_entry_count(nodeinfo1->paths) : 0); + nodeinfo1->paths ? queue_entry_count(nodeinfo1->paths) : 0, + ni->local_v4_addrs); + + // Запуск connectivity probing для нового или обновлённого узла + if (node_id != bgp->instance->node_id && (is_new_node || socks_changed) && nodeinfo1->node.local_v4_addrs > 0) { + route_connectivity_probe_node(bgp->instance, nodeinfo1); + } return 0; } @@ -813,20 +828,13 @@ int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* sender, } int ret=route_bgp_remove_path_by_hop(nq, wd_source); if (ret>0 || !nq->paths || (nq->paths && queue_entry_count(nq->paths) == 0)) { - if (bgp->instance && bgp->instance->rt) { - route_delete(bgp->instance->rt, nq); - } + if (bgp->instance && bgp->instance->rt) route_delete(bgp->instance->rt, nq); nq->dirty = 1; - if (nq->paths) { - queue_free(nq->paths); - nq->paths = NULL; - } + route_connectivity_cancel_node(bgp->instance, nq); + if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } uint64_t key = node_id; struct ll_entry* entry = queue_find_data_by_index(bgp->nodes, &key, 8); - if (entry) { - queue_remove_data(bgp->nodes, entry); - queue_entry_free(entry); - } + if (entry) { queue_remove_data(bgp->nodes, entry); queue_entry_free(entry); } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removed node %016llx after WITHDRAW", (unsigned long long)node_id); route_bgp_broadcast_withdraw(bgp, node_id, wd_source, sender); } @@ -939,86 +947,90 @@ static void route_bgp_handle_nat_info(struct ROUTE_BGP* bgp, struct ETCP_CONN* f if (!bgp || !from_conn || !data || len < sizeof(struct BGP_NAT_INFO) || !bgp->local_node) return; const struct BGP_NAT_INFO* info = (const struct BGP_NAT_INFO*)data; - /* IP в пакете в network byte order (big-endian bytes), конвертируем в host order */ uint32_t nat_ip = (info->nat_ip[0] << 24) | (info->nat_ip[1] << 16) | (info->nat_ip[2] << 8) | info->nat_ip[3]; - uint16_t nat_port = ntohs(info->nat_port); /* порт из network byte order */ + uint16_t nat_port = ntohs(info->nat_port); uint8_t socket_id = info->socket_id; - DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO recv: ip_bytes=0x%02x%02x%02x%02x ip_host=0x%08x port_net=%u port_host=%u", + DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO recv: ip_bytes=0x%02x%02x%02x%02x ip_host=0x%08x port_net=%u port_host=%u socket_id=%u type=%d", info->nat_ip[0], info->nat_ip[1], info->nat_ip[2], info->nat_ip[3], - nat_ip, info->nat_port, nat_port); + nat_ip, info->nat_port, nat_port, socket_id, info->nat_type); + // Обновить тип в socket meta uint8_t* dynamic = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO); dynamic += bgp->local_node->node.node_name_len; - struct NODEINFO_IPV4_SOCKET* sockets = (struct NODEINFO_IPV4_SOCKET*)dynamic; - DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO updating local_node: sockets_count=%d looking for socket_id=%u", - bgp->local_node->node.local_v4_sockets, socket_id); + struct NODEINFO_IPV4_SOCKET_META* meta = (struct NODEINFO_IPV4_SOCKET_META*)dynamic; + uint8_t verified_type; + if (info->nat_type == NAT_TYPE_EIM) verified_type = NAT_VERIFIED_EIM; + else if (info->nat_type == NAT_TYPE_STRICT) verified_type = NAT_VERIFIED_STRICT; + else verified_type = NAT_VERIFIED_UNKNOWN; + + int data_changed = 0; for (int i = 0; i < bgp->local_node->node.local_v4_sockets; i++) { - DEBUG_INFO(DEBUG_CATEGORY_BGP, " checking socket %d: id=%u", i, sockets[i].id); - if (sockets[i].id == socket_id) { - uint8_t old_type = sockets[i].type; - /* nat_addr[] хранится в network byte order (big-endian bytes) */ - uint32_t old_ip = (sockets[i].nat_addr[0] << 24) | (sockets[i].nat_addr[1] << 16) | - (sockets[i].nat_addr[2] << 8) | sockets[i].nat_addr[3]; - uint16_t old_port = sockets[i].nat_port; - memcpy(sockets[i].nat_addr, info->nat_ip, 4); - sockets[i].nat_port = nat_port; - uint8_t verified_type; - if (info->nat_type == NAT_TYPE_OPEN) { - verified_type = NAT_VERIFIED_OPEN; - } else if (info->nat_type == NAT_TYPE_RESTRICTED) { - verified_type = NAT_VERIFIED_RESTRICTED; - } else { - verified_type = NAT_VERIFIED_UNKNOWN; - } - sockets[i].type = verified_type; - - struct ETCP_SOCKET* es = bgp->instance->etcp_sockets; - while (es) { - if (es->sock_id == socket_id) { - es->nat_type = verified_type; - break; - } - es = es->next; + if (meta[i].id == socket_id) { + if (meta[i].type != verified_type) { + meta[i].type = verified_type; + data_changed = 1; } + break; + } + } - DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO update: socket_id=%u old_ip=0x%08x nat_ip=0x%08x old_port=%u nat_port=%u old_type=%u verified_type=%u changed=%d", - socket_id, old_ip, nat_ip, old_port, nat_port, old_type, verified_type, - (old_ip != nat_ip || old_port != nat_port || old_type != verified_type) ? 1 : 0); - if (old_ip != nat_ip || old_port != nat_port || old_type != verified_type) { - bgp->local_node->dirty = 1; - bgp->local_node->node.ver = (bgp->local_node->node.ver + 1) % 255 + 1; - DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO triggered NODEINFO send, ver=%d senders=%d", - bgp->local_node->node.ver, - bgp->senders_list ? queue_entry_count(bgp->senders_list) : 0); - if (bgp->senders_list) { - 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) { - DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO sending NODEINFO to %s", item->conn->log_name); - route_bgp_send_nodeinfo(bgp->local_node, item->conn); - } - se = se->next; - } - } + // Обновить ETCP_SOCKET + struct ETCP_SOCKET* es = bgp->instance->etcp_sockets; + while (es) { + if (es->sock_id == socket_id) { + if (verified_type == NAT_VERIFIED_STRICT) { + // STRICT: не добавляем NAT адрес в список (бесполезен для probing) + // но обновляем nat_addr в сокете для информации + struct sockaddr_in* nat_sin = (struct sockaddr_in*)&es->nat_addr; + nat_sin->sin_family = AF_INET; + nat_sin->sin_addr.s_addr = nat_ip; + nat_sin->sin_port = htons(nat_port); + es->nat_type = verified_type; + DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO: socket %d is STRICT, not adding to addr list", socket_id); + } else { + struct sockaddr_in* nat_sin = (struct sockaddr_in*)&es->nat_addr; + uint32_t old_nat_ip = nat_sin->sin_addr.s_addr; + uint16_t old_nat_port = ntohs(nat_sin->sin_port); + nat_sin->sin_family = AF_INET; + nat_sin->sin_addr.s_addr = nat_ip; + nat_sin->sin_port = htons(nat_port); + if (old_nat_ip != nat_ip || old_nat_port != nat_port) data_changed = 1; + es->nat_type = verified_type; + DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO: socket %d type=%d ip=%08x port=%u", socket_id, verified_type, nat_ip, nat_port); } break; } + es = es->next; } - struct ETCP_LINK* l = from_conn->links; - while (l) { - l->nat_type = info->nat_type; - l = l->next; + // Обновить local_node и разослать если изменилось + if (data_changed) { + int prev_v4_addrs = bgp->local_node->node.local_v4_addrs; + route_bgp_update_my_nodeinfo(bgp->instance, bgp); + if (bgp->local_node->node.local_v4_addrs != prev_v4_addrs) { + bgp->local_node->dirty = 1; + bgp->local_node->node.ver = (bgp->local_node->node.ver % 255) + 1; + bgp->local_node->last_ver = bgp->local_node->node.ver; + } + if (bgp->local_node->dirty && bgp->senders_list) { + 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, "NAT_INFO from %s: socket_id=%u type=%s ip=%u.%u.%u.%u %s port=%u", + // NAT type for the link + struct ETCP_LINK* l = from_conn->links; + while (l) { l->nat_type = info->nat_type; l = l->next; } + + DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO from %s: socket_id=%u type=%s ip=%s port=%u", from_conn->log_name, socket_id, - info->nat_type == NAT_TYPE_OPEN ? "OPEN" : "RESTRICTED", - (info->nat_ip[0]), (info->nat_ip[1]), - (info->nat_ip[2]), (info->nat_ip[3]), ip_to_str(&info->nat_ip, AF_INET).str, - (unsigned)nat_port); + info->nat_type == NAT_TYPE_EIM ? "EIM" : "STRICT", + ip_to_str(info->nat_ip, AF_INET).str, (unsigned)nat_port); } static void route_bgp_handle_nat_check_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len) {// поиск нужного линка и запуск nat_check @@ -1088,7 +1100,7 @@ void route_bgp_send_nat_info(struct ETCP_CONN* conn, uint8_t socket_id, uint32_t e->len = sizeof(struct BGP_NAT_INFO); DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_send_nat_info to %s: socket_id=%u type=%s ip=%s port=%u", conn->log_name, socket_id, - nat_type == NAT_TYPE_OPEN ? "OPEN" : "RESTRICTED", + nat_type == NAT_TYPE_EIM ? "EIM" : "STRICT", ip_to_str(pkt->nat_ip, AF_INET).str, (unsigned)nat_port); etcp_send(conn, e); } diff --git a/src/route_bgp.h b/src/route_bgp.h index ccda1355..70e2a070 100644 --- a/src/route_bgp.h +++ b/src/route_bgp.h @@ -189,7 +189,7 @@ int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn); * @param conn соединение к клиенту * @param nat_ip IP клиента (network byte order) * @param nat_port порт клиента (network byte order) - * @param nat_type NAT_TYPE_OPEN или NAT_TYPE_RESTRICTED + * @param nat_type NAT_TYPE_EIM или NAT_TYPE_STRICT */ void route_bgp_send_nat_info(struct ETCP_CONN* conn, uint8_t socket_id, uint32_t nat_ip, uint16_t nat_port, uint8_t nat_type); diff --git a/src/route_connectivity.c b/src/route_connectivity.c new file mode 100644 index 00000000..f05d4c00 --- /dev/null +++ b/src/route_connectivity.c @@ -0,0 +1,348 @@ +#include +#include +#ifdef _WIN32 +#include +#include +#else +#include +#endif +#include "../lib/platform_compat.h" +#include "../lib/debug_config.h" +#include "../lib/mem.h" +#include "../lib/u_async.h" +#include "utun_instance.h" +#include "etcp.h" +#include "etcp_connections.h" +#include "route_node.h" +#include "route_bgp.h" +#include "route_connectivity.h" + +#define CONN_MAX_SOCKET_CANDIDATES 8 + +struct conn_probe_ctx { + struct UTUN_INSTANCE* instance; + struct NODEINFO_Q* nq; + uint8_t addr_type; // ADDR_TYPE_* + struct sockaddr_storage target_addr; + uint8_t peer_pubkey[SC_PUBKEY_SIZE]; + + struct ETCP_SOCKET* candidate_sockets[CONN_MAX_SOCKET_CANDIDATES]; + uint8_t candidate_count; + uint8_t candidate_index; + + uint16_t best_across_sockets; // min RTT по всем сокетам + uint8_t count_total; // 3 на серию + uint8_t count_sent; + uint8_t count_ok; + uint16_t min_rtt; // min RTT в текущей серии + uint16_t timeout_ms; + void* ping_timer; +}; + +// ---- forward ---- +static void conn_probe_single_cb(int success, uint16_t rtt, void* arg, + uint64_t nonce, const uint8_t* resp_data, size_t resp_data_len); +static void conn_probe_finish(struct conn_probe_ctx* ctx, int ok); +static void conn_probe_start_series(struct conn_probe_ctx* ctx); + +// ---- helpers ---- + +static int sock_addr_cmp(const struct sockaddr_storage* a, const struct sockaddr_storage* b) { + if (a->ss_family != b->ss_family) return 1; + if (a->ss_family == AF_INET) { + const struct sockaddr_in* sa = (const struct sockaddr_in*)a; + const struct sockaddr_in* sb = (const struct sockaddr_in*)b; + if (sa->sin_addr.s_addr != sb->sin_addr.s_addr) return 1; + if (sa->sin_port != sb->sin_port) return 1; + return 0; + } + return memcmp(a, b, sizeof(struct sockaddr_storage)); +} + +// проверяет что два адреса (NODEINFO_IPV4_ADDR) не дубликаты по IP+port +static int addr_eq(const struct NODEINFO_IPV4_ADDR* a, uint32_t ip, uint16_t port) { + uint32_t a_ip; memcpy(&a_ip, a->addr, 4); + return a_ip == ip && a->port == port; +} + +// собирает список локальных сокетов-кандидатов для probing заданного адреса +// сортирует: лучшие (по совпадению подсети / типу NAT) первые +static int conn_match_candidate_sockets(struct UTUN_INSTANCE* instance, + uint8_t addr_type, + const struct sockaddr_storage* target_addr, + struct ETCP_SOCKET** out_sockets, uint8_t max_count) { + if (!instance || !target_addr || !out_sockets || max_count == 0) return 0; + + uint32_t target_ip = ((const struct sockaddr_in*)target_addr)->sin_addr.s_addr; + int found = 0; + struct ETCP_SOCKET* e_sock = instance->etcp_sockets; + + // Проход 1: точное совпадение подсети (для INTERFACE) или PUBLIC/NAT_VERIFIED (для NAT/REAL) + while (e_sock && found < (int)max_count) { + if (e_sock->local_addr.ss_family != AF_INET) { e_sock = e_sock->next; continue; } + int match = 0; + struct sockaddr_in* if_sin = (struct sockaddr_in*)&e_sock->interface_addr; + 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)) + match = 1; + } else { + // NAT или REAL: предпочитаем DIRECT > EIM > PUBLIC + 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; + } + + if (match) { + int dup = 0; + for (int i = 0; i < found; i++) if (out_sockets[i] == e_sock) { dup = 1; break; } + if (!dup) out_sockets[found++] = e_sock; + } + e_sock = e_sock->next; + } + + // Проход 2: все остальные подходящие + e_sock = instance->etcp_sockets; + while (e_sock && found < (int)max_count) { + if (e_sock->local_addr.ss_family != AF_INET) { e_sock = e_sock->next; continue; } + int dup = 0; + 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; + } else { + // любой не-private + if (e_sock->type != CFG_SERVER_TYPE_PRIVATE) out_sockets[found++] = e_sock; + } + } + e_sock = e_sock->next; + } + + // Проход 3: остальные IPv4 (fallback) + e_sock = instance->etcp_sockets; + while (e_sock && found < (int)max_count) { + if (e_sock->local_addr.ss_family != AF_INET) { e_sock = e_sock->next; continue; } + int dup = 0; + for (int i = 0; i < found; i++) if (out_sockets[i] == e_sock) { dup = 1; break; } + if (!dup) out_sockets[found++] = e_sock; + e_sock = e_sock->next; + } + + return found; +} + +// ---- probe lifecycle ---- + +static void conn_probe_start_series(struct conn_probe_ctx* ctx) { + if (ctx->candidate_index >= ctx->candidate_count) { + // все сокеты перебраны + if (ctx->best_across_sockets != 65535) conn_probe_finish(ctx, 1); + else conn_probe_finish(ctx, 0); + return; + } + + struct ETCP_SOCKET* sock = ctx->candidate_sockets[ctx->candidate_index]; + ctx->count_sent = 0; + ctx->count_ok = 0; + ctx->min_rtt = 65535; + + DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe series start: socket=%s (idx=%d/%d) addr_type=%d target=%s:%u", + sock->name, ctx->candidate_index, ctx->candidate_count, ctx->addr_type, + ip_to_str(&((struct sockaddr_in*)&ctx->target_addr)->sin_addr, AF_INET).str, + (unsigned)ntohs(((struct sockaddr_in*)&ctx->target_addr)->sin_port)); + + int ret = etcp_send_ping_to_socket(ctx->instance, sock, ctx->peer_pubkey, + &ctx->target_addr, ctx->timeout_ms, + conn_probe_single_cb, ctx, NULL, 0); + if (ret != 0) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "probe: cannot start ping from socket %s", sock->name); + ctx->count_sent = 3; // simulate full failure + ctx->candidate_index++; + conn_probe_start_series(ctx); + } +} + +static void conn_probe_single_cb(int success, uint16_t rtt, void* arg, + uint64_t nonce, const uint8_t* resp_data, size_t resp_data_len) { + (void)nonce; (void)resp_data; (void)resp_data_len; + struct conn_probe_ctx* ctx = (struct conn_probe_ctx*)arg; + if (!ctx) return; + + ctx->count_sent++; + if (success) { + ctx->count_ok++; + if (rtt < ctx->min_rtt) ctx->min_rtt = rtt; + } + + DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe cb: success=%d rtt=%u sent=%d ok=%d min_rtt=%u", + success, rtt, ctx->count_sent, ctx->count_ok, ctx->min_rtt); + + if (ctx->count_sent < ctx->count_total) { + // продолжаем с тем же сокетом + struct ETCP_SOCKET* sock = ctx->candidate_sockets[ctx->candidate_index]; + int ret = etcp_send_ping_to_socket(ctx->instance, sock, ctx->peer_pubkey, + &ctx->target_addr, ctx->timeout_ms, + conn_probe_single_cb, ctx, NULL, 0); + if (ret != 0) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "probe: cannot continue ping from socket %s", sock->name); + ctx->count_sent = ctx->count_total; // force finish series + ctx->count_ok = 0; + } else return; // следующий пинг отправлен, ждём callback + } + + // серия из 3 пингов завершена + if (ctx->count_ok > 0) { + if (ctx->min_rtt < ctx->best_across_sockets) ctx->best_across_sockets = ctx->min_rtt; + conn_probe_finish(ctx, 1); + } else { + // этот сокет не подошёл — пробуем следующий + ctx->candidate_index++; + conn_probe_start_series(ctx); + } +} + +static void conn_probe_finish(struct conn_probe_ctx* ctx, int ok) { + if (!ctx || !ctx->nq) return; + struct NODE_CONNECTIVITY* c = &ctx->nq->connectivity; + + switch (ctx->addr_type) { + case ADDR_TYPE_INTERFACE: + c->interface_status = ok ? PROBE_RESULT_REACHABLE : (c->interface_status == PROBE_RESULT_UNKNOWN ? PROBE_RESULT_UNREACHABLE : c->interface_status); + if (ok && ctx->best_across_sockets < c->interface_min_rtt) c->interface_min_rtt = ctx->best_across_sockets; + c->interface_probe_time = get_time_tb(); + break; + case ADDR_TYPE_NAT: + c->nat_status = ok ? PROBE_RESULT_REACHABLE : (c->nat_status == PROBE_RESULT_UNKNOWN ? PROBE_RESULT_UNREACHABLE : c->nat_status); + if (ok && ctx->best_across_sockets < c->nat_min_rtt) c->nat_min_rtt = ctx->best_across_sockets; + c->nat_probe_time = get_time_tb(); + break; + case ADDR_TYPE_REAL: + c->real_status = ok ? PROBE_RESULT_REACHABLE : (c->real_status == PROBE_RESULT_UNKNOWN ? PROBE_RESULT_UNREACHABLE : c->real_status); + if (ok && ctx->best_across_sockets < c->real_min_rtt) c->real_min_rtt = ctx->best_across_sockets; + c->real_probe_time = get_time_tb(); + break; + } + + if (c->pending_count > 0) c->pending_count--; + if (c->pending_count == 0) { + c->probe_status = PROBE_STATUS_DONE; + DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe DONE for node %016llx: intf=%d nat=%d real=%d", + (unsigned long long)ctx->nq->node.node_id, + c->interface_status, c->nat_status, c->real_status); + } + + u_free(ctx); +} + +// ---- public API ---- + +void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct NODEINFO_Q* nq) { + if (!instance || !nq) return; + if (nq->node.node_id == instance->node_id) return; // не пингуем себя + if (nq->connectivity.probe_status == PROBE_STATUS_IN_PROGRESS) { + DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe already in progress for node %016llx", (unsigned long long)nq->node.node_id); + return; + } + + const struct NODEINFO_IPV4_ADDR* addrs; + int addr_count = get_node_v4_addrs(nq, &addrs); + if (addr_count <= 0) { + nq->connectivity.probe_status = PROBE_STATUS_DONE; + DEBUG_INFO(DEBUG_CATEGORY_BGP, "no addresses to probe for node %016llx", (unsigned long long)nq->node.node_id); + return; + } + + // дедупликация: массив уже проверенных (IP, port) пар + uint32_t seen_ips[16]; uint16_t seen_ports[16]; + int seen_count = 0; + int pend = 0; + + nq->connectivity.probe_status = PROBE_STATUS_IN_PROGRESS; + nq->connectivity.probe_start_time = get_time_tb(); + nq->connectivity.interface_status = PROBE_RESULT_UNKNOWN; + nq->connectivity.nat_status = PROBE_RESULT_UNKNOWN; + nq->connectivity.real_status = PROBE_RESULT_UNKNOWN; + nq->connectivity.interface_min_rtt = 65535; + nq->connectivity.nat_min_rtt = 65535; + nq->connectivity.real_min_rtt = 65535; + nq->connectivity.pending_count = 0; + + for (int i = 0; i < addr_count; i++) { + uint32_t ip; memcpy(&ip, addrs[i].addr, 4); + uint16_t port = addrs[i].port; + if (ip == 0 || port == 0) continue; + + // дедупликация + int dup = 0; + for (int j = 0; j < seen_count; j++) { + if (seen_ips[j] == ip && seen_ports[j] == port) { dup = 1; break; } + } + if (dup) continue; + if (seen_count >= 16) break; + seen_ips[seen_count] = ip; seen_ports[seen_count] = port; seen_count++; + + // собрать целевой адрес + struct sockaddr_storage target; + memset(&target, 0, sizeof(target)); + struct sockaddr_in* sin = (struct sockaddr_in*)⌖ + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = ip; + sin->sin_port = htons(port); + + // собрать кандидатские локальные сокеты + struct ETCP_SOCKET* candidates[CONN_MAX_SOCKET_CANDIDATES]; + int cand_count = conn_match_candidate_sockets(instance, addrs[i].type, &target, + candidates, CONN_MAX_SOCKET_CANDIDATES); + if (cand_count == 0) { + DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe: no local sockets for target %s:%u type=%d", + ip_to_str(&ip, AF_INET).str, port, addrs[i].type); + continue; + } + + struct conn_probe_ctx* ctx = u_calloc(1, sizeof(struct conn_probe_ctx)); + if (!ctx) continue; + ctx->instance = instance; + ctx->nq = nq; + ctx->addr_type = addrs[i].type; + ctx->target_addr = target; + memcpy(ctx->peer_pubkey, nq->node.public_key, SC_PUBKEY_SIZE); + memcpy(ctx->candidate_sockets, candidates, cand_count * sizeof(struct ETCP_SOCKET*)); + ctx->candidate_count = cand_count; + ctx->candidate_index = 0; + ctx->count_total = CONN_PROBE_COUNT; + ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS; + ctx->best_across_sockets = 65535; + + pend++; + conn_probe_start_series(ctx); + } + + if (pend == 0) { + nq->connectivity.probe_status = PROBE_STATUS_DONE; + } else { + if (nq->node.local_v4_addrs == 0) nq->connectivity.interface_status = PROBE_RESULT_UNREACHABLE; + nq->connectivity.pending_count = pend; + DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe started for node %016llx: %d series pending", + (unsigned long long)nq->node.node_id, pend); + } +} + +void route_connectivity_cancel_node(struct UTUN_INSTANCE* instance, struct NODEINFO_Q* nq) { + if (!instance || !nq) return; + nq->connectivity.probe_status = PROBE_STATUS_NONE; + nq->connectivity.pending_count = 0; + DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe cancelled for node %016llx", (unsigned long long)nq->node.node_id); +} + +void route_connectivity_cancel_all(struct UTUN_INSTANCE* instance) { + if (!instance || !instance->bgp || !instance->bgp->nodes) return; + struct ll_entry* e = instance->bgp->nodes->head; + while (e) { + struct NODEINFO_Q* nq = (struct NODEINFO_Q*)e; + nq->connectivity.probe_status = PROBE_STATUS_NONE; + nq->connectivity.pending_count = 0; + e = e->next; + } +} diff --git a/src/route_connectivity.h b/src/route_connectivity.h new file mode 100644 index 00000000..1d6f65e3 --- /dev/null +++ b/src/route_connectivity.h @@ -0,0 +1,20 @@ +#ifndef ROUTE_CONNECTIVITY_H +#define ROUTE_CONNECTIVITY_H + +#include +#include "route_node.h" + +struct UTUN_INSTANCE; + +// Запускает зондирование связности ко всем адресам удалённого узла +void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, + struct NODEINFO_Q* nq); + +// Отменяет все pending пробы для узла (при удалении / withdraw) +void route_connectivity_cancel_node(struct UTUN_INSTANCE* instance, + struct NODEINFO_Q* nq); + +// Отменяет все pending пробы для всех узлов (при destroy) +void route_connectivity_cancel_all(struct UTUN_INSTANCE* instance); + +#endif // ROUTE_CONNECTIVITY_H diff --git a/src/route_node.c b/src/route_node.c index f51e9798..f0d697f6 100644 --- a/src/route_node.c +++ b/src/route_node.c @@ -1,247 +1,145 @@ -#include -#include -#include - -#include "../lib/ll_queue.h" -#include "../lib/debug_config.h" -#include "../lib/mem.h" -#include "utun_instance.h" -#include "etcp.h" -#include "config_parser.h" -#include "route_node.h" -#include "route_bgp.h" -#include "etcp_debug.h" - - - -/** - * @brief Получает указатель на массив IPv4-подсетей узла (без malloc/копирования). - * - * Функция вычисляет смещение внутри динамической части BGP_NODEINFO_Q - * и возвращает прямой указатель на массив struct BGP_NODEINFO_IPV4_SUBNET. - * - * @param node Указатель на BGP_NODEINFO_Q - * @param out_subnets [out] сюда будет записан указатель на первый элемент массива - * (NULL если подсетей нет) - * @return количество подсетей (>= 0) или -1 при ошибке - */ -int get_node_v4_sockets(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SOCKET **out_sockets) { - if (!node || !out_sockets) { - return -1; - } - *out_sockets = NULL; - const struct NODEINFO *info = &node->node; - if (info->local_v4_sockets == 0) { - return 0; - } - const uint8_t *dynamic = (const uint8_t *)&node->node + sizeof(struct NODEINFO); - dynamic += info->node_name_len; - *out_sockets = (const struct NODEINFO_IPV4_SOCKET *)dynamic; - DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "get_node_v4_sockets: returned %u IPv4 sockets from node %p", - (unsigned)info->local_v4_sockets, (void*)node); - return (int)info->local_v4_sockets; -} - -int get_node_v6_sockets(struct NODEINFO_Q *node, const struct NODEINFO_IPV6_SOCKET **out_sockets) { - if (!node || !out_sockets) { - return -1; - } - *out_sockets = NULL; - const struct NODEINFO *info = &node->node; - if (info->local_v6_sockets == 0) { - return 0; - } - const uint8_t *dynamic = (const uint8_t *)&node->node + sizeof(struct NODEINFO); - dynamic += info->node_name_len; - dynamic += info->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET); - *out_sockets = (const struct NODEINFO_IPV6_SOCKET *)dynamic; - DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "get_node_v6_sockets: returned %u IPv6 sockets from node %p", - (unsigned)info->local_v6_sockets, (void*)node); - return (int)info->local_v6_sockets; -} - -int get_node_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SUBNET **out_subnets) { - if (!node || !out_subnets) { - return -1; - } - - *out_subnets = NULL; - - const struct NODEINFO *info = &node->node; - - if (info->local_v4_subnets == 0) { - return 0; // успех, но нет подсетей - } - - // Начало динамических полей сразу после фиксированной части NODEINFO - const uint8_t *dynamic = (const uint8_t *)&node->node + sizeof(struct NODEINFO); - - // 1. Пропускаем node_name - dynamic += info->node_name_len; - - // 2. Пропускаем local_v4_sockets - dynamic += info->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET); - - // 3. Пропускаем local_v6_sockets - dynamic += info->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET); - - // Теперь dynamic указывает точно на начало массива NODEINFO_IPV4_SUBNET - *out_subnets = (const struct NODEINFO_IPV4_SUBNET *)dynamic; - - DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "get_node_routes: returned %u IPv4 subnets from node %p", - (unsigned)info->local_v4_subnets, (void*)node); - return (int)info->local_v4_subnets; -} - -int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BGP* bgp) { - if (!instance || !bgp) { - DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_update_my_nodeinfo: invalid args"); - return -1; - } - size_t name_len = 0; - if (instance->name[0]) { - name_len = strlen(instance->name); - if (name_len > 63) name_len = 63; - } - int vc = 0; - struct CFG_ROUTE_ENTRY* s = instance->config->my_subnets; - while (s) { - if (s->ip.family == AF_INET) vc++; - s = s->next; - } - - int sock_count = 0; - struct ETCP_SOCKET* e_sock = instance->etcp_sockets; - while (e_sock) { - if (e_sock->local_addr.ss_family == AF_INET) sock_count++; - e_sock = e_sock->next; - } - - size_t dyn = name_len + sock_count * sizeof(struct NODEINFO_IPV4_SOCKET) + vc * sizeof(struct NODEINFO_IPV4_SUBNET); - - if (!bgp->local_node) { - bgp->local_node = u_calloc(1, sizeof(struct NODEINFO_Q) + dyn); - if (!bgp->local_node) return -1; - bgp->local_node->node.node_id = instance->node_id; - bgp->local_node->node.hop_count = 0; - bgp->local_node->node.ver = 1; - bgp->local_node->dirty = 1; - bgp->local_node->last_ver = 1; - bgp->local_node->node.local_v4_sockets = sock_count; - bgp->local_node->node.local_v4_subnets = vc; - bgp->local_node->node.node_name_len = name_len; - memcpy(bgp->local_node->node.public_key, instance->my_keys.public_key, SC_PUBKEY_SIZE); - } - - int changed = (vc != (int)bgp->local_node->node.local_v4_subnets) - || (sock_count != (int)bgp->local_node->node.local_v4_sockets) - || (name_len != bgp->local_node->node.node_name_len) - || (memcmp(bgp->local_node->node.public_key, instance->my_keys.public_key, SC_PUBKEY_SIZE) != 0); - - if (!changed && vc > 0) { - uint8_t* current = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO); - current += name_len + sock_count * sizeof(struct NODEINFO_IPV4_SOCKET); - struct NODEINFO_IPV4_SUBNET* ra = (struct NODEINFO_IPV4_SUBNET*)current; - s = instance->config->my_subnets; - bool same = true; - while (s) { - if (s->ip.family == AF_INET) { - if (memcmp(ra->addr, &s->ip.addr.v4, 4) != 0 || ra->prefix_length != s->netmask) { - same = false; - break; - } - ra++; - } - s = s->next; - } - changed = !same; - } - - if (!changed && sock_count > 0) { - uint8_t* current = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO); - current += name_len; - struct NODEINFO_IPV4_SOCKET* sa = (struct NODEINFO_IPV4_SOCKET*)current; - e_sock = instance->etcp_sockets; - bool same = true; - while (e_sock) { - 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; - uint8_t nat_addr_cmp[4] = {0}; - uint16_t nat_port_cmp = 0; - if (e_sock->nat_addr.ss_family == AF_INET) { - memcpy(nat_addr_cmp, &nat_sin->sin_addr.s_addr, 4); - nat_port_cmp = ntohs(nat_sin->sin_port); - } - if (memcmp(sa->addr, &if_sin->sin_addr.s_addr, 4) != 0 || sa->port != ntohs(if_sin->sin_port) || - memcmp(sa->nat_addr, nat_addr_cmp, 4) != 0 || sa->nat_port != nat_port_cmp || - sa->type != e_sock->nat_type || sa->id != e_sock->sock_id) { - same = false; - break; - } - sa++; - } - e_sock = e_sock->next; - } - changed = !same; - } - - if (changed) { - if (bgp->local_node) u_free(bgp->local_node); - bgp->local_node = u_calloc(1, sizeof(struct NODEINFO_Q) + dyn); - if (!bgp->local_node) return -1; - bgp->local_node->node.node_id = instance->node_id; - bgp->local_node->node.hop_count = 0; - uint8_t oldv = bgp->local_node->node.ver; - bgp->local_node->node.ver = ((oldv + 1) % 255) + 1; - bgp->local_node->node.local_v4_sockets = sock_count; - bgp->local_node->node.local_v4_subnets = vc; - bgp->local_node->node.node_name_len = name_len; - memcpy(bgp->local_node->node.public_key, instance->my_keys.public_key, SC_PUBKEY_SIZE); - bgp->local_node->dirty = 1; - bgp->local_node->last_ver = bgp->local_node->node.ver; - uint8_t* dp = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO); - if (name_len) { - memcpy(dp, instance->name, name_len); - dp += name_len; - } - struct NODEINFO_IPV4_SOCKET* sa = (struct NODEINFO_IPV4_SOCKET*)dp; - e_sock = instance->etcp_sockets; - while (e_sock) { - 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; - memcpy(sa->addr, &if_sin->sin_addr.s_addr, 4); - sa->port = ntohs(if_sin->sin_port); - sa->type = e_sock->nat_type; - sa->id = e_sock->sock_id; - if (e_sock->nat_addr.ss_family == AF_INET) { - memcpy(sa->nat_addr, &nat_sin->sin_addr.s_addr, 4); - sa->nat_port = ntohs(nat_sin->sin_port); - } else { - memset(sa->nat_addr, 0, 4); - sa->nat_port = 0; - } - DEBUG_INFO(DEBUG_CATEGORY_BGP, "NODEINFO socket: addr=0x%02x%02x%02x%02x port=%u nat=0x%02x%02x%02x%02x:%u", - sa->addr[0], sa->addr[1], sa->addr[2], sa->addr[3], sa->port, - sa->nat_addr[0], sa->nat_addr[1], sa->nat_addr[2], sa->nat_addr[3], sa->nat_port); - sa++; - } - e_sock = e_sock->next; - } - dp = (uint8_t*)sa; - struct NODEINFO_IPV4_SUBNET* ra = (struct NODEINFO_IPV4_SUBNET*)dp; - s = instance->config->my_subnets; - while (s) { - if (s->ip.family == AF_INET) { - memcpy(ra->addr, &s->ip.addr.v4, 4); - ra->prefix_length = s->netmask; - ra++; - } - s = s->next; - } - } else { - bgp->local_node->last_ver = bgp->local_node->node.ver; - } - return vc; -} +#include +#include +#include + +#include "../lib/ll_queue.h" +#include "../lib/debug_config.h" +#include "../lib/mem.h" +#include "utun_instance.h" +#include "etcp.h" +#include "config_parser.h" +#include "route_node.h" +#include "route_bgp.h" +#include "etcp_debug.h" + +static const uint8_t* node_dyn_start(const struct NODEINFO* info) { + return (const uint8_t*)info + sizeof(struct NODEINFO); +} + +static const uint8_t* skip_name(const uint8_t* p, uint8_t name_len) { + return p + name_len; +} + +static const uint8_t* skip_v4_sockets_meta(const uint8_t* p, uint8_t count) { + return p + count * sizeof(struct NODEINFO_IPV4_SOCKET_META); +} + +static const uint8_t* skip_v4_addrs(const uint8_t* p, uint8_t count) { + return p + count * sizeof(struct NODEINFO_IPV4_ADDR); +} + +static const uint8_t* skip_v6_sockets_meta(const uint8_t* p, uint8_t count) { + return p + count * sizeof(struct NODEINFO_IPV6_SOCKET_META); +} + +static const uint8_t* skip_v6_addrs(const uint8_t* p, uint8_t count) { + return p + count * sizeof(struct NODEINFO_IPV6_ADDR); +} + +int get_node_v4_sockets_meta(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SOCKET_META **out_meta) { + if (!node || !out_meta) return -1; + *out_meta = NULL; + const struct NODEINFO *info = &node->node; + if (info->local_v4_sockets == 0) return 0; + const uint8_t *dynamic = node_dyn_start(info); + dynamic = skip_name(dynamic, info->node_name_len); + *out_meta = (const struct NODEINFO_IPV4_SOCKET_META *)dynamic; + return (int)info->local_v4_sockets; +} + +int get_node_v4_addrs(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_ADDR **out_addrs) { + if (!node || !out_addrs) return -1; + *out_addrs = NULL; + const struct NODEINFO *info = &node->node; + if (info->local_v4_addrs == 0) return 0; + const uint8_t *dynamic = node_dyn_start(info); + dynamic = skip_name(dynamic, info->node_name_len); + dynamic = skip_v4_sockets_meta(dynamic, info->local_v4_sockets); + *out_addrs = (const struct NODEINFO_IPV4_ADDR *)dynamic; + return (int)info->local_v4_addrs; +} + +int get_node_v6_sockets_meta(struct NODEINFO_Q *node, const struct NODEINFO_IPV6_SOCKET_META **out_meta) { + if (!node || !out_meta) return -1; + *out_meta = NULL; + const struct NODEINFO *info = &node->node; + if (info->local_v6_sockets == 0) return 0; + const uint8_t *dynamic = node_dyn_start(info); + dynamic = skip_name(dynamic, info->node_name_len); + dynamic = skip_v4_sockets_meta(dynamic, info->local_v4_sockets); + dynamic = skip_v4_addrs(dynamic, info->local_v4_addrs); + *out_meta = (const struct NODEINFO_IPV6_SOCKET_META *)dynamic; + return (int)info->local_v6_sockets; +} + +int get_node_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SUBNET **out_subnets) { + if (!node || !out_subnets) return -1; + *out_subnets = NULL; + const struct NODEINFO *info = &node->node; + if (info->local_v4_subnets == 0) return 0; + const uint8_t *dynamic = node_dyn_start(info); + dynamic = skip_name(dynamic, info->node_name_len); + dynamic = skip_v4_sockets_meta(dynamic, info->local_v4_sockets); + dynamic = skip_v4_addrs(dynamic, info->local_v4_addrs); + dynamic = skip_v6_sockets_meta(dynamic, info->local_v6_sockets); + dynamic = skip_v6_addrs(dynamic, info->local_v6_addrs); + *out_subnets = (const struct NODEINFO_IPV4_SUBNET *)dynamic; + return (int)info->local_v4_subnets; +} + +int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BGP* bgp) { + if (!instance || !bgp) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_update_my_nodeinfo: invalid args"); + return -1; + } + size_t name_len = 0; + if (instance->name[0]) { + name_len = strlen(instance->name); + if (name_len > 63) name_len = 63; + } + int vc = 0; + struct CFG_ROUTE_ENTRY* s = instance->config->my_subnets; + while (s) { if (s->ip.family == AF_INET) vc++; s = s->next; } + + // считаем сокеты и адреса + int sock_count = 0, addr_count = 0; + struct ETCP_SOCKET* e_sock = instance->etcp_sockets; + while (e_sock) { + if (e_sock->local_addr.ss_family == AF_INET) { + sock_count++; + addr_count++; // INTERFACE + struct sockaddr_in* if_sin = (struct sockaddr_in*)&e_sock->interface_addr; + struct sockaddr_in* nat_sin = (struct sockaddr_in*)&e_sock->nat_addr; + if (e_sock->nat_addr.ss_family == AF_INET) { + uint32_t nat_ip = nat_sin->sin_addr.s_addr; + if (nat_ip != 0) { + if (e_sock->nat_type != NAT_VERIFIED_STRICT) addr_count++; + } + } + 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 = e_sock->next; + } + + size_t dyn = name_len + + sock_count * sizeof(struct NODEINFO_IPV4_SOCKET_META) + + addr_count * sizeof(struct NODEINFO_IPV4_ADDR) + + vc * sizeof(struct NODEINFO_IPV4_SUBNET); + + int changed = 1; + uint8_t old_ver = 0; + if (bgp->local_node) { + old_ver = bgp->local_node->node.ver; + changed = (vc != (int)bgp->local_node->node.local_v4_subnets) + || (sock_count != (int)bgp->local_node->node.local_v4_sockets) + || (addr_count != (int)bgp->local_node->node.local_v4_addrs) + || (name_len != bgp->local_node->node.node_name_len) + || (memcmp(bgp->local_node->node.public_key, instance->my_keys.public_key, SC_PUBKEY_SIZE) != 0); + DEBUG_INFO(DEBUG_CATEGORY_BGP, "my_nodeinfo updated: socks=%d addrs=%d subnets=%d ver=%d", + sock_count, addr_count, vc, bgp->local_node->node.ver); + } else { + bgp->local_node->last_ver = bgp->local_node->node.ver; + } + return vc; +} diff --git a/src/route_node.h b/src/route_node.h index 82fc6a9a..c7005b2b 100644 --- a/src/route_node.h +++ b/src/route_node.h @@ -10,35 +10,100 @@ struct ROUTE_BGP; struct ETCP_SOCKET; struct UTUN_INSTANCE; +// ---- типы адресов в плоском списке NODEINFO_IPV4_ADDR ---- + +#define ADDR_TYPE_INTERFACE 0 // interface_addr сокета (LAN) +#define ADDR_TYPE_NAT 1 // nat_addr после детекции NAT +#define ADDR_TYPE_REAL 2 // подтверждённый прямой интернет-адрес (nat проверка показала совпадение с interface_addr) + +// ---- типы NAT (etcp_connections.h) ---- +// NAT_TYPE_UNKNOWN(0), NAT_TYPE_EIM(1), NAT_TYPE_STRICT(2) +// NAT_VERIFIED_UNKNOWN(4), NAT_VERIFIED_EIM(5), NAT_VERIFIED_STRICT(6), NAT_VERIFIED_DIRECT(7) + +// ---- статусы зондирования связности ---- + +#define PROBE_STATUS_NONE 0 +#define PROBE_STATUS_IN_PROGRESS 1 +#define PROBE_STATUS_DONE 2 + +#define PROBE_RESULT_UNKNOWN 0 +#define PROBE_RESULT_REACHABLE 1 +#define PROBE_RESULT_UNREACHABLE 2 + +#define CONN_PROBE_COUNT 3 +#define CONN_PROBE_TIMEOUT_MS 1000 + +// ---- состояние связности с удалённым узлом (локальное, не передаётся по BGP) ---- + +struct NODE_CONNECTIVITY { + uint8_t probe_status; // PROBE_STATUS_* + uint8_t pending_count; // активных probe series + uint64_t probe_start_time; // timebase + uint16_t interface_min_rtt; // x0.1ms + uint16_t nat_min_rtt; + uint16_t real_min_rtt; + uint8_t interface_status; // PROBE_RESULT_* + uint8_t nat_status; + uint8_t real_status; + uint8_t reserved; + uint64_t interface_probe_time; + uint64_t nat_probe_time; + uint64_t real_probe_time; +}; + /** - * @brief Информация о узле + * @brief Информация о узле (передаётся по BGP) */ struct NODEINFO { uint64_t node_id; // (big-endian) uint8_t ver; // версия пакета (циклический счетчик чтобы быстро сравнивать с локальной копией - были ли обновления) uint8_t public_key[SC_PUBKEY_SIZE]; // node pubkey uint8_t node_name_len; // размер в байтах (без null терминации) - uint8_t local_v4_sockets; // NODEINFO_IPV4_SOCKET число локальных ipv4 сокетов узла (для direct incoming connections) - uint8_t local_v6_sockets; // NODEINFO_IPV6_SOCKET число локальных ipv6 сокетов узла (для direct incoming connections) (пока 0) + uint8_t local_v4_sockets; // NODEINFO_IPV4_SOCKET_META число метаданных ipv4 сокетов + uint8_t local_v4_addrs; // NODEINFO_IPV4_ADDR число типизированных ipv4 адресов узла (плоский список) + uint8_t local_v6_sockets; // NODEINFO_IPV6_SOCKET_META (пока 0) + uint8_t local_v6_addrs; // NODEINFO_IPV6_ADDR (пока 0) uint8_t local_v4_subnets; // NODEINFO_IPV4_SUBNET число локальных ipv4 подсетей узла - uint8_t local_v6_subnets; // NODEINFO_IPV6_SUBNET число локальных ipv6 подсетей узла (пока 0) - uint8_t tranzit_nodes; // NODEINFO_TRANZIT_NODE лучшие транзитные узлы для этой ноды (минимальный пинг / лучшее качество каналов. выбирается/обновляется узлом) - uint8_t hop_count; // hop list: маршрут по которому распространялся этот NODEINFO_PACKET. для избежания зацикливаний при распространении по узлам. каждый узел при передаче инкрементирует и добавляет в конец свой node_id. -// далее идут динамическип поля по порядку следования полей в этой структуре: char node_name[node_name_len], сокеты, роуты, tranzit nodes, hop list (блоки описаны структурами ниже). hop list - это массив node_id[hop_count]. + uint8_t local_v6_subnets; // NODEINFO_IPV6_SUBNET (пока 0) + uint8_t tranzit_nodes; // NODEINFO_TRANZIT_NODE лучшие транзитные узлы для этой ноды + uint8_t hop_count; // hop list: маршрут по которому распространялся этот NODEINFO_PACKET +// далее идут динамические поля по порядку: +// char node_name[node_name_len] +// NODEINFO_IPV4_SOCKET_META[local_v4_sockets] +// NODEINFO_IPV4_ADDR[local_v4_addrs] +// NODEINFO_IPV6_SOCKET_META[local_v6_sockets] +// NODEINFO_IPV6_ADDR[local_v6_addrs] +// NODEINFO_IPV4_SUBNET[local_v4_subnets] +// NODEINFO_IPV6_SUBNET[local_v6_subnets] +// NODEINFO_TRANZIT_NODE[tranzit_nodes] +// uint64_t hop_list[hop_count] } __attribute__((packed)); -struct NODEINFO_IPV4_SOCKET { - uint8_t addr[4]; // interface IP (network byte order) - uint16_t port; // interface port - uint8_t type; // CFG_SERVER_TYPE_PUBLIC/PRIVATE/NAT +// метаданные одного сокета +struct NODEINFO_IPV4_SOCKET_META { uint8_t id; // unique socket id (0-255) - uint8_t nat_addr[4]; // NAT IP (network byte order), 0 = не определён - uint16_t nat_port; // NAT port, 0 = не определён + uint8_t type; // CFG_SERVER_TYPE_*/NAT_VERIFIED_* +} __attribute__((packed)); + +// одна типизированная адресная запись (плоский список) +struct NODEINFO_IPV4_ADDR { + uint8_t addr[4]; // IP (network byte order) + uint16_t port; // port (host byte order) + uint8_t type; // ADDR_TYPE_INTERFACE/NAT/REAL + uint8_t socket_id; // к какому сокету относится +} __attribute__((packed)); + +// Пока IPv6 не используется, структуры-заглушки: +struct NODEINFO_IPV6_SOCKET_META { + uint8_t addr[16]; + uint16_t port; } __attribute__((packed)); -struct NODEINFO_IPV6_SOCKET { +struct NODEINFO_IPV6_ADDR { uint8_t addr[16]; uint16_t port; + uint8_t type; + uint8_t socket_id; } __attribute__((packed)); struct NODEINFO_IPV4_SUBNET { @@ -61,60 +126,42 @@ struct NODEINFO_PATH { struct ll_entry ll; struct ETCP_CONN* conn; uint8_t hop_count; // hop list: маршрут этого path -};// __attribute__((packed)); +}; struct NODEINFO_Q { struct ll_entry ll; - struct ll_queue* paths; // сюда помещаем struct NODEINFO_PATH + struct ll_queue* paths; // сюда помещаем struct NODEINFO_PATH uint8_t dirty; uint8_t last_ver; - uint64_t last_ping_time; // время последнего замера в 0.1ms - uint16_t last_rtt; // лучший RTT в 0.1ms + struct NODE_CONNECTIVITY connectivity; // состояние связности (локальное) struct ETCP_SOCKET* best_socket; struct NODEINFO node; // Всегда в конце структуры - динамически расширяемый блок -};// __attribute__((packed)); +}; /** * @brief Создаёт/обновляет nodeinfo для собственного узла - * - * Собирает данные из локальных структур и упаковывает к структуру (оптимизированную для передачи по сети) - * - * @param instance Указатель на UTUN_INSTANCE (с него сбоираем все данные) - * @param bgp Указатель на ROUTE_BGP (для доступа к my_nodeinfo и instance) - * @return количество подсетей (>= 0) или -1 при ошибке */ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BGP* bgp); /** * @brief Получает указатель на массив IPv4-подсетей узла (без malloc/копирования). - * - * Функция вычисляет смещение внутри динамической части NODEINFO_Q - * и возвращает прямой указатель на массив struct NODEINFO_IPV4_SUBNET. - * - * @param node Указатель на NODEINFO_Q - * @param out_subnets [out] сюда будет записан указатель на первый элемент массива - * (NULL если подсетей нет) - * @return количество подсетей (>= 0) или -1 при ошибке */ int get_node_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SUBNET **out_subnets); /** - * @brief Получает указатель на массив IPv4-сокетов узла. - * - * @param node Указатель на NODEINFO_Q - * @param out_sockets [out] указатель на первый элемент массива NODEINFO_IPV4_SOCKET - * @return количество сокетов (>= 0) или -1 при ошибке + * @brief Получает указатель на массив метаданных IPv4-сокетов узла. + */ +int get_node_v4_sockets_meta(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SOCKET_META **out_meta); + +/** + * @brief Получает указатель на плоский список IPv4-адресов узла. */ -int get_node_v4_sockets(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SOCKET **out_sockets); +int get_node_v4_addrs(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_ADDR **out_addrs); /** - * @brief Получает указатель на массив IPv6-сокетов узла. - * - * @param node Указатель на NODEINFO_Q - * @param out_sockets [out] указатель на первый элемент массива NODEINFO_IPV6_SOCKET - * @return количество сокетов (>= 0) или -1 при ошибке + * @brief Получает указатель на массив метаданных IPv6-сокетов узла. */ -int get_node_v6_sockets(struct NODEINFO_Q *node, const struct NODEINFO_IPV6_SOCKET **out_sockets); +int get_node_v6_sockets_meta(struct NODEINFO_Q *node, const struct NODEINFO_IPV6_SOCKET_META **out_meta); #endif // ROUTE_NODE_H diff --git a/src/route_ping.c b/src/route_ping.c index e92ab904..cfd5de8b 100644 --- a/src/route_ping.c +++ b/src/route_ping.c @@ -340,13 +340,11 @@ void route_ping_handle_req(struct ROUTE_BGP* bgp, if (sin->sin_addr.s_addr == 0 && sin->sin_port == 0) { struct NODEINFO_Q* nq = route_bgp_get_node(bgp, req_pkt->node_id); if (nq) { - const struct NODEINFO_IPV4_SOCKET* sockets; - int count = get_node_v4_sockets(nq, &sockets); + const struct NODEINFO_IPV4_ADDR* addrs; + int count = get_node_v4_addrs(nq, &addrs); if (count > 0) { - memcpy(&sin->sin_addr.s_addr, sockets[0].addr, 4); - sin->sin_port = sockets[0].port; - DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "resolved target from nodeinfo: %s:%u", - ip_to_str(&sin->sin_addr, AF_INET).str, ntohs(sin->sin_port)); + memcpy(&sin->sin_addr.s_addr, addrs[0].addr, 4); + sin->sin_port = addrs[0].port; } } } diff --git a/tests/Makefile.am b/tests/Makefile.am index d230c22d..4bd2cc4c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -89,6 +89,7 @@ ETCP_FULL_OBJS = \ $(top_builddir)/src/utun-route_bgp.o \ $(top_builddir)/src/utun-route_ping.o \ $(top_builddir)/src/utun-route_node.o \ + $(top_builddir)/src/utun-route_connectivity.o \ $(top_builddir)/src/utun-routing.o \ $(top_builddir)/src/utun-tun_if.o \ $(top_builddir)/src/utun-tun_route.o \ diff --git a/tests/test_nat_detection.c b/tests/test_nat_detection.c index 7dabc8cb..05a16d23 100644 --- a/tests/test_nat_detection.c +++ b/tests/test_nat_detection.c @@ -278,14 +278,14 @@ int main(void) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "Waiting for NAT detection to complete for C1..."); bgp_wait_cycles = 0; while (!test_timed_out && bgp_wait_cycles < 1500) { - if (link_sc1->nat_check_status == NAT_CHECK_OPEN) { - DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT detection completed for C1: OPEN"); + if (link_sc1->nat_check_status == NAT_CHECK_EIM) { + DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT detection completed for C1: EIM"); break; } uasync_poll(ua, 10); bgp_wait_cycles++; } - if (!link_sc1 || link_sc1->nat_check_status != NAT_CHECK_OPEN) { + if (!link_sc1 || link_sc1->nat_check_status != NAT_CHECK_EIM) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NAT detection did not complete for C1"); goto cleanup; } @@ -297,7 +297,7 @@ int main(void) { struct ETCP_SOCKET* sock = inst_c1->etcp_sockets; int found = 0; while (sock) { - if (sock->nat_type == NAT_VERIFIED_OPEN) { found = 1; break; } + if (sock->nat_type == NAT_VERIFIED_EIM) { found = 1; break; } sock = sock->next; } if (found) { @@ -318,14 +318,14 @@ int main(void) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO_Q for C1 disappeared"); goto cleanup; } - if (link_sc1->nat_check_status != NAT_CHECK_OPEN) { - DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: nat_check_status=%d, expected OPEN(%d)", - (int)link_sc1->nat_check_status, NAT_CHECK_OPEN); + if (link_sc1->nat_check_status != NAT_CHECK_EIM) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: nat_check_status=%d, expected EIM(%d)", + (int)link_sc1->nat_check_status, NAT_CHECK_EIM); goto cleanup; } - if (link_sc1->nat_type != NAT_TYPE_OPEN) { - DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: nat_type=%d, expected OPEN(%d)", - (int)link_sc1->nat_type, NAT_TYPE_OPEN); + if (link_sc1->nat_type != NAT_TYPE_EIM) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: nat_type=%d, expected EIM(%d)", + (int)link_sc1->nat_type, NAT_TYPE_EIM); goto cleanup; } if (link_sc1->nat_ip == 0 || link_sc1->nat_port == 0) { @@ -340,8 +340,8 @@ int main(void) { if (conn_sc1->peer_node_id == NODE_ID_C1) break; conn_sc1 = conn_sc1->next; } - if (!link_sc1 || link_sc1->nat_type != NAT_TYPE_OPEN) { - DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: link nat_type not OPEN on server"); + if (!link_sc1 || link_sc1->nat_type != NAT_TYPE_EIM) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: link nat_type not EIM on server"); goto cleanup; } @@ -349,32 +349,27 @@ int main(void) { struct ETCP_SOCKET* sock_c1 = NULL; struct ETCP_SOCKET* sock = inst_c1->etcp_sockets; while (sock) { - if (sock->nat_type == NAT_VERIFIED_OPEN) { + if (sock->nat_type == NAT_VERIFIED_EIM) { sock_c1 = sock; break; } sock = sock->next; } if (!sock_c1) { - DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: no socket with NAT_VERIFIED_OPEN on C1"); + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: no socket with NAT_VERIFIED_EIM on C1"); goto cleanup; } // Verify local_node socket update on C1 (received NAT_INFO updated nodeinfo) if (inst_c1->bgp && inst_c1->bgp->local_node) { - const struct NODEINFO_IPV4_SOCKET* sockets = NULL; - int sock_count = get_node_v4_sockets(inst_c1->bgp->local_node, &sockets); + const struct NODEINFO_IPV4_SOCKET_META* meta = NULL; + int meta_count = get_node_v4_sockets_meta(inst_c1->bgp->local_node, &meta); int found = 0; - for (int i = 0; i < sock_count; i++) { - if (sockets[i].id == sock_c1->sock_id) { - uint32_t s_ip = (sockets[i].nat_addr[0] << 24) | (sockets[i].nat_addr[1] << 16) | - (sockets[i].nat_addr[2] << 8) | sockets[i].nat_addr[3]; - if (s_ip != link_sc1->nat_ip || sockets[i].nat_port != link_sc1->nat_port || - sockets[i].type != NAT_VERIFIED_OPEN) { - DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: local_node socket not updated on C1: " - "s_ip=0x%08x nat_ip=0x%08x s_port=%u nat_port=%u s_type=%u expected=%u", - s_ip, link_sc1->nat_ip, sockets[i].nat_port, link_sc1->nat_port, - sockets[i].type, NAT_VERIFIED_OPEN); + for (int i = 0; i < meta_count; i++) { + if (meta[i].id == sock_c1->sock_id) { + if (meta[i].type != NAT_VERIFIED_EIM) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: local_node socket meta not updated on C1: type=%u expected=%u", + meta[i].type, NAT_VERIFIED_EIM); goto cleanup; } found = 1; @@ -382,10 +377,30 @@ int main(void) { } } if (!found) { - DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: socket not found in local_node on C1"); + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: socket not found in local_node meta on C1"); goto cleanup; } - DEBUG_INFO(DEBUG_CATEGORY_BGP, "local_node socket update check PASSED on C1"); + // Verify NAT addr in flat address list + const struct NODEINFO_IPV4_ADDR* addrs = NULL; + int addr_count = get_node_v4_addrs(inst_c1->bgp->local_node, &addrs); + int nat_found = 0; + for (int i = 0; i < addr_count; i++) { + if (addrs[i].type == ADDR_TYPE_NAT && addrs[i].socket_id == sock_c1->sock_id) { + uint32_t addr_ip; memcpy(&addr_ip, addrs[i].addr, 4); + if (addr_ip != link_sc1->nat_ip || addrs[i].port != link_sc1->nat_port) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: NAT addr mismatch in local_node: addr=%08x nat_ip=%08x port=%u nat_port=%u", + addr_ip, link_sc1->nat_ip, addrs[i].port, link_sc1->nat_port); + goto cleanup; + } + nat_found = 1; + break; + } + } + if (!nat_found) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: NAT addr not found in local_node flat addr list on C1"); + goto cleanup; + } + DEBUG_INFO(DEBUG_CATEGORY_BGP, "local_node meta+addr check PASSED on C1"); } // Wait for C2 to learn updated C1 nodeinfo (with verified NAT type) @@ -395,10 +410,10 @@ int main(void) { while (!test_timed_out && bgp_wait_cycles < 500) { struct NODEINFO_Q* node_c1_on_c2 = inst_c2->bgp ? route_bgp_get_node(inst_c2->bgp, NODE_ID_C1) : NULL; if (node_c1_on_c2) { - const struct NODEINFO_IPV4_SOCKET* sockets = NULL; - int sock_count = get_node_v4_sockets(node_c1_on_c2, &sockets); - for (int i = 0; i < sock_count; i++) { - if (sockets[i].type == NAT_VERIFIED_OPEN) { + const struct NODEINFO_IPV4_SOCKET_META* meta = NULL; + int meta_count = get_node_v4_sockets_meta(node_c1_on_c2, &meta); + for (int i = 0; i < meta_count; i++) { + if (meta[i].type == NAT_VERIFIED_EIM) { c2_verified_nat = 1; break; } diff --git a/tests/test_route_ping.c b/tests/test_route_ping.c index 6b1fc599..193101ed 100644 --- a/tests/test_route_ping.c +++ b/tests/test_route_ping.c @@ -273,19 +273,11 @@ int main(void) { uint16_t target_port = 0; struct NODEINFO_Q* nq = inst_b->bgp ? route_bgp_get_node(inst_b->bgp, NODE_ID_C) : NULL; if (nq) { - const struct NODEINFO_IPV4_SOCKET* sockets; - int sc = get_node_v4_sockets(nq, &sockets); + const struct NODEINFO_IPV4_ADDR* addrs; + int sc = get_node_v4_addrs(nq, &addrs); if (sc > 0) { - /* Используем NAT адрес если есть, иначе interface адрес */ - uint32_t nat_ip = (sockets[0].nat_addr[0] << 24) | (sockets[0].nat_addr[1] << 16) | - (sockets[0].nat_addr[2] << 8) | sockets[0].nat_addr[3]; - if (nat_ip != 0 && sockets[0].nat_port != 0) { - memcpy(&target_ip, sockets[0].nat_addr, 4); /* network byte order */ - target_port = sockets[0].nat_port; /* уже в host order */ - } else { - memcpy(&target_ip, sockets[0].addr, 4); /* network byte order */ - target_port = sockets[0].port; /* уже в host order */ - } + memcpy(&target_ip, addrs[0].addr, 4); /* network byte order */ + target_port = addrs[0].port; /* host byte order */ } } if (target_ip == 0 || target_port == 0) { diff --git a/tools/etcpmon/etcpmon_gui.c b/tools/etcpmon/etcpmon_gui.c index 60d99b33..274fa1c0 100644 --- a/tools/etcpmon/etcpmon_gui.c +++ b/tools/etcpmon/etcpmon_gui.c @@ -1211,14 +1211,16 @@ void etcpmon_gui_update_conn_list(struct etcpmon_app* app, static const char* nat_type_to_string(uint8_t nat_type) { switch (nat_type) { - case NAT_TYPE_OPEN: return "Open"; - case NAT_TYPE_RESTRICTED: return "Restricted"; - case NAT_VERIFIED_OPEN: return "Verified-Open"; - case NAT_VERIFIED_RESTRICTED: return "Verified-Restricted"; - case NAT_VERIFIED_DIRECT: return "Direct"; - default: return "Unknown"; + case NAT_TYPE_EIM: return "EIM"; + case NAT_TYPE_STRICT: return "Strict"; + case NAT_VERIFIED_EIM: return "Verified-EIM"; + case NAT_VERIFIED_STRICT: return "Verified-Strict"; + case NAT_VERIFIED_DIRECT: return "Verified-Direct"; + case NAT_TYPE_UNKNOWN: return "Unknown"; + default: return "?"; } } +} void etcpmon_gui_update_socket_list(struct etcpmon_app* app, struct etcpmon_socket_info* list, diff --git a/tools/etcpmon/etcpmon_protocol.h b/tools/etcpmon/etcpmon_protocol.h index 85e3fc32..08be86b8 100644 --- a/tools/etcpmon/etcpmon_protocol.h +++ b/tools/etcpmon/etcpmon_protocol.h @@ -25,11 +25,11 @@ extern "C" { /* NAT types (mirrored from etcp_connections.h for monitor protocol) */ #define NAT_TYPE_UNKNOWN 0 -#define NAT_TYPE_OPEN 1 -#define NAT_TYPE_RESTRICTED 2 +#define NAT_TYPE_EIM 1 /* Endpoint-Independent Mapping */ +#define NAT_TYPE_STRICT 2 /* Address/Restricted or Symmetric */ #define NAT_VERIFIED_UNKNOWN 4 -#define NAT_VERIFIED_OPEN 5 -#define NAT_VERIFIED_RESTRICTED 6 +#define NAT_VERIFIED_EIM 5 /* EIM NAT */ +#define NAT_VERIFIED_STRICT 6 /* strict NAT */ #define NAT_VERIFIED_DIRECT 7 #define ETCPMON_MAX_MSG_SIZE 4096 #define ETCPMON_MAX_CONN_NAME 32