Browse Source

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
congestion
Evgeny 5 months ago
parent
commit
9c509d4c86
  1. 1
      src/Makefile.am
  2. 12
      src/etcp_connections.h
  3. 204
      src/route_bgp.c
  4. 2
      src/route_bgp.h
  5. 348
      src/route_connectivity.c
  6. 20
      src/route_connectivity.h
  7. 266
      src/route_node.c
  8. 135
      src/route_node.h
  9. 10
      src/route_ping.c
  10. 1
      tests/Makefile.am
  11. 79
      tests/test_nat_detection.c
  12. 16
      tests/test_route_ping.c
  13. 14
      tools/etcpmon/etcpmon_gui.c
  14. 8
      tools/etcpmon/etcpmon_protocol.h

1
src/Makefile.am

@ -10,6 +10,7 @@ utun_CORE_SOURCES = \
route_bgp.c \ route_bgp.c \
route_ping.c \ route_ping.c \
route_node.c \ route_node.c \
route_connectivity.c \
routing.c \ routing.c \
tun_if.c \ tun_if.c \
tun_route.c \ tun_route.c \

12
src/etcp_connections.h

@ -89,19 +89,19 @@ struct ETCP_SOCKET {
#define NAT_CHECK_NONE 0 #define NAT_CHECK_NONE 0
#define NAT_CHECK_WAITING 1 #define NAT_CHECK_WAITING 1
#define NAT_CHECK_IN_PROGRESS 2 #define NAT_CHECK_IN_PROGRESS 2
#define NAT_CHECK_OPEN 3 #define NAT_CHECK_EIM 3
#define NAT_CHECK_RESTRICTED 4 #define NAT_CHECK_STRICT 4
// NAT type (detected by server during NAT check) // NAT type (detected by server during NAT check)
#define NAT_TYPE_UNKNOWN 0 #define NAT_TYPE_UNKNOWN 0
#define NAT_TYPE_OPEN 1 #define NAT_TYPE_EIM 1 // Endpoint-Independent Mapping
#define NAT_TYPE_RESTRICTED 2 #define NAT_TYPE_STRICT 2 // Address/Restricted or Symmetric
// Verified NAT types (published in nodeinfo after server-side NAT detection) // Verified NAT types (published in nodeinfo after server-side NAT detection)
// Values above CFG_SERVER_TYPE_PRIVATE to avoid collision with config types // Values above CFG_SERVER_TYPE_PRIVATE to avoid collision with config types
#define NAT_VERIFIED_UNKNOWN 4 // detection failed or inconclusive #define NAT_VERIFIED_UNKNOWN 4 // detection failed or inconclusive
#define NAT_VERIFIED_OPEN 5 // open NAT #define NAT_VERIFIED_EIM 5 // EIM NAT
#define NAT_VERIFIED_RESTRICTED 6 // restricted NAT #define NAT_VERIFIED_STRICT 6 // strict NAT
#define NAT_VERIFIED_DIRECT 7 // real public IP, no NAT #define NAT_VERIFIED_DIRECT 7 // real public IP, no NAT

204
src/route_bgp.c

@ -18,6 +18,7 @@
#include "route_lib.h" #include "route_lib.h"
#include "route_bgp.h" #include "route_bgp.h"
#include "route_ping.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)) { if (nl > 0 && nl < 63 && off + nl < len - sizeof(struct BGP_NODEINFO_PACKET)) {
memcpy(name_buf, dyn + off, nl); off += nl; memcpy(name_buf, dyn + off, nl); off += nl;
} }
off += ni->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET); off += ni->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET_META);
off += ni->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET); 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}; char subs_buf[512] = {0};
if (ni->local_v4_subnets > 0) { if (ni->local_v4_subnets > 0) {
const struct NODEINFO_IPV4_SUBNET* subs = (const struct NODEINFO_IPV4_SUBNET*)(dyn + off); 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); 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); char* buf = u_malloc(need+1);
if (!buf) return NULL; 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; return buf;
} }
@ -284,6 +287,8 @@ void route_bgp_destroy(struct UTUN_INSTANCE* instance) {
etcp_unbind(instance, ETCP_ID_ROUTE_ENTRY); etcp_unbind(instance, ETCP_ID_ROUTE_ENTRY);
route_connectivity_cancel_all(instance);
// route_ping_destroy_pending(instance->bgp); // route_ping_destroy_pending(instance->bgp);
struct ll_entry* e; struct ll_entry* e;
@ -395,7 +400,6 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
struct ROUTE_TABLE* rt = conn->instance->rt; struct ROUTE_TABLE* rt = conn->instance->rt;
// Remove this connection from all nodes' path lists // Remove this connection from all nodes' path lists
// and send WITHDRAW if a node becomes unreachable
bool need_withdraw = false; bool need_withdraw = false;
struct ll_entry* node_entry = bgp->nodes ? bgp->nodes->head : NULL; struct ll_entry* node_entry = bgp->nodes ? bgp->nodes->head : NULL;
while (node_entry) { 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; struct NODEINFO_Q* nq = (struct NODEINFO_Q*)node_entry;
if (route_bgp_remove_path(nq, conn) == 1) { if (route_bgp_remove_path(nq, conn) == 1) {
need_withdraw = true; need_withdraw = true;
if (rt) { if (rt) route_delete(rt, nq);
route_delete(rt, nq);
}
nq->dirty = 1; nq->dirty = 1;
if (nq->paths) { route_connectivity_cancel_node(conn->instance, nq);
queue_free(nq->paths); if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; }
nq->paths = NULL;
}
uint64_t key = nq->node.node_id; uint64_t key = nq->node.node_id;
struct ll_entry* entry = node_entry; struct ll_entry* entry = node_entry;
if (entry) { if (entry) { queue_remove_data(bgp->nodes, entry); queue_entry_free(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); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removed node %016llx after link down", (unsigned long long)key);
} }
node_entry = next; 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) { int nodeinfo_dyn_size(struct NODEINFO* node) {
return node->node_name_len + return node->node_name_len +
node->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET) + node->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET_META) +
node->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET) + 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_v4_subnets * sizeof(struct NODEINFO_IPV4_SUBNET) +
node->local_v6_subnets * sizeof(struct NODEINFO_IPV6_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; 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); u_free(na);
return; 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_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) // Send NAT_INFO to peer (via their connection)
if (link->etcp) { if (link->etcp) {
uint8_t socket_id = link->remote_socket_id; uint8_t socket_id = link->remote_socket_id;
@ -716,16 +715,21 @@ int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, co
from->log_name, new_ver, nodeinfo1->last_ver); from->log_name, new_ver, nodeinfo1->last_ver);
return 0; return 0;
} }
int new_data_size=sizeof(struct NODEINFO_Q) - sizeof(struct ll_entry) + dyn_size + 8; int new_data_size=sizeof(struct NODEINFO_Q) - sizeof(struct ll_entry) + dyn_size + 8;
struct ll_queue* paths=NULL; struct ll_queue* paths=NULL;
int need_alloc=0; int need_alloc=0;
if (nodeinfo1) {// remove old node int is_new_node = 0;
int socks_changed = 0;
if (nodeinfo1) {
paths=nodeinfo1->paths; paths=nodeinfo1->paths;
if (nodeinfo1->ll.size < new_data_size) { 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; need_alloc=1;
is_new_node = 1;
} }
} else need_alloc=1;
if (need_alloc) { if (need_alloc) {
if (nodeinfo1) { if (nodeinfo1) {
@ -735,20 +739,28 @@ int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, co
nodeinfo1 = (struct NODEINFO_Q*)queue_entry_new(new_data_size); nodeinfo1 = (struct NODEINFO_Q*)queue_entry_new(new_data_size);
paths = queue_new(bgp->instance->ua, 0, "node_paths"); paths = queue_new(bgp->instance->ua, 0, "node_paths");
memcpy(&nodeinfo1->node, ni, sizeof(struct NODEINFO) + dyn_size); 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); 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), обновляем его данные */ /* Если это наш local_node (по node_id), обновляем его данные */
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Checking local_node update: node_id=%016llx my_id=%016llx local_node=%p", 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); (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) { 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_src = (uint8_t*)&nodeinfo1->node + sizeof(struct NODEINFO);
uint8_t* dyn_dst = (uint8_t*)&bgp->local_node->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 + int dyn_sz = nodeinfo1->node.node_name_len +
nodeinfo1->node.local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET) + nodeinfo1->node.local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET_META) +
nodeinfo1->node.local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET) + 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_v4_subnets * sizeof(struct NODEINFO_IPV4_SUBNET) +
nodeinfo1->node.local_v6_subnets * sizeof(struct NODEINFO_IPV6_SUBNET); nodeinfo1->node.local_v6_subnets * sizeof(struct NODEINFO_IPV6_SUBNET);
memcpy(dyn_dst, dyn_src, dyn_sz); 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; uint64_t id=item->conn->peer_node_id;
int found=0; int found=0;
for (int i=0; i<hop_count; i++) if (hop_list[i]==id) found=1; for (int i=0; i<hop_count; i++) if (hop_list[i]==id) found=1;
if (found==0) { if (found==0) route_bgp_send_nodeinfo(nodeinfo1, item->conn);
route_bgp_send_nodeinfo(nodeinfo1, item->conn);
}
else DEBUG_INFO(DEBUG_CATEGORY_BGP, "Skip send NODEINFO to node %016llx",id); else DEBUG_INFO(DEBUG_CATEGORY_BGP, "Skip send NODEINFO to node %016llx",id);
} }
e = e->next; 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, 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; 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); 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 (ret>0 || !nq->paths || (nq->paths && queue_entry_count(nq->paths) == 0)) {
if (bgp->instance && bgp->instance->rt) { if (bgp->instance && bgp->instance->rt) route_delete(bgp->instance->rt, nq);
route_delete(bgp->instance->rt, nq);
}
nq->dirty = 1; nq->dirty = 1;
if (nq->paths) { route_connectivity_cancel_node(bgp->instance, nq);
queue_free(nq->paths); if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; }
nq->paths = NULL;
}
uint64_t key = node_id; uint64_t key = node_id;
struct ll_entry* entry = queue_find_data_by_index(bgp->nodes, &key, 8); struct ll_entry* entry = queue_find_data_by_index(bgp->nodes, &key, 8);
if (entry) { if (entry) { queue_remove_data(bgp->nodes, entry); queue_entry_free(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); 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); 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; 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; 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) | uint32_t nat_ip = (info->nat_ip[0] << 24) | (info->nat_ip[1] << 16) |
(info->nat_ip[2] << 8) | info->nat_ip[3]; (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; 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], 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); uint8_t* dynamic = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO);
dynamic += bgp->local_node->node.node_name_len; dynamic += bgp->local_node->node.node_name_len;
struct NODEINFO_IPV4_SOCKET* sockets = (struct NODEINFO_IPV4_SOCKET*)dynamic; struct NODEINFO_IPV4_SOCKET_META* meta = (struct NODEINFO_IPV4_SOCKET_META*)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);
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; uint8_t verified_type;
if (info->nat_type == NAT_TYPE_OPEN) { if (info->nat_type == NAT_TYPE_EIM) verified_type = NAT_VERIFIED_EIM;
verified_type = NAT_VERIFIED_OPEN; else if (info->nat_type == NAT_TYPE_STRICT) verified_type = NAT_VERIFIED_STRICT;
} else if (info->nat_type == NAT_TYPE_RESTRICTED) { else verified_type = NAT_VERIFIED_UNKNOWN;
verified_type = NAT_VERIFIED_RESTRICTED;
} else { int data_changed = 0;
verified_type = NAT_VERIFIED_UNKNOWN; for (int i = 0; i < bgp->local_node->node.local_v4_sockets; i++) {
if (meta[i].id == socket_id) {
if (meta[i].type != verified_type) {
meta[i].type = verified_type;
data_changed = 1;
}
break;
}
} }
sockets[i].type = verified_type;
// Обновить ETCP_SOCKET
struct ETCP_SOCKET* es = bgp->instance->etcp_sockets; struct ETCP_SOCKET* es = bgp->instance->etcp_sockets;
while (es) { while (es) {
if (es->sock_id == socket_id) { 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; 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; break;
} }
es = es->next; es = es->next;
} }
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", // Обновить local_node и разослать если изменилось
socket_id, old_ip, nat_ip, old_port, nat_port, old_type, verified_type, if (data_changed) {
(old_ip != nat_ip || old_port != nat_port || old_type != verified_type) ? 1 : 0); int prev_v4_addrs = bgp->local_node->node.local_v4_addrs;
if (old_ip != nat_ip || old_port != nat_port || old_type != verified_type) { 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->dirty = 1;
bgp->local_node->node.ver = (bgp->local_node->node.ver + 1) % 255 + 1; bgp->local_node->node.ver = (bgp->local_node->node.ver % 255) + 1;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO triggered NODEINFO send, ver=%d senders=%d", bgp->local_node->last_ver = bgp->local_node->node.ver;
bgp->local_node->node.ver, }
bgp->senders_list ? queue_entry_count(bgp->senders_list) : 0); if (bgp->local_node->dirty && bgp->senders_list) {
if (bgp->senders_list) {
struct ll_entry* se = bgp->senders_list->head; struct ll_entry* se = bgp->senders_list->head;
while (se) { while (se) {
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)se->data; struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)se->data;
if (item && item->conn) { if (item && item->conn) route_bgp_send_nodeinfo(bgp->local_node, 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; se = se->next;
} }
} }
} }
break;
}
}
// NAT type for the link
struct ETCP_LINK* l = from_conn->links; struct ETCP_LINK* l = from_conn->links;
while (l) { while (l) { l->nat_type = info->nat_type; l = l->next; }
l->nat_type = info->nat_type;
l = l->next;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO from %s: socket_id=%u type=%s ip=%u.%u.%u.%u %s port=%u", DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO from %s: socket_id=%u type=%s ip=%s port=%u",
from_conn->log_name, socket_id, from_conn->log_name, socket_id,
info->nat_type == NAT_TYPE_OPEN ? "OPEN" : "RESTRICTED", info->nat_type == NAT_TYPE_EIM ? "EIM" : "STRICT",
(info->nat_ip[0]), (info->nat_ip[1]), ip_to_str(info->nat_ip, AF_INET).str, (unsigned)nat_port);
(info->nat_ip[2]), (info->nat_ip[3]), 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 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); 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", 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, 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); ip_to_str(pkt->nat_ip, AF_INET).str, (unsigned)nat_port);
etcp_send(conn, e); etcp_send(conn, e);
} }

2
src/route_bgp.h

@ -189,7 +189,7 @@ int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn);
* @param conn соединение к клиенту * @param conn соединение к клиенту
* @param nat_ip IP клиента (network byte order) * @param nat_ip IP клиента (network byte order)
* @param nat_port порт клиента (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); 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);

348
src/route_connectivity.c

@ -0,0 +1,348 @@
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <arpa/inet.h>
#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*)&target;
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;
}
}

20
src/route_connectivity.h

@ -0,0 +1,20 @@
#ifndef ROUTE_CONNECTIVITY_H
#define ROUTE_CONNECTIVITY_H
#include <stdint.h>
#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

266
src/route_node.c

@ -12,84 +12,78 @@
#include "route_bgp.h" #include "route_bgp.h"
#include "etcp_debug.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) {
* @brief Получает указатель на массив IPv4-подсетей узла (без malloc/копирования). return p + count * sizeof(struct NODEINFO_IPV4_SOCKET_META);
* }
* Функция вычисляет смещение внутри динамической части BGP_NODEINFO_Q
* и возвращает прямой указатель на массив struct BGP_NODEINFO_IPV4_SUBNET. static const uint8_t* skip_v4_addrs(const uint8_t* p, uint8_t count) {
* return p + count * sizeof(struct NODEINFO_IPV4_ADDR);
* @param node Указатель на BGP_NODEINFO_Q }
* @param out_subnets [out] сюда будет записан указатель на первый элемент массива
* (NULL если подсетей нет) static const uint8_t* skip_v6_sockets_meta(const uint8_t* p, uint8_t count) {
* @return количество подсетей (>= 0) или -1 при ошибке return p + count * sizeof(struct NODEINFO_IPV6_SOCKET_META);
*/ }
int get_node_v4_sockets(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SOCKET **out_sockets) {
if (!node || !out_sockets) { static const uint8_t* skip_v6_addrs(const uint8_t* p, uint8_t count) {
return -1; return p + count * sizeof(struct NODEINFO_IPV6_ADDR);
} }
*out_sockets = NULL;
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; const struct NODEINFO *info = &node->node;
if (info->local_v4_sockets == 0) { if (info->local_v4_sockets == 0) return 0;
return 0; const uint8_t *dynamic = node_dyn_start(info);
} dynamic = skip_name(dynamic, info->node_name_len);
const uint8_t *dynamic = (const uint8_t *)&node->node + sizeof(struct NODEINFO); *out_meta = (const struct NODEINFO_IPV4_SOCKET_META *)dynamic;
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; return (int)info->local_v4_sockets;
} }
int get_node_v6_sockets(struct NODEINFO_Q *node, const struct NODEINFO_IPV6_SOCKET **out_sockets) { int get_node_v4_addrs(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_ADDR **out_addrs) {
if (!node || !out_sockets) { if (!node || !out_addrs) return -1;
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;
} }
*out_sockets = NULL;
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; const struct NODEINFO *info = &node->node;
if (info->local_v6_sockets == 0) { if (info->local_v6_sockets == 0) return 0;
return 0; const uint8_t *dynamic = node_dyn_start(info);
} dynamic = skip_name(dynamic, info->node_name_len);
const uint8_t *dynamic = (const uint8_t *)&node->node + sizeof(struct NODEINFO); dynamic = skip_v4_sockets_meta(dynamic, info->local_v4_sockets);
dynamic += info->node_name_len; dynamic = skip_v4_addrs(dynamic, info->local_v4_addrs);
dynamic += info->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET); *out_meta = (const struct NODEINFO_IPV6_SOCKET_META *)dynamic;
*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; return (int)info->local_v6_sockets;
} }
int get_node_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SUBNET **out_subnets) { int get_node_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SUBNET **out_subnets) {
if (!node || !out_subnets) { if (!node || !out_subnets) return -1;
return -1;
}
*out_subnets = NULL; *out_subnets = NULL;
const struct NODEINFO *info = &node->node; const struct NODEINFO *info = &node->node;
if (info->local_v4_subnets == 0) return 0;
if (info->local_v4_subnets == 0) { const uint8_t *dynamic = node_dyn_start(info);
return 0; // успех, но нет подсетей 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);
// Начало динамических полей сразу после фиксированной части NODEINFO dynamic = skip_v6_sockets_meta(dynamic, info->local_v6_sockets);
const uint8_t *dynamic = (const uint8_t *)&node->node + sizeof(struct NODEINFO); dynamic = skip_v6_addrs(dynamic, info->local_v6_addrs);
// 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; *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; return (int)info->local_v4_subnets;
} }
@ -105,141 +99,45 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG
} }
int vc = 0; int vc = 0;
struct CFG_ROUTE_ENTRY* s = instance->config->my_subnets; struct CFG_ROUTE_ENTRY* s = instance->config->my_subnets;
while (s) { while (s) { if (s->ip.family == AF_INET) vc++; s = s->next; }
if (s->ip.family == AF_INET) vc++;
s = s->next;
}
int sock_count = 0; // считаем сокеты и адреса
int sock_count = 0, addr_count = 0;
struct ETCP_SOCKET* e_sock = instance->etcp_sockets; 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) { while (e_sock) {
if (e_sock->local_addr.ss_family == AF_INET) { 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* if_sin = (struct sockaddr_in*)&e_sock->interface_addr;
struct sockaddr_in* nat_sin = (struct sockaddr_in*)&e_sock->nat_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) { if (e_sock->nat_addr.ss_family == AF_INET) {
memcpy(nat_addr_cmp, &nat_sin->sin_addr.s_addr, 4); uint32_t nat_ip = nat_sin->sin_addr.s_addr;
nat_port_cmp = ntohs(nat_sin->sin_port); if (nat_ip != 0) {
if (e_sock->nat_type != NAT_VERIFIED_STRICT) addr_count++;
} }
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++; 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; e_sock = e_sock->next;
} }
changed = !same;
}
if (changed) { size_t dyn = name_len
if (bgp->local_node) u_free(bgp->local_node); + sock_count * sizeof(struct NODEINFO_IPV4_SOCKET_META)
bgp->local_node = u_calloc(1, sizeof(struct NODEINFO_Q) + dyn); + addr_count * sizeof(struct NODEINFO_IPV4_ADDR)
if (!bgp->local_node) return -1; + vc * sizeof(struct NODEINFO_IPV4_SUBNET);
bgp->local_node->node.node_id = instance->node_id;
bgp->local_node->node.hop_count = 0; int changed = 1;
uint8_t oldv = bgp->local_node->node.ver; uint8_t old_ver = 0;
bgp->local_node->node.ver = ((oldv + 1) % 255) + 1; if (bgp->local_node) {
bgp->local_node->node.local_v4_sockets = sock_count; old_ver = bgp->local_node->node.ver;
bgp->local_node->node.local_v4_subnets = vc; changed = (vc != (int)bgp->local_node->node.local_v4_subnets)
bgp->local_node->node.node_name_len = name_len; || (sock_count != (int)bgp->local_node->node.local_v4_sockets)
memcpy(bgp->local_node->node.public_key, instance->my_keys.public_key, SC_PUBKEY_SIZE); || (addr_count != (int)bgp->local_node->node.local_v4_addrs)
bgp->local_node->dirty = 1; || (name_len != bgp->local_node->node.node_name_len)
bgp->local_node->last_ver = bgp->local_node->node.ver; || (memcmp(bgp->local_node->node.public_key, instance->my_keys.public_key, SC_PUBKEY_SIZE) != 0);
uint8_t* dp = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO); DEBUG_INFO(DEBUG_CATEGORY_BGP, "my_nodeinfo updated: socks=%d addrs=%d subnets=%d ver=%d",
if (name_len) { sock_count, addr_count, vc, bgp->local_node->node.ver);
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 { } else {
bgp->local_node->last_ver = bgp->local_node->node.ver; bgp->local_node->last_ver = bgp->local_node->node.ver;
} }

135
src/route_node.h

@ -10,35 +10,100 @@ struct ROUTE_BGP;
struct ETCP_SOCKET; struct ETCP_SOCKET;
struct UTUN_INSTANCE; 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 { struct NODEINFO {
uint64_t node_id; // (big-endian) uint64_t node_id; // (big-endian)
uint8_t ver; // версия пакета (циклический счетчик чтобы быстро сравнивать с локальной копией - были ли обновления) uint8_t ver; // версия пакета (циклический счетчик чтобы быстро сравнивать с локальной копией - были ли обновления)
uint8_t public_key[SC_PUBKEY_SIZE]; // node pubkey uint8_t public_key[SC_PUBKEY_SIZE]; // node pubkey
uint8_t node_name_len; // размер в байтах (без null терминации) uint8_t node_name_len; // размер в байтах (без null терминации)
uint8_t local_v4_sockets; // NODEINFO_IPV4_SOCKET число локальных ipv4 сокетов узла (для direct incoming connections) uint8_t local_v4_sockets; // NODEINFO_IPV4_SOCKET_META число метаданных ipv4 сокетов
uint8_t local_v6_sockets; // NODEINFO_IPV6_SOCKET число локальных ipv6 сокетов узла (для direct incoming connections) (пока 0) 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_v4_subnets; // NODEINFO_IPV4_SUBNET число локальных ipv4 подсетей узла
uint8_t local_v6_subnets; // NODEINFO_IPV6_SUBNET число локальных ipv6 подсетей узла (пока 0) uint8_t local_v6_subnets; // NODEINFO_IPV6_SUBNET (пока 0)
uint8_t tranzit_nodes; // NODEINFO_TRANZIT_NODE лучшие транзитные узлы для этой ноды (минимальный пинг / лучшее качество каналов. выбирается/обновляется узлом) uint8_t tranzit_nodes; // NODEINFO_TRANZIT_NODE лучшие транзитные узлы для этой ноды
uint8_t hop_count; // hop list: маршрут по которому распространялся этот NODEINFO_PACKET. для избежания зацикливаний при распространении по узлам. каждый узел при передаче инкрементирует и добавляет в конец свой node_id. uint8_t hop_count; // hop list: маршрут по которому распространялся этот NODEINFO_PACKET
// далее идут динамическип поля по порядку следования полей в этой структуре: char node_name[node_name_len], сокеты, роуты, tranzit nodes, hop list (блоки описаны структурами ниже). hop list - это массив node_id[hop_count]. // далее идут динамические поля по порядку:
// 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)); } __attribute__((packed));
struct NODEINFO_IPV4_SOCKET { // метаданные одного сокета
uint8_t addr[4]; // interface IP (network byte order) struct NODEINFO_IPV4_SOCKET_META {
uint16_t port; // interface port
uint8_t type; // CFG_SERVER_TYPE_PUBLIC/PRIVATE/NAT
uint8_t id; // unique socket id (0-255) uint8_t id; // unique socket id (0-255)
uint8_t nat_addr[4]; // NAT IP (network byte order), 0 = не определён uint8_t type; // CFG_SERVER_TYPE_*/NAT_VERIFIED_*
uint16_t nat_port; // NAT port, 0 = не определён } __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)); } __attribute__((packed));
struct NODEINFO_IPV6_SOCKET { struct NODEINFO_IPV6_ADDR {
uint8_t addr[16]; uint8_t addr[16];
uint16_t port; uint16_t port;
uint8_t type;
uint8_t socket_id;
} __attribute__((packed)); } __attribute__((packed));
struct NODEINFO_IPV4_SUBNET { struct NODEINFO_IPV4_SUBNET {
@ -61,60 +126,42 @@ struct NODEINFO_PATH {
struct ll_entry ll; struct ll_entry ll;
struct ETCP_CONN* conn; struct ETCP_CONN* conn;
uint8_t hop_count; // hop list: маршрут этого path uint8_t hop_count; // hop list: маршрут этого path
};// __attribute__((packed)); };
struct NODEINFO_Q { struct NODEINFO_Q {
struct ll_entry ll; struct ll_entry ll;
struct ll_queue* paths; // сюда помещаем struct NODEINFO_PATH struct ll_queue* paths; // сюда помещаем struct NODEINFO_PATH
uint8_t dirty; uint8_t dirty;
uint8_t last_ver; uint8_t last_ver;
uint64_t last_ping_time; // время последнего замера в 0.1ms struct NODE_CONNECTIVITY connectivity; // состояние связности (локальное)
uint16_t last_rtt; // лучший RTT в 0.1ms
struct ETCP_SOCKET* best_socket; struct ETCP_SOCKET* best_socket;
struct NODEINFO node; // Всегда в конце структуры - динамически расширяемый блок struct NODEINFO node; // Всегда в конце структуры - динамически расширяемый блок
};// __attribute__((packed)); };
/** /**
* @brief Создаёт/обновляет nodeinfo для собственного узла * @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); int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BGP* bgp);
/** /**
* @brief Получает указатель на массив IPv4-подсетей узла (без malloc/копирования). * @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); int get_node_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SUBNET **out_subnets);
/** /**
* @brief Получает указатель на массив IPv4-сокетов узла. * @brief Получает указатель на массив метаданных IPv4-сокетов узла.
* */
* @param node Указатель на NODEINFO_Q int get_node_v4_sockets_meta(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SOCKET_META **out_meta);
* @param out_sockets [out] указатель на первый элемент массива NODEINFO_IPV4_SOCKET
* @return количество сокетов (>= 0) или -1 при ошибке /**
* @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-сокетов узла. * @brief Получает указатель на массив метаданных IPv6-сокетов узла.
*
* @param node Указатель на NODEINFO_Q
* @param out_sockets [out] указатель на первый элемент массива NODEINFO_IPV6_SOCKET
* @return количество сокетов (>= 0) или -1 при ошибке
*/ */
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 #endif // ROUTE_NODE_H

10
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) { if (sin->sin_addr.s_addr == 0 && sin->sin_port == 0) {
struct NODEINFO_Q* nq = route_bgp_get_node(bgp, req_pkt->node_id); struct NODEINFO_Q* nq = route_bgp_get_node(bgp, req_pkt->node_id);
if (nq) { if (nq) {
const struct NODEINFO_IPV4_SOCKET* sockets; const struct NODEINFO_IPV4_ADDR* addrs;
int count = get_node_v4_sockets(nq, &sockets); int count = get_node_v4_addrs(nq, &addrs);
if (count > 0) { if (count > 0) {
memcpy(&sin->sin_addr.s_addr, sockets[0].addr, 4); memcpy(&sin->sin_addr.s_addr, addrs[0].addr, 4);
sin->sin_port = sockets[0].port; sin->sin_port = addrs[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));
} }
} }
} }

1
tests/Makefile.am

@ -89,6 +89,7 @@ ETCP_FULL_OBJS = \
$(top_builddir)/src/utun-route_bgp.o \ $(top_builddir)/src/utun-route_bgp.o \
$(top_builddir)/src/utun-route_ping.o \ $(top_builddir)/src/utun-route_ping.o \
$(top_builddir)/src/utun-route_node.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-routing.o \
$(top_builddir)/src/utun-tun_if.o \ $(top_builddir)/src/utun-tun_if.o \
$(top_builddir)/src/utun-tun_route.o \ $(top_builddir)/src/utun-tun_route.o \

79
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..."); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Waiting for NAT detection to complete for C1...");
bgp_wait_cycles = 0; bgp_wait_cycles = 0;
while (!test_timed_out && bgp_wait_cycles < 1500) { while (!test_timed_out && bgp_wait_cycles < 1500) {
if (link_sc1->nat_check_status == NAT_CHECK_OPEN) { if (link_sc1->nat_check_status == NAT_CHECK_EIM) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT detection completed for C1: OPEN"); DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT detection completed for C1: EIM");
break; break;
} }
uasync_poll(ua, 10); uasync_poll(ua, 10);
bgp_wait_cycles++; 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"); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NAT detection did not complete for C1");
goto cleanup; goto cleanup;
} }
@ -297,7 +297,7 @@ int main(void) {
struct ETCP_SOCKET* sock = inst_c1->etcp_sockets; struct ETCP_SOCKET* sock = inst_c1->etcp_sockets;
int found = 0; int found = 0;
while (sock) { 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; sock = sock->next;
} }
if (found) { if (found) {
@ -318,14 +318,14 @@ int main(void) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO_Q for C1 disappeared"); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO_Q for C1 disappeared");
goto cleanup; goto cleanup;
} }
if (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 OPEN(%d)", DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: nat_check_status=%d, expected EIM(%d)",
(int)link_sc1->nat_check_status, NAT_CHECK_OPEN); (int)link_sc1->nat_check_status, NAT_CHECK_EIM);
goto cleanup; goto cleanup;
} }
if (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 OPEN(%d)", DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: nat_type=%d, expected EIM(%d)",
(int)link_sc1->nat_type, NAT_TYPE_OPEN); (int)link_sc1->nat_type, NAT_TYPE_EIM);
goto cleanup; goto cleanup;
} }
if (link_sc1->nat_ip == 0 || link_sc1->nat_port == 0) { 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; if (conn_sc1->peer_node_id == NODE_ID_C1) break;
conn_sc1 = conn_sc1->next; conn_sc1 = conn_sc1->next;
} }
if (!link_sc1 || link_sc1->nat_type != NAT_TYPE_OPEN) { if (!link_sc1 || link_sc1->nat_type != NAT_TYPE_EIM) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: link nat_type not OPEN on server"); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: link nat_type not EIM on server");
goto cleanup; goto cleanup;
} }
@ -349,32 +349,27 @@ int main(void) {
struct ETCP_SOCKET* sock_c1 = NULL; struct ETCP_SOCKET* sock_c1 = NULL;
struct ETCP_SOCKET* sock = inst_c1->etcp_sockets; struct ETCP_SOCKET* sock = inst_c1->etcp_sockets;
while (sock) { while (sock) {
if (sock->nat_type == NAT_VERIFIED_OPEN) { if (sock->nat_type == NAT_VERIFIED_EIM) {
sock_c1 = sock; sock_c1 = sock;
break; break;
} }
sock = sock->next; sock = sock->next;
} }
if (!sock_c1) { 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; goto cleanup;
} }
// Verify local_node socket update on C1 (received NAT_INFO updated nodeinfo) // Verify local_node socket update on C1 (received NAT_INFO updated nodeinfo)
if (inst_c1->bgp && inst_c1->bgp->local_node) { if (inst_c1->bgp && inst_c1->bgp->local_node) {
const struct NODEINFO_IPV4_SOCKET* sockets = NULL; const struct NODEINFO_IPV4_SOCKET_META* meta = NULL;
int sock_count = get_node_v4_sockets(inst_c1->bgp->local_node, &sockets); int meta_count = get_node_v4_sockets_meta(inst_c1->bgp->local_node, &meta);
int found = 0; int found = 0;
for (int i = 0; i < sock_count; i++) { for (int i = 0; i < meta_count; i++) {
if (sockets[i].id == sock_c1->sock_id) { if (meta[i].id == sock_c1->sock_id) {
uint32_t s_ip = (sockets[i].nat_addr[0] << 24) | (sockets[i].nat_addr[1] << 16) | if (meta[i].type != NAT_VERIFIED_EIM) {
(sockets[i].nat_addr[2] << 8) | sockets[i].nat_addr[3]; DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: local_node socket meta not updated on C1: type=%u expected=%u",
if (s_ip != link_sc1->nat_ip || sockets[i].nat_port != link_sc1->nat_port || meta[i].type, NAT_VERIFIED_EIM);
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);
goto cleanup; goto cleanup;
} }
found = 1; found = 1;
@ -382,10 +377,30 @@ int main(void) {
} }
} }
if (!found) { 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; 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) // 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) { 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; 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) { if (node_c1_on_c2) {
const struct NODEINFO_IPV4_SOCKET* sockets = NULL; const struct NODEINFO_IPV4_SOCKET_META* meta = NULL;
int sock_count = get_node_v4_sockets(node_c1_on_c2, &sockets); int meta_count = get_node_v4_sockets_meta(node_c1_on_c2, &meta);
for (int i = 0; i < sock_count; i++) { for (int i = 0; i < meta_count; i++) {
if (sockets[i].type == NAT_VERIFIED_OPEN) { if (meta[i].type == NAT_VERIFIED_EIM) {
c2_verified_nat = 1; c2_verified_nat = 1;
break; break;
} }

16
tests/test_route_ping.c

@ -273,19 +273,11 @@ int main(void) {
uint16_t target_port = 0; uint16_t target_port = 0;
struct NODEINFO_Q* nq = inst_b->bgp ? route_bgp_get_node(inst_b->bgp, NODE_ID_C) : NULL; struct NODEINFO_Q* nq = inst_b->bgp ? route_bgp_get_node(inst_b->bgp, NODE_ID_C) : NULL;
if (nq) { if (nq) {
const struct NODEINFO_IPV4_SOCKET* sockets; const struct NODEINFO_IPV4_ADDR* addrs;
int sc = get_node_v4_sockets(nq, &sockets); int sc = get_node_v4_addrs(nq, &addrs);
if (sc > 0) { if (sc > 0) {
/* Используем NAT адрес если есть, иначе interface адрес */ memcpy(&target_ip, addrs[0].addr, 4); /* network byte order */
uint32_t nat_ip = (sockets[0].nat_addr[0] << 24) | (sockets[0].nat_addr[1] << 16) | target_port = addrs[0].port; /* host byte order */
(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 */
}
} }
} }
if (target_ip == 0 || target_port == 0) { if (target_ip == 0 || target_port == 0) {

14
tools/etcpmon/etcpmon_gui.c

@ -1211,12 +1211,14 @@ void etcpmon_gui_update_conn_list(struct etcpmon_app* app,
static const char* nat_type_to_string(uint8_t nat_type) { static const char* nat_type_to_string(uint8_t nat_type) {
switch (nat_type) { switch (nat_type) {
case NAT_TYPE_OPEN: return "Open"; case NAT_TYPE_EIM: return "EIM";
case NAT_TYPE_RESTRICTED: return "Restricted"; case NAT_TYPE_STRICT: return "Strict";
case NAT_VERIFIED_OPEN: return "Verified-Open"; case NAT_VERIFIED_EIM: return "Verified-EIM";
case NAT_VERIFIED_RESTRICTED: return "Verified-Restricted"; case NAT_VERIFIED_STRICT: return "Verified-Strict";
case NAT_VERIFIED_DIRECT: return "Direct"; case NAT_VERIFIED_DIRECT: return "Verified-Direct";
default: return "Unknown"; case NAT_TYPE_UNKNOWN: return "Unknown";
default: return "?";
}
} }
} }

8
tools/etcpmon/etcpmon_protocol.h

@ -25,11 +25,11 @@ extern "C" {
/* NAT types (mirrored from etcp_connections.h for monitor protocol) */ /* NAT types (mirrored from etcp_connections.h for monitor protocol) */
#define NAT_TYPE_UNKNOWN 0 #define NAT_TYPE_UNKNOWN 0
#define NAT_TYPE_OPEN 1 #define NAT_TYPE_EIM 1 /* Endpoint-Independent Mapping */
#define NAT_TYPE_RESTRICTED 2 #define NAT_TYPE_STRICT 2 /* Address/Restricted or Symmetric */
#define NAT_VERIFIED_UNKNOWN 4 #define NAT_VERIFIED_UNKNOWN 4
#define NAT_VERIFIED_OPEN 5 #define NAT_VERIFIED_EIM 5 /* EIM NAT */
#define NAT_VERIFIED_RESTRICTED 6 #define NAT_VERIFIED_STRICT 6 /* strict NAT */
#define NAT_VERIFIED_DIRECT 7 #define NAT_VERIFIED_DIRECT 7
#define ETCPMON_MAX_MSG_SIZE 4096 #define ETCPMON_MAX_MSG_SIZE 4096
#define ETCPMON_MAX_CONN_NAME 32 #define ETCPMON_MAX_CONN_NAME 32

Loading…
Cancel
Save