Browse Source

Add NAT detection with STUN-like ping and integration test

congestion
Evgeny 6 months ago
parent
commit
31eb4a5d26
  1. 18
      src/etcp_connections.c
  2. 4
      src/etcp_connections.h
  3. 169
      src/route_bgp.c
  4. 22
      src/route_bgp.h
  5. 5
      src/route_node.c
  6. 18
      src/route_node.h
  7. 182
      src/route_ping.c
  8. 15
      src/route_ping.h
  9. 2
      src/utun_instance.h
  10. 5
      tests/Makefile.am
  11. 406
      tests/test_nat_detection.c
  12. 10
      tests/test_route_ping.c

18
src/etcp_connections.c

@ -11,6 +11,7 @@
#include "config_parser.h"
#include "crc32.h"
#include "etcp.h"
#include "route_node.h"
#include "../lib/memory_pool.h"
#include "../lib/u_async.h"
#include "../lib/debug_config.h"
@ -84,6 +85,7 @@ static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset) {
dgram->data[offset++] = (link->recovery_interval/100) & 0xFF;
dgram->data[offset++] = link->local_link_id;
dgram->data[offset++] = link->conn ? link->conn->sock_id : 0;
// padding
int s = rand() % (link->handshake_maxsize - link->handshake_minsize) + link->handshake_minsize;
@ -544,6 +546,8 @@ struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_S
e_sock->errorcode = 0;
e_sock->pkt_format_errors = 0;
e_sock->type = type;
e_sock->sock_id = instance->next_socket_id++;
e_sock->nat_type = NAT_TYPE_UNKNOWN;
e_sock->mtu = mtu;
e_sock->loss_rate = loss_rate;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Add Socket type=%d", type);
@ -1155,6 +1159,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
uint8_t keepalive[2];
uint8_t recovery[2];
uint8_t link_id;
uint8_t remote_socket_id;
uint8_t pubkey[SC_PUBKEY_SIZE];
} *ack_hdr=(void*)&pkt->data[0];
uint64_t peer_id = be64toh(*(uint64_t*)ack_hdr->id);
@ -1280,6 +1285,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
// Link exists - reuse it for recovery
link->remote_link_id = ack_hdr->link_id;
link->remote_socket_id = ack_hdr->remote_socket_id;
// For CHANNEL_INIT (0x04): if link already initialized - no reset, otherwise reset
// For INIT_REQUEST (0x02): always reset
@ -1302,6 +1308,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
link = etcp_link_new(conn, e_sock, &addr, 1);
if (!link) { if (new_conn) etcp_connection_close(conn); errorcode=66; DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connections_read_callback: failed to create link for connection"); goto ec_fr; }// облом
link->remote_link_id = ack_hdr->link_id;
link->remote_socket_id = ack_hdr->remote_socket_id;
// For new links: INIT_REQUEST (0x02) causes reset, CHANNEL_INIT (0x04) does not
if (ack_hdr->code == ETCP_INIT_REQUEST || new_conn) {
@ -1323,6 +1330,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
uint8_t id[8];
uint8_t mtu[2];
uint8_t link_id;
uint8_t remote_socket_id;
uint8_t peer_ipv4[4];
uint8_t peer_port[2];
} *ack_repl_hdr=(void*)&pkt->data[0];
@ -1340,6 +1348,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
ack_repl_hdr->mtu[0]=link->mtu_local>>8;
ack_repl_hdr->mtu[1]=link->mtu_local;
ack_repl_hdr->link_id = link->local_link_id;
ack_repl_hdr->remote_socket_id = ack_hdr->remote_socket_id;
// Add client's IP:port (so client behind NAT can know its external address)
if (addr.ss_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in*)&addr;
@ -1347,10 +1356,14 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
uint16_t port = ntohs(sin->sin_port);
ack_repl_hdr->peer_port[0] = port >> 8;
ack_repl_hdr->peer_port[1] = port & 0xFF;
link->nat_ip = ntohl(sin->sin_addr.s_addr);
link->nat_port = port;
} else {
// For IPv6, set to 0 (not supported for NAT traversal)
memset(ack_repl_hdr->peer_ipv4, 0, 4);
memset(ack_repl_hdr->peer_port, 0, 2);
link->nat_ip = 0;
link->nat_port = 0;
}
pkt->noencrypt_len=0;
pkt->link=link;
@ -1430,9 +1443,10 @@ process_decrypted:
link->mtu_remote = (pkt->data[offset++] << 8) | pkt->data[offset++];
link->mtu = link->mtu_local < link->mtu_remote ? link->mtu_local : link->mtu_remote;
link->remote_link_id = pkt->data[offset++];
link->remote_socket_id = pkt->data[offset++];
// Parse NAT IP:port from response (new format includes 4+2 bytes)
if (pkt_len >= 18) {
if (pkt_len >= 19) {
uint32_t new_nat_ip = (pkt->data[offset] << 24) | (pkt->data[offset+1] << 16) |
(pkt->data[offset+2] << 8) | pkt->data[offset+3];
offset += 4;

4
src/etcp_connections.h

@ -68,6 +68,8 @@ struct ETCP_SOCKET {
void* socket_id; // Socket ID from uasync_add_socket
uint8_t type; // CFG_SERVER_TYPE_PUBLIC/NAT/PRIVATE
uint8_t sock_id; // unique socket id (0-255) for NAT matching
uint8_t nat_type; // NAT_TYPE_* (detected by server)
uint32_t local_defaultroute_ip; // auto-detected IPv4 for public servers (network byte order)
uint8_t local_defaultroute_ip6[16]; // auto-detected IPv6 for public servers
};
@ -93,6 +95,8 @@ struct ETCP_LINK {
uint8_t initialized; // Флаг инициализации (1=подтверждено или получен request)
uint8_t local_link_id; // id моего линка
uint8_t remote_link_id; // id этого линка на peer (устанавливается в момент initialized)
uint8_t remote_socket_id; // socket id peer
uint8_t nat_type; // NAT_TYPE_* (detected for this client link)
uint8_t recv_keepalive; // 1 - up, 0 - down (принимаются ли пакеты)
uint8_t remote_keepalive; // 1 - up, 0 - down (удаленная сторона сообщает - принимаются ли у нее пакеты)
uint8_t link_status; // 1 - up, 0 - down (итоговый статус - если есть проблемы на любой стороне - линк down)

169
src/route_bgp.c

@ -155,6 +155,29 @@ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
route_ping_handle_req(bgp, from_conn, data, entry->len);
} else if (subcmd == ROUTE_SUBCMD_PING_RESP) {
route_ping_handle_resp(bgp, from_conn, data, entry->len);
} else if (subcmd == ROUTE_SUBCMD_NAT_INFO) {
if (entry->len >= sizeof(struct BGP_NAT_INFO)) {
const struct BGP_NAT_INFO* info = (const struct BGP_NAT_INFO*)data;
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 = ((info->nat_port >> 8) & 0xFF) | ((info->nat_port & 0xFF) << 8);
uint16_t nat_port_be = info->nat_port;
// Save nat_type to all links of this connection and their sockets
struct ETCP_LINK* l = from_conn->links;
while (l) {
l->nat_type = info->nat_type;
if (l->conn) {
l->conn->nat_type = info->nat_type;
}
l = l->next;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO from %s: type=%s ip=%u.%u.%u.%u port=%u",
from_conn->log_name,
info->nat_type == NAT_TYPE_OPEN ? "OPEN" : "RESTRICTED",
(info->nat_ip[0]), (info->nat_ip[1]),
(info->nat_ip[2]), (info->nat_ip[3]),
(unsigned)nat_port);
}
}
queue_dgram_free(entry);
@ -300,6 +323,8 @@ void route_bgp_destroy(struct UTUN_INSTANCE* instance) {
instance->bgp = NULL;
}
static void route_bgp_start_nat_check(struct ROUTE_BGP* bgp, struct NODEINFO_Q* node, struct ETCP_CONN* via_conn);
void route_bgp_new_conn(struct ETCP_CONN* conn) {
if (!conn) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_new_conn: conn is NULL");
@ -322,6 +347,18 @@ void route_bgp_new_conn(struct ETCP_CONN* conn) {
route_bgp_add_to_senders(bgp, conn);
// Try to start NAT checks for nodes waiting for a third node
if (bgp->nodes) {
struct ll_entry* e = bgp->nodes->head;
while (e) {
struct NODEINFO_Q* node = (struct NODEINFO_Q*)e;
if (node->nat_check_status == NAT_CHECK_WAITING) {
route_bgp_start_nat_check(bgp, node, conn);
}
e = e->next;
}
}
route_bgp_send_table_request(bgp, conn);
}
@ -520,6 +557,99 @@ int nodeinfo_dyn_size(struct NODEINFO* node) {
node->hop_count * 8;
}
// NAT check context
struct nat_check_arg {
struct NODEINFO_Q* node;
struct ETCP_CONN* client_conn;
uint32_t nat_ip;
uint16_t nat_port;
};
static void nat_check_cb(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok,
uint32_t recv_ip, uint16_t recv_port, void* arg) {
(void)avg_rtt; (void)count_sent; (void)count_ok; (void)recv_ip; (void)recv_port;
struct nat_check_arg* na = (struct nat_check_arg*)arg;
if (!na || !na->node) { u_free(na); return; }
uint8_t nat_type = success ? NAT_TYPE_OPEN : NAT_TYPE_RESTRICTED;
na->node->nat_type = nat_type;
na->node->nat_check_status = success ? NAT_CHECK_OPEN : NAT_CHECK_RESTRICTED;
// Save nat_type to all links of client connection
if (na->client_conn) {
struct ETCP_LINK* l = na->client_conn->links;
while (l) { l->nat_type = nat_type; l = l->next; }
}
route_bgp_send_nat_info(na->client_conn, na->nat_ip, na->nat_port, nat_type);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "nat_check_cb: node=%016llx type=%s success=%d",
(unsigned long long)na->node->node.node_id,
success ? "OPEN" : "RESTRICTED", success);
u_free(na);
}
static struct ETCP_CONN* route_bgp_find_third_node(struct ROUTE_BGP* bgp, struct ETCP_CONN* exclude) {
if (!bgp || !bgp->senders_list) return NULL;
struct ll_entry* e = bgp->senders_list->head;
while (e) {
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data;
if (item && item->conn && item->conn != exclude) return item->conn;
e = e->next;
}
return NULL;
}
static void route_bgp_extract_nat_addr(struct ETCP_CONN* conn, uint32_t* nat_ip, uint16_t* nat_port) {
*nat_ip = 0; *nat_port = 0;
if (!conn) return;
struct ETCP_LINK* l = conn->links;
while (l) {
if (l->nat_ip != 0) {
*nat_ip = l->nat_ip;
*nat_port = l->nat_port;
return;
}
l = l->next;
}
}
static void route_bgp_start_nat_check(struct ROUTE_BGP* bgp, struct NODEINFO_Q* node, struct ETCP_CONN* via_conn) {
if (!bgp || !node || !via_conn) return;
if (node->nat_check_status != NAT_CHECK_NONE && node->nat_check_status != NAT_CHECK_WAITING) return;
// Find client connection
struct ETCP_CONN* client_conn = NULL;
if (node->paths && node->paths->head) {
struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)node->paths->head;
client_conn = path->conn;
}
if (!client_conn) return;
// Extract NAT address from client connection
uint32_t nat_ip; uint16_t nat_port;
route_bgp_extract_nat_addr(client_conn, &nat_ip, &nat_port);
if (nat_ip == 0 || nat_port == 0) return;
// Save NAT address to node
node->nat_ip = nat_ip;
node->nat_port = nat_port;
// Allocate callback arg
struct nat_check_arg* arg = u_calloc(1, sizeof(struct nat_check_arg));
if (!arg) return;
arg->node = node;
arg->client_conn = client_conn;
arg->nat_ip = nat_ip;
arg->nat_port = nat_port;
// Send ping request via third node (with embedded pubkey for NAT detection)
int ret = route_ping_send_req_addr(bgp, via_conn, node->node.node_id, nat_ip, nat_port,
3, 500, 1000, 5000, nat_check_cb, arg,
node->node.public_key);
if (ret == 0) {
node->nat_check_status = NAT_CHECK_IN_PROGRESS;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_start_nat_check: started for node=%016llx via=%s",
(unsigned long long)node->node.node_id, via_conn->log_name);
} else {
node->nat_check_status = NAT_CHECK_WAITING;
u_free(arg);
DEBUG_WARN(DEBUG_CATEGORY_BGP, "route_bgp_start_nat_check: failed to start for node=%016llx",
(unsigned long long)node->node.node_id);
}
}
int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, const uint8_t* data, size_t len) {
if (!bgp || !from || len < sizeof(struct BGP_NODEINFO_PACKET)) return -1;
@ -582,6 +712,18 @@ int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, co
route_bgp_remove_path_by_hop(nodeinfo1, from->peer_node_id);
route_bgp_add_path(nodeinfo1, from, hop_list, nodeinfo1->node.hop_count);
// Start NAT check for direct peers
if (nodeinfo1->node.hop_count == 1 && nodeinfo1->nat_check_status == NAT_CHECK_NONE) {
struct ETCP_CONN* third = route_bgp_find_third_node(bgp, from);
if (third) {
route_bgp_start_nat_check(bgp, nodeinfo1, third);
} else {
nodeinfo1->nat_check_status = NAT_CHECK_WAITING;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT check queued (no third node yet) for node=%016llx",
(unsigned long long)node_id);
}
}
if (bgp->instance->rt) {
route_insert(bgp->instance->rt, nodeinfo1);
}
@ -744,3 +886,30 @@ static void route_bgp_handle_request_table(struct ROUTE_BGP* bgp, struct ETCP_CO
route_bgp_send_full_table(bgp, conn);
route_bgp_add_to_senders(bgp, conn);
}
void route_bgp_send_nat_info(struct ETCP_CONN* conn, uint32_t nat_ip, uint16_t nat_port, uint8_t nat_type) {
if (!conn) return;
struct BGP_NAT_INFO* pkt = u_calloc(1, sizeof(struct BGP_NAT_INFO));
if (!pkt) return;
pkt->cmd = ETCP_ID_ROUTE_ENTRY;
pkt->subcmd = ROUTE_SUBCMD_NAT_INFO;
pkt->nat_ip[0] = (nat_ip >> 24) & 0xFF;
pkt->nat_ip[1] = (nat_ip >> 16) & 0xFF;
pkt->nat_ip[2] = (nat_ip >> 8) & 0xFF;
pkt->nat_ip[3] = nat_ip & 0xFF;
pkt->nat_port = htons(nat_port);
pkt->nat_type = nat_type;
struct ll_entry* e = queue_entry_new(0);
if (!e) {
u_free(pkt);
return;
}
e->dgram = (uint8_t*)pkt;
e->len = sizeof(struct BGP_NAT_INFO);
etcp_send(conn, e);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_send_nat_info to %s: type=%s ip=%08x port=%u",
conn->log_name,
nat_type == NAT_TYPE_OPEN ? "OPEN" : "RESTRICTED",
(unsigned)nat_ip, (unsigned)nat_port);
}

22
src/route_bgp.h

@ -16,6 +16,7 @@
#define ROUTE_SUBCMD_NODEINFO 0x04 // полная информация об узле + маршруты
#define ROUTE_SUBCMD_REQUEST_TABLE 0x05 // запрос полной таблицы
#define ROUTE_SUBCMD_WITHDRAW 0x06 // узел стал недоступен
#define ROUTE_SUBCMD_NAT_INFO 0x09 // информация о типе NAT клиента
#define MAX_HOPS 16
#define BGP_NODES_HASH_SIZE 256
@ -52,6 +53,17 @@ struct ROUTE_BGP_CONN_ITEM {
struct ETCP_CONN* conn;
};
/**
* @brief Пакет NAT_INFO (фиксированный)
*/
struct BGP_NAT_INFO {
uint8_t cmd;
uint8_t subcmd;
uint8_t nat_ip[4]; // network byte order
uint16_t nat_port; // network byte order
uint8_t nat_type; // NAT_TYPE_*
} __attribute__((packed));
struct route_ping_pending;
struct ROUTE_BGP {
@ -145,4 +157,14 @@ int route_bgp_add_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn, uint64_t*
*/
int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn);
/**
* @brief Отправляет NAT_INFO клиенту с его типом NAT.
*
* @param conn соединение к клиенту
* @param nat_ip IP клиента (network byte order)
* @param nat_port порт клиента (network byte order)
* @param nat_type NAT_TYPE_OPEN или NAT_TYPE_RESTRICTED
*/
void route_bgp_send_nat_info(struct ETCP_CONN* conn, uint32_t nat_ip, uint16_t nat_port, uint8_t nat_type);
#endif // ROUTE_BGP_H

5
src/route_node.c

@ -166,7 +166,8 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG
while (e_sock) {
if (e_sock->local_addr.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&e_sock->local_addr;
if (memcmp(sa->addr, &sin->sin_addr.s_addr, 4) != 0 || sa->port != ntohs(sin->sin_port)) {
if (memcmp(sa->addr, &sin->sin_addr.s_addr, 4) != 0 || sa->port != ntohs(sin->sin_port) ||
sa->type != e_sock->type || sa->id != e_sock->sock_id) {
same = false;
break;
}
@ -203,6 +204,8 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG
struct sockaddr_in* sin = (struct sockaddr_in*)&e_sock->local_addr;
memcpy(sa->addr, &sin->sin_addr.s_addr, 4);
sa->port = ntohs(sin->sin_port);
sa->type = e_sock->type;
sa->id = e_sock->sock_id;
sa++;
}
e_sock = e_sock->next;

18
src/route_node.h

@ -29,6 +29,8 @@ struct NODEINFO {
struct NODEINFO_IPV4_SOCKET {
uint8_t addr[4];// network byte order
uint16_t port;
uint8_t type; // CFG_SERVER_TYPE_PUBLIC/PRIVATE/NAT
uint8_t id; // unique socket id (0-255)
} __attribute__((packed));
struct NODEINFO_IPV6_SOCKET {
@ -58,6 +60,18 @@ struct NODEINFO_PATH {
uint8_t hop_count; // hop list: маршрут этого path
};// __attribute__((packed));
// NAT check status
#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
// NAT type
#define NAT_TYPE_UNKNOWN 0
#define NAT_TYPE_OPEN 1
#define NAT_TYPE_RESTRICTED 2
struct NODEINFO_Q {
struct ll_entry ll;
struct ll_queue* paths; // сюда помещаем struct NODEINFO_PATH
@ -66,6 +80,10 @@ struct NODEINFO_Q {
uint64_t last_ping_time; // время последнего замера в 0.1ms
uint16_t last_rtt; // лучший RTT в 0.1ms
struct ETCP_SOCKET* best_socket;
uint8_t nat_check_status; // NAT_CHECK_*
uint32_t nat_ip; // IP клиента (network byte order)
uint16_t nat_port; // порт клиента
uint8_t nat_type; // NAT_TYPE_*
struct NODEINFO node; // Всегда в конце структуры - динамически расширяемый блок
};// __attribute__((packed));

182
src/route_ping.c

@ -27,6 +27,8 @@ struct route_ping_req {
uint8_t socket_count;
uint8_t completed_count;
uint16_t timeout_ms;
uint8_t recv_ipv4[4]; // IP:port с которого получен PING_REQ (STUN-like)
uint16_t recv_port; // network byte order
struct route_ping_sock_ctx sockets[0];
};
@ -72,7 +74,7 @@ static void route_ping_pending_timeout(void* arg) {
cur = &(*cur)->next;
}
if (p->callback) {
p->callback(0, 0, 0, 0, p->arg);
p->callback(0, 0, 0, 0, 0, 0, p->arg);
}
u_free(p);
}
@ -193,6 +195,8 @@ static void route_ping_send_resp(struct route_ping_req* req, uint16_t avg_rtt) {
resp->count_sent = total_sent;
resp->count_ok = total_ok;
resp->avg_rtt = avg_rtt;
memcpy(resp->recv_ipv4, req->recv_ipv4, 4);
resp->recv_port = htons(req->recv_port);
struct ll_entry* e = queue_entry_new(0);
if (!e) {
u_free(resp);
@ -262,6 +266,78 @@ int route_ping_send_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, uint64
return 0;
}
int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, uint64_t node_id,
uint32_t target_ip, uint16_t target_port,
uint8_t count, uint16_t interval_ms, uint16_t timeout_ms,
uint16_t wait_timeout_ms, route_ping_callback_t cb, void* arg,
const uint8_t* pubkey) {
if (!bgp || !to_conn || count == 0 || timeout_ms == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr: invalid args");
return -1;
}
size_t extra = 6 + (pubkey ? SC_PUBKEY_SIZE : 0);
size_t pkt_size = sizeof(struct BGP_PING_REQUEST) + extra;
struct BGP_PING_REQUEST* req_pkt = u_calloc(1, pkt_size);
if (!req_pkt) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr: alloc failed");
return -2;
}
req_pkt->cmd = ETCP_ID_ROUTE_ENTRY;
req_pkt->subcmd = ROUTE_SUBCMD_PING_REQ;
req_pkt->request_id = bgp->next_ping_req_id++;
req_pkt->node_id = node_id;
req_pkt->count = count;
req_pkt->interval_ms = interval_ms;
req_pkt->timeout_ms = timeout_ms;
uint8_t* tail = (uint8_t*)req_pkt + sizeof(struct BGP_PING_REQUEST);
tail[0] = (target_ip >> 24) & 0xFF;
tail[1] = (target_ip >> 16) & 0xFF;
tail[2] = (target_ip >> 8) & 0xFF;
tail[3] = target_ip & 0xFF;
tail[4] = (target_port >> 8) & 0xFF;
tail[5] = target_port & 0xFF;
if (pubkey) {
memcpy(tail + 6, pubkey, SC_PUBKEY_SIZE);
}
struct ll_entry* e = queue_entry_new(0);
if (!e) {
u_free(req_pkt);
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr: queue_entry_new failed");
return -3;
}
e->dgram = (uint8_t*)req_pkt;
e->len = pkt_size;
int ret = etcp_send(to_conn, e);
if (ret != 0) {
u_free(req_pkt);
queue_entry_free(e);
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr: etcp_send failed");
return -4;
}
struct route_ping_pending* pending = u_calloc(1, sizeof(struct route_ping_pending));
if (!pending) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr: pending alloc failed");
return -5;
}
pending->bgp = bgp;
pending->request_id = req_pkt->request_id;
pending->callback = cb;
pending->arg = arg;
pending->next = bgp->ping_pending;
bgp->ping_pending = pending;
pending->timeout_timer = uasync_set_timeout(bgp->instance->ua, wait_timeout_ms * 10, pending, route_ping_pending_timeout);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr: request_id=%016llx node=%016llx ip=%08x port=%u pubkey=%s",
(unsigned long long)pending->request_id, (unsigned long long)node_id,
(unsigned)target_ip, (unsigned)target_port, pubkey ? "yes" : "no");
return 0;
}
void route_ping_destroy_pending(struct ROUTE_BGP* bgp) {
if (!bgp) return;
while (bgp->ping_pending) {
@ -281,25 +357,57 @@ void route_ping_handle_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, c
return;
}
const struct BGP_PING_REQUEST* req_pkt = (const struct BGP_PING_REQUEST*)data;
struct NODEINFO_Q* target = route_bgp_get_node(bgp, req_pkt->node_id);
if (!target) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "route_ping_handle_req: node %016llx not found",
// Check for custom target IP:port first (for NAT detection with STUN)
struct NODEINFO_IPV4_SOCKET custom_socket;
const struct NODEINFO_IPV4_SOCKET* sockets_v4 = NULL;
const struct NODEINFO_IPV6_SOCKET* sockets_v6 = NULL;
int sock_count_v4 = 0;
int sock_count_v6 = 0;
struct NODEINFO_Q* target = NULL;
const uint8_t* embedded_pubkey = NULL;
if (len >= sizeof(struct BGP_PING_REQUEST) + 6) {
// Custom target address provided (for NAT ping)
const uint8_t* tail = data + sizeof(struct BGP_PING_REQUEST);
custom_socket.addr[0] = tail[0];
custom_socket.addr[1] = tail[1];
custom_socket.addr[2] = tail[2];
custom_socket.addr[3] = tail[3];
custom_socket.port = (tail[4] << 8) | tail[5];
custom_socket.type = 0;
custom_socket.id = 0;
sockets_v4 = &custom_socket;
sock_count_v4 = 1;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_ping_handle_req: using custom target %d.%d.%d.%d:%u for node %016llx",
tail[0], tail[1], tail[2], tail[3], custom_socket.port,
(unsigned long long)req_pkt->node_id);
// отправляем ответ с нулями
struct route_ping_req* req = u_calloc(1, sizeof(struct route_ping_req));
if (req) {
req->reply_conn = from_conn;
req->request_id = req_pkt->request_id;
req->socket_count = 0;
route_ping_send_resp(req, 0);
u_free(req);
// Check for embedded pubkey
if (len >= sizeof(struct BGP_PING_REQUEST) + 6 + SC_PUBKEY_SIZE) {
embedded_pubkey = tail + 6;
}
return;
// For custom target, we still need target node for pubkey lookup if not embedded
target = route_bgp_get_node(bgp, req_pkt->node_id);
} else {
// No custom target - look up nodeinfo for advertised sockets
target = route_bgp_get_node(bgp, req_pkt->node_id);
if (!target) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "route_ping_handle_req: node %016llx not found",
(unsigned long long)req_pkt->node_id);
// отправляем ответ с нулями
struct route_ping_req* req = u_calloc(1, sizeof(struct route_ping_req));
if (req) {
req->reply_conn = from_conn;
req->request_id = req_pkt->request_id;
req->socket_count = 0;
route_ping_send_resp(req, 0);
u_free(req);
}
return;
}
sock_count_v4 = get_node_v4_sockets(target, &sockets_v4);
sock_count_v6 = get_node_v6_sockets(target, &sockets_v6);
}
const struct NODEINFO_IPV4_SOCKET* sockets_v4 = NULL;
const struct NODEINFO_IPV6_SOCKET* sockets_v6 = NULL;
int sock_count_v4 = get_node_v4_sockets(target, &sockets_v4);
int sock_count_v6 = get_node_v6_sockets(target, &sockets_v6);
if ((sock_count_v4 <= 0 || !sockets_v4) && (sock_count_v6 <= 0 || !sockets_v6)) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "route_ping_handle_req: no sockets for node %016llx",
@ -347,6 +455,19 @@ void route_ping_handle_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, c
req->target_node = target;
req->timeout_ms = req_pkt->timeout_ms;
req->socket_count = local_count;
// Сохраняем IP:port запрашивающего (из первого линка) для STUN-ответа
struct ETCP_LINK* l = from_conn->links;
if (l && l->remote_addr.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&l->remote_addr;
req->recv_ipv4[0] = (ntohl(sin->sin_addr.s_addr) >> 24) & 0xFF;
req->recv_ipv4[1] = (ntohl(sin->sin_addr.s_addr) >> 16) & 0xFF;
req->recv_ipv4[2] = (ntohl(sin->sin_addr.s_addr) >> 8) & 0xFF;
req->recv_ipv4[3] = ntohl(sin->sin_addr.s_addr) & 0xFF;
req->recv_port = ntohs(sin->sin_port);
} else {
memset(req->recv_ipv4, 0, 4);
req->recv_port = 0;
}
ls = bgp->instance->etcp_sockets;
uint8_t idx = 0;
@ -386,14 +507,26 @@ void route_ping_handle_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, c
series->sock_ctx = &req->sockets[idx];
series->local_sock = ls;
series->target_addr = target_addr;
memcpy(series->pubkey, target->node.public_key, SC_PUBKEY_SIZE);
const uint8_t* pubkey_to_use = target ? target->node.public_key : embedded_pubkey;
if (!pubkey_to_use) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_handle_req: no pubkey available for node %016llx",
(unsigned long long)req_pkt->node_id);
series->sock_ctx->avg_rtt = req_pkt->timeout_ms * 10;
series->sock_ctx->count_sent = req_pkt->count;
req->completed_count++;
u_free(series);
idx++;
ls = ls->next;
continue;
}
memcpy(series->pubkey, pubkey_to_use, SC_PUBKEY_SIZE);
series->count_total = req_pkt->count;
series->count_sent = 0;
series->count_ok = 0;
series->rtt_sum = 0;
series->timeout_ms = req_pkt->timeout_ms;
series->interval_ms = req_pkt->interval_ms;
int ret = etcp_send_ping_to_socket(bgp->instance, ls, target->node.public_key,
int ret = etcp_send_ping_to_socket(bgp->instance, ls, pubkey_to_use,
&target_addr, req_pkt->timeout_ms, route_ping_cb, series, NULL, 0);
if (ret != 0) {
series->sock_ctx->avg_rtt = req_pkt->timeout_ms * 10;
@ -433,10 +566,13 @@ void route_ping_handle_resp(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn,
uasync_cancel_timeout(bgp->instance->ua, p->timeout_timer);
p->timeout_timer = NULL;
}
if (p->callback) {
int success = (resp->count_ok > 0) ? 1 : 0;
p->callback(success, resp->avg_rtt, resp->count_sent, resp->count_ok, p->arg);
}
if (p->callback) {
int success = (resp->count_ok > 0) ? 1 : 0;
uint32_t recv_ip = (resp->recv_ipv4[0] << 24) | (resp->recv_ipv4[1] << 16) |
(resp->recv_ipv4[2] << 8) | resp->recv_ipv4[3];
uint16_t recv_port = ntohs(resp->recv_port);
p->callback(success, resp->avg_rtt, resp->count_sent, resp->count_ok, recv_ip, recv_port, p->arg);
}
u_free(p);
return;
}

15
src/route_ping.h

@ -25,16 +25,27 @@ struct BGP_PING_RESPONSE {
uint8_t count_sent;
uint8_t count_ok;
uint16_t avg_rtt; // средний RTT в 0.1ms
uint8_t reserved[4];
uint8_t recv_ipv4[4]; // IP:port с которого получен PING_REQ (STUN-like)
uint16_t recv_port; // network byte order
} __attribute__((packed));
typedef void (*route_ping_callback_t)(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok, void* arg);
typedef void (*route_ping_callback_t)(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok,
uint32_t recv_ip, uint16_t recv_port, void* arg);
// Отправить запрос пинга через BGP, ожидать ответа с таймаутом
int route_ping_send_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, uint64_t node_id,
uint8_t count, uint16_t interval_ms, uint16_t timeout_ms,
uint16_t wait_timeout_ms, route_ping_callback_t cb, void* arg);
// Отправить запрос пинга по произвольному IP:port (используется для NAT-детекции)
// Если target_ip == 0, используется node_id из списка известных узлов
// Если pubkey != NULL, он передается в пакете (для пинга без локального nodeinfo)
int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, uint64_t node_id,
uint32_t target_ip, uint16_t target_port,
uint8_t count, uint16_t interval_ms, uint16_t timeout_ms,
uint16_t wait_timeout_ms, route_ping_callback_t cb, void* arg,
const uint8_t* pubkey);
// Очистить список ожидающих запросов (при уничтожении BGP)
void route_ping_destroy_pending(struct ROUTE_BGP* bgp);

2
src/utun_instance.h

@ -44,6 +44,8 @@ struct UTUN_INSTANCE {
uint64_t node_id;
struct SC_MYKEYS my_keys;
uint8_t next_socket_id; // Counter for unique socket IDs (0-255)
// Main async context
struct UASYNC* ua;

5
tests/Makefile.am

@ -25,6 +25,7 @@ check_PROGRAMS = \
test_routing_mesh \
test_etcp_ping \
test_route_ping \
test_nat_detection \
bench_timeout_heap \
bench_uasync_timeouts
@ -180,6 +181,10 @@ test_route_ping_SOURCES = test_route_ping.c
test_route_ping_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
test_route_ping_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS)
test_nat_detection_SOURCES = test_nat_detection.c
test_nat_detection_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
test_nat_detection_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS)
test_ll_queue_SOURCES = test_ll_queue.c
test_ll_queue_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_ll_queue_LDADD = $(COMMON_LIBS)

406
tests/test_nat_detection.c

@ -0,0 +1,406 @@
/**
* @file test_nat_detection.c
* @brief Интеграционный тест детекции типа NAT через три узла
*
* Топология: C1 -> S <- C2
* - S получает NODEINFO от C1 (direct peer)
* - S использует C2 как третий узел для NAT-пинга C1
* - C2 пингует C1, результат возвращается S
* - S отправляет NAT_INFO C1
* - Проверяем корректность заполнения всех NAT-полей
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "test_utils.h"
#include "../src/etcp.h"
#include "../src/etcp_connections.h"
#include "../src/config_parser.h"
#include "../src/config_updater.h"
#include "../src/utun_instance.h"
#include "../src/routing.h"
#include "../src/route_bgp.h"
#include "../src/route_ping.h"
#include "../src/route_node.h"
#include "../src/tun_if.h"
#include "../src/secure_channel.h"
#include "../lib/u_async.h"
#include "../lib/debug_config.h"
#define TEST_TIMEOUT_MS 5000
#define NODE_ID_S 0x1111111111111111ULL
#define NODE_ID_C1 0x2222222222222222ULL
#define NODE_ID_C2 0x3333333333333333ULL
static struct UTUN_INSTANCE* inst_s = NULL;
static struct UTUN_INSTANCE* inst_c1 = NULL;
static struct UTUN_INSTANCE* inst_c2 = NULL;
static struct UASYNC* ua = NULL;
static int test_timed_out = 0;
static void* test_timeout_id = NULL;
static char temp_dir[] = "/tmp/utun_nat_test_XXXXXX";
static char config_s[256];
static char config_c1[256];
static char config_c2[256];
static struct {
int done;
int success;
uint16_t avg_rtt;
uint8_t count_sent;
uint8_t count_ok;
uint32_t recv_ip;
uint16_t recv_port;
} nat_ping_result;
static int write_config(const char* path, const char* content) {
FILE* f = fopen(path, "w");
if (!f) return -1;
fprintf(f, "%s", content);
fclose(f);
return 0;
}
static char* get_pubkey_from_config(const char* path) {
struct utun_config* cfg = parse_config(path);
if (!cfg) return NULL;
char* pub = strdup(cfg->global.my_public_key_hex);
free_config(cfg);
return pub;
}
static int create_temp_configs(void) {
if (test_mkdtemp(temp_dir) != 0) {
fprintf(stderr, "Failed to create temp directory\n");
return -1;
}
snprintf(config_s, sizeof(config_s), "%s/s.conf", temp_dir);
snprintf(config_c1, sizeof(config_c1), "%s/c1.conf", temp_dir);
snprintf(config_c2, sizeof(config_c2), "%s/c2.conf", temp_dir);
const char* tpl_s =
"[global]\n"
"my_node_id=0x1111111111111111\n"
"tun_ip=10.100.0.1/24\n"
"tun_ifname=tun100\n"
"\n"
"[server:test_s]\n"
"addr=127.0.0.1:39011\n"
"type=public\n";
if (write_config(config_s, tpl_s) != 0) return -1;
if (config_ensure_keys_and_node_id(config_s) != 0) return -1;
char* pub_s = get_pubkey_from_config(config_s);
if (!pub_s) return -1;
const char* tpl_c2 =
"[global]\n"
"my_node_id=0x3333333333333333\n"
"tun_ip=10.100.0.3/24\n"
"tun_ifname=tun103\n"
"\n"
"[server:test_c2]\n"
"addr=127.0.0.1:39013\n"
"type=public\n"
"\n"
"[client:to_s]\n"
"keepalive=1\n"
"peer_public_key=%s\n"
"link=test_c2:127.0.0.1:39011\n";
char tpl_c2_full[4096];
snprintf(tpl_c2_full, sizeof(tpl_c2_full), tpl_c2, pub_s);
if (write_config(config_c2, tpl_c2_full) != 0) { free(pub_s); return -1; }
if (config_ensure_keys_and_node_id(config_c2) != 0) { free(pub_s); return -1; }
char* pub_c2 = get_pubkey_from_config(config_c2);
if (!pub_c2) { free(pub_s); return -1; }
const char* tpl_c1 =
"[global]\n"
"my_node_id=0x2222222222222222\n"
"tun_ip=10.100.0.2/24\n"
"tun_ifname=tun102\n"
"\n"
"[server:test_c1]\n"
"addr=127.0.0.1:39012\n"
"type=public\n"
"\n"
"[client:to_s]\n"
"keepalive=1\n"
"peer_public_key=%s\n"
"link=test_c1:127.0.0.1:39011\n";
char tpl_c1_full[4096];
snprintf(tpl_c1_full, sizeof(tpl_c1_full), tpl_c1, pub_s);
free(pub_s);
if (write_config(config_c1, tpl_c1_full) != 0) { free(pub_c2); return -1; }
if (config_ensure_keys_and_node_id(config_c1) != 0) { free(pub_c2); return -1; }
free(pub_c2);
return 0;
}
static void cleanup_temp_configs(void) {
test_unlink(config_s);
test_unlink(config_c1);
test_unlink(config_c2);
test_rmdir(temp_dir);
}
static int has_initialized_link(struct UTUN_INSTANCE* inst) {
if (!inst) return 0;
struct ETCP_CONN* conn = inst->connections;
while (conn) {
struct ETCP_LINK* link = conn->links;
while (link) {
if (link->initialized) return 1;
link = link->next;
}
conn = conn->next;
}
return 0;
}
static void test_timeout_cb(void* arg) {
(void)arg;
test_timed_out = 1;
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "test_nat_detection: overall test timeout");
}
static void nat_ping_resp_cb(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok,
uint32_t recv_ip, uint16_t recv_port, void* arg) {
(void)arg;
nat_ping_result.done = 1;
nat_ping_result.success = success;
nat_ping_result.avg_rtt = avg_rtt;
nat_ping_result.count_sent = count_sent;
nat_ping_result.count_ok = count_ok;
nat_ping_result.recv_ip = recv_ip;
nat_ping_result.recv_port = recv_port;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "nat_ping_resp_cb: success=%d avg_rtt=%u sent=%u ok=%u recv_ip=%08x recv_port=%u",
success, (unsigned)avg_rtt, (unsigned)count_sent, (unsigned)count_ok,
(unsigned)recv_ip, (unsigned)recv_port);
}
int main(void) {
int test_result = 1;
debug_config_init();
debug_set_level(DEBUG_LEVEL_INFO);
debug_set_categories(DEBUG_CATEGORY_ETCP | DEBUG_CATEGORY_BGP | DEBUG_CATEGORY_ROUTING);
utun_instance_set_tun_init_enabled(0);
if (create_temp_configs() != 0) {
fprintf(stderr, "Failed to create temp configs\n");
return 1;
}
ua = uasync_create();
if (!ua) {
cleanup_temp_configs();
return 1;
}
inst_s = utun_instance_create(ua, config_s);
inst_c1 = utun_instance_create(ua, config_c1);
inst_c2 = utun_instance_create(ua, config_c2);
if (!inst_s || !inst_c1 || !inst_c2) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create instances");
goto cleanup;
}
if (init_connections(inst_s) != 0 || init_connections(inst_c1) != 0 || init_connections(inst_c2) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to init connections");
goto cleanup;
}
test_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS * 10, NULL, test_timeout_cb);
// 1. Wait for links C1->S and C2->S to initialize
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Waiting for ETCP links to initialize...");
// Give some time for connections to establish before checking
for (int i = 0; i < 100; i++) {
uasync_poll(ua, 10);
}
while (!test_timed_out) {
if (has_initialized_link(inst_c1) && has_initialized_link(inst_c2)) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Links C1->S and C2->S initialized");
break;
}
uasync_poll(ua, 10);
}
if (test_timed_out) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Timeout waiting for links");
goto cleanup;
}
// 2. Wait for BGP exchange so S learns about C1 and C2
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Waiting for BGP exchange (S learns C1 and C2)...");
int bgp_wait_cycles = 0;
while (!test_timed_out && bgp_wait_cycles < 500) {
if (inst_s->bgp && route_bgp_get_node(inst_s->bgp, NODE_ID_C1) != NULL &&
route_bgp_get_node(inst_s->bgp, NODE_ID_C2) != NULL) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "S learned about C1 and C2");
break;
}
uasync_poll(ua, 10);
bgp_wait_cycles++;
}
if (!inst_s->bgp || route_bgp_get_node(inst_s->bgp, NODE_ID_C1) == NULL ||
route_bgp_get_node(inst_s->bgp, NODE_ID_C2) == NULL) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "S did not learn about C1/C2 in time");
goto cleanup;
}
// 3. Wait for NAT detection to complete for C1 on server
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) {
struct NODEINFO_Q* node_c1 = route_bgp_get_node(inst_s->bgp, NODE_ID_C1);
if (node_c1 && node_c1->nat_check_status == NAT_CHECK_OPEN) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT detection completed for C1: OPEN");
break;
}
uasync_poll(ua, 10);
bgp_wait_cycles++;
}
struct NODEINFO_Q* node_c1 = route_bgp_get_node(inst_s->bgp, NODE_ID_C1);
if (!node_c1 || node_c1->nat_check_status != NAT_CHECK_OPEN) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NAT detection did not complete for C1");
goto cleanup;
}
// 4. Wait for C1 to receive NAT_INFO
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Waiting for C1 to receive NAT_INFO...");
bgp_wait_cycles = 0;
while (!test_timed_out && bgp_wait_cycles < 500) {
struct ETCP_SOCKET* sock = inst_c1->etcp_sockets;
int found = 0;
while (sock) {
if (sock->nat_type == NAT_TYPE_OPEN) { found = 1; break; }
sock = sock->next;
}
if (found) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "C1 received NAT_INFO");
break;
}
uasync_poll(ua, 10);
bgp_wait_cycles++;
}
// 5. Verify all NAT fields on server
node_c1 = route_bgp_get_node(inst_s->bgp, NODE_ID_C1);
if (!node_c1) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO_Q for C1 disappeared");
goto cleanup;
}
if (node_c1->nat_check_status != NAT_CHECK_OPEN) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: nat_check_status=%d, expected OPEN(%d)",
(int)node_c1->nat_check_status, NAT_CHECK_OPEN);
goto cleanup;
}
if (node_c1->nat_type != NAT_TYPE_OPEN) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: nat_type=%d, expected OPEN(%d)",
(int)node_c1->nat_type, NAT_TYPE_OPEN);
goto cleanup;
}
if (node_c1->nat_ip == 0 || node_c1->nat_port == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: nat_ip/port not set (ip=%08x port=%u)",
(unsigned)node_c1->nat_ip, (unsigned)node_c1->nat_port);
goto cleanup;
}
// Verify link nat_type on server
struct ETCP_CONN* conn_sc1 = NULL;
struct ETCP_LINK* link_sc1 = NULL;
struct ETCP_CONN* conn = inst_s->connections;
while (conn) {
if (conn->peer_node_id == NODE_ID_C1) {
conn_sc1 = conn;
link_sc1 = conn->links;
break;
}
conn = conn->next;
}
if (!link_sc1 || link_sc1->nat_type != NAT_TYPE_OPEN) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: link nat_type not OPEN on server");
goto cleanup;
}
// Verify socket nat_type on C1
struct ETCP_SOCKET* sock_c1 = NULL;
struct ETCP_SOCKET* sock = inst_c1->etcp_sockets;
while (sock) {
if (sock->nat_type == NAT_TYPE_OPEN) {
sock_c1 = sock;
break;
}
sock = sock->next;
}
if (!sock_c1) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: no socket with nat_type OPEN on C1");
goto cleanup;
}
// 6. Separate STUN check via explicit route_ping_send_req_addr
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Performing explicit STUN ping from S via C2 to C1...");
struct ETCP_CONN* conn_sc2 = NULL;
conn = inst_s->connections;
while (conn) {
if (conn->peer_node_id == NODE_ID_C2) { conn_sc2 = conn; break; }
conn = conn->next;
}
if (!conn_sc2) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: S has no connection to C2");
goto cleanup;
}
memset(&nat_ping_result, 0, sizeof(nat_ping_result));
int ret = route_ping_send_req_addr(inst_s->bgp, conn_sc2, NODE_ID_C1,
node_c1->nat_ip, node_c1->nat_port,
3, 10, 200, 3000, nat_ping_resp_cb, NULL,
node_c1->node.public_key);
if (ret != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr failed: %d", ret);
goto cleanup;
}
bgp_wait_cycles = 0;
while (!test_timed_out && bgp_wait_cycles < 500 && !nat_ping_result.done) {
uasync_poll(ua, 10);
bgp_wait_cycles++;
}
if (!nat_ping_result.done) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: STUN ping timeout");
goto cleanup;
}
if (!nat_ping_result.success || nat_ping_result.count_ok == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: STUN ping failed success=%d ok=%u",
nat_ping_result.success, (unsigned)nat_ping_result.count_ok);
goto cleanup;
}
if (nat_ping_result.recv_ip == 0 || nat_ping_result.recv_port == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: STUN recv_ip/port zero (ip=%08x port=%u)",
(unsigned)nat_ping_result.recv_ip, (unsigned)nat_ping_result.recv_port);
goto cleanup;
}
// recv_ip should match C2's socket address (127.0.0.1)
if (nat_ping_result.recv_ip != 0x7F000001) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "STUN recv_ip=%08x (expected 127.0.0.1), tolerating",
(unsigned)nat_ping_result.recv_ip);
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "STUN check PASSED: recv_ip=%08x recv_port=%u",
(unsigned)nat_ping_result.recv_ip, (unsigned)nat_ping_result.recv_port);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT detection test PASSED");
test_result = 0;
cleanup:
if (test_timeout_id) uasync_cancel_timeout(ua, test_timeout_id);
if (inst_s) utun_instance_destroy(inst_s);
if (inst_c1) utun_instance_destroy(inst_c1);
if (inst_c2) utun_instance_destroy(inst_c2);
if (ua) uasync_destroy(ua, 0);
cleanup_temp_configs();
return test_result;
}

10
tests/test_route_ping.c

@ -177,15 +177,17 @@ static void test_timeout_cb(void* arg) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "test_route_ping: overall test timeout");
}
static void ping_resp_cb(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok, void* arg) {
(void)arg;
static void ping_resp_cb(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok,
uint32_t recv_ip, uint16_t recv_port, void* arg) {
(void)arg; (void)recv_ip; (void)recv_port;
ping_result.done = 1;
ping_result.success = success;
ping_result.avg_rtt = avg_rtt;
ping_result.count_sent = count_sent;
ping_result.count_ok = count_ok;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "ping_resp_cb: success=%d avg_rtt=%u sent=%u ok=%u",
success, (unsigned)avg_rtt, (unsigned)count_sent, (unsigned)count_ok);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "ping_resp_cb: success=%d avg_rtt=%u sent=%u ok=%u recv_ip=%08x recv_port=%u",
success, (unsigned)avg_rtt, (unsigned)count_sent, (unsigned)count_ok,
(unsigned)recv_ip, (unsigned)recv_port);
}
int main(void) {

Loading…
Cancel
Save