Browse Source

feat: NAT_DIRECT detection during INIT handshake + refactor init packet structs

- Extract ETCP_INIT_REQUEST_PKT / ETCP_INIT_RESPONSE_PKT named structs
- Replace anonymous structs and manual offset parsing with typed struct access
- Add src_ipv4/src_port fields (V2) to INIT REQUEST for DIRECT detection
- Server compares reported client addr with observed src; match + public IP → NAT_TYPE_DIRECT
- Skip STUN NAT check for DIRECT links
- route_bgp: handle NAT_TYPE_DIRECT → NAT_VERIFIED_DIRECT in handle_nat_info
feature/x25519-migration
Evgeny 3 months ago
parent
commit
69112ffde1
  1. 222
      src/etcp_connections.c
  2. 40
      src/etcp_connections.h
  3. 2
      src/route_bgp.c

222
src/etcp_connections.c

@ -112,41 +112,27 @@ static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset) {
dgram->link = link; dgram->link = link;
dgram->noencrypt_len = SC_PUBKEY_ENC_SIZE; dgram->noencrypt_len = SC_PUBKEY_ENC_SIZE;
size_t offset = 0;
// reset=1: ETCP_INIT_REQUEST (0x02), reset=0: ETCP_INIT_REQUEST_NOINIT (0x04)
dgram->data[offset++] = reset ? ETCP_INIT_REQUEST : ETCP_INIT_REQUEST_NOINIT;
uint64_t node_id = link->etcp->instance->node_id;
dgram->data[offset++] = (node_id >> 56) & 0xFF;
dgram->data[offset++] = (node_id >> 48) & 0xFF;
dgram->data[offset++] = (node_id >> 40) & 0xFF;
dgram->data[offset++] = (node_id >> 32) & 0xFF;
dgram->data[offset++] = (node_id >> 24) & 0xFF;
dgram->data[offset++] = (node_id >> 16) & 0xFF;
dgram->data[offset++] = (node_id >> 8) & 0xFF;
dgram->data[offset++] = node_id & 0xFF;
// session_id (4 bytes) - для защиты от ложного reinit
uint32_t session_id = link->etcp->session_id;
dgram->data[offset++] = (session_id >> 24) & 0xFF;
dgram->data[offset++] = (session_id >> 16) & 0xFF;
dgram->data[offset++] = (session_id >> 8) & 0xFF;
dgram->data[offset++] = session_id & 0xFF;
dgram->data[offset++] = (link->mtu_local >> 8) & 0xFF;
dgram->data[offset++] = link->mtu_local & 0xFF;
dgram->data[offset++] = (link->keepalive_interval >> 8) & 0xFF;
dgram->data[offset++] = link->keepalive_interval & 0xFF;
dgram->data[offset++] = ((link->recovery_interval/100) >> 8) & 0xFF;
dgram->data[offset++] = (link->recovery_interval/100) & 0xFF;
dgram->data[offset++] = link->local_link_id; struct ETCP_INIT_REQUEST_PKT* req = (struct ETCP_INIT_REQUEST_PKT*)dgram->data;
dgram->data[offset++] = link->conn ? link->conn->sock_id : 0; req->code = reset ? ETCP_INIT_REQUEST : ETCP_INIT_REQUEST_NOINIT;
dgram->data[offset++] = link->conn ? link->conn->only_local : 0; *(uint64_t*)req->node_id = htobe64(link->etcp->instance->node_id);
dgram->data[offset++] = link->conn ? link->conn->type : CFG_SERVER_TYPE_UNKNOWN; *(uint32_t*)req->session_id = htobe32(link->etcp->session_id);
*(uint16_t*)req->mtu = htobe16(link->mtu_local);
*(uint16_t*)req->keepalive = htobe16(link->keepalive_interval);
*(uint16_t*)req->recovery = htobe16(link->recovery_interval / 100);
req->link_id = link->local_link_id;
req->socket_id = link->conn ? link->conn->sock_id : 0;
req->only_local = link->conn ? link->conn->only_local : 0;
req->type = link->conn ? link->conn->type : CFG_SERVER_TYPE_UNKNOWN;
if (link->conn && link->conn->interface_addr.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&link->conn->interface_addr;
memcpy(req->src_ipv4, &sin->sin_addr.s_addr, 4);
*(uint16_t*)req->src_port = sin->sin_port;
} else {
memset(req->src_ipv4, 0, 4);
memset(req->src_port, 0, 2);
}
size_t offset = ETCP_INIT_REQ_V2_SIZE;
// padding // padding
int s = rand() % (link->handshake_maxsize - link->handshake_minsize) + link->handshake_minsize; int s = rand() % (link->handshake_maxsize - link->handshake_minsize) + link->handshake_minsize;
@ -179,7 +165,7 @@ static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset) {
dgram->data_len = offset; dgram->data_len = offset;
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Sending INIT request to link, node_id=%016llx, retry=%d", (unsigned long long)node_id, link->init_retry_count); DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Sending INIT request to link, node_id=%016llx, retry=%d", (unsigned long long)link->etcp->instance->node_id, link->init_retry_count);
// Debug: print remote address before sending // Debug: print remote address before sending
if (link->remote_addr.ss_family == AF_INET) { if (link->remote_addr.ss_family == AF_INET) {
@ -1263,21 +1249,9 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
pkt->data_len=pkt_len-3; pkt->data_len=pkt_len-3;
pkt->noencrypt_len=0; pkt->noencrypt_len=0;
struct { uint8_t code = pkt->data[0];
uint8_t code; uint64_t peer_id = be64toh(*(uint64_t*)(pkt->data + 1));
uint8_t id[8]; if (code == ETCP_PING) {
uint8_t session_id[4];
uint8_t mtu[2];
uint8_t keepalive[2];
uint8_t recovery[2];
uint8_t link_id;
uint8_t remote_socket_id;
uint8_t only_local;
uint8_t type;
uint8_t pubkey[SC_PUBKEY_SIZE];
} *ack_hdr=(void*)&pkt->data[0];
uint64_t peer_id = be64toh(*(uint64_t*)ack_hdr->id);
if (ack_hdr->code == ETCP_PING) {
if (pkt_len < 22) { if (pkt_len < 22) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "PING too short: pkt_len=%zu from %s", pkt_len, sockaddr_storage_to_str(&addr).str); DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "PING too short: pkt_len=%zu from %s", pkt_len, sockaddr_storage_to_str(&addr).str);
errorcode=7; errorcode=7;
@ -1319,7 +1293,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
memory_pool_free(e_sock->instance->pkt_pool, pkt); memory_pool_free(e_sock->instance->pkt_pool, pkt);
return; return;
} }
if (ack_hdr->code == ETCP_PONG) { if (code == ETCP_PONG) {
if (pkt_len < 20) { if (pkt_len < 20) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "PONG too short: pkt_len=%zu from %s", pkt_len, sockaddr_storage_to_str(&addr).str); DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "PONG too short: pkt_len=%zu from %s", pkt_len, sockaddr_storage_to_str(&addr).str);
errorcode=7; errorcode=7;
@ -1368,11 +1342,16 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
memory_pool_free(e_sock->instance->pkt_pool, pkt); memory_pool_free(e_sock->instance->pkt_pool, pkt);
return; return;
} }
if (ack_hdr->code!=ETCP_INIT_REQUEST && ack_hdr->code!=ETCP_INIT_REQUEST_NOINIT) { if (code!=ETCP_INIT_REQUEST && code!=ETCP_INIT_REQUEST_NOINIT) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "not an init packet, code=%02x", ack_hdr->code); DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "not an init packet, code=%02x", code);
errorcode=4; errorcode=4;
goto ec_fr; goto ec_fr;
}// не init }// не init
if (pkt_len < ETCP_INIT_REQ_V1_SIZE) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "INIT REQUEST too short: pkt_len=%zu from %s", pkt_len, sockaddr_storage_to_str(&addr).str);
errorcode=7;
goto ec_fr;
}
// Check allowed keys for incoming connections // Check allowed keys for incoming connections
struct global_config *global = &e_sock->instance->config->global; struct global_config *global = &e_sock->instance->config->global;
@ -1398,7 +1377,8 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
} }
} }
uint32_t session_id = be32toh(*(uint32_t*)ack_hdr->session_id); struct ETCP_INIT_REQUEST_PKT* req = (struct ETCP_INIT_REQUEST_PKT*)pkt->data;
uint32_t session_id = be32toh(*(uint32_t*)req->session_id);
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "INIT request session_id=%08x", session_id); DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "INIT request session_id=%08x", session_id);
struct ETCP_CONN* conn=e_sock->instance->connections; struct ETCP_CONN* conn=e_sock->instance->connections;
@ -1428,15 +1408,15 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
// Check if link already exists (for CHANNEL_INIT recovery) // Check if link already exists (for CHANNEL_INIT recovery)
struct ETCP_LINK* existing_link = etcp_link_find_by_remote_id(conn, ack_hdr->link_id); struct ETCP_LINK* existing_link = etcp_link_find_by_remote_id(conn, req->link_id);
uint8_t send_reset = 0; uint8_t send_reset = 0;
if (existing_link && existing_link->etcp == conn) {// существующий линк if (existing_link && existing_link->etcp == conn) {// существующий линк
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] found existing link for id=%d, socket=[%s]", conn->log_name, ack_hdr->link_id, e_sock->name); DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] found existing link for id=%d, socket=[%s]", conn->log_name, req->link_id, e_sock->name);
link = existing_link; link = existing_link;
if (!sockaddr_equal(&link->remote_addr, &addr)) { if (!sockaddr_equal(&link->remote_addr, &addr)) {
DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "[%s] IP:port changed for remote_link_id=%d socket:[%s]", conn->log_name, ack_hdr->link_id, e_sock->name); DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "[%s] IP:port changed for remote_link_id=%d socket:[%s]", conn->log_name, req->link_id, e_sock->name);
if (link->conn) remove_link(link->conn, link->ip_port_hash); // remove old connection from old socket if (link->conn) remove_link(link->conn, link->ip_port_hash); // remove old connection from old socket
link->conn=e_sock; link->conn=e_sock;
memcpy(&link->remote_addr, &addr, sizeof(addr)); memcpy(&link->remote_addr, &addr, sizeof(addr));
@ -1454,10 +1434,10 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
} }
// Link exists - reuse it for recovery // Link exists - reuse it for recovery
link->remote_link_id = ack_hdr->link_id; link->remote_link_id = req->link_id;
link->remote_socket_id = ack_hdr->remote_socket_id; link->remote_socket_id = req->socket_id;
link->remote_only_local = ack_hdr->only_local; link->remote_only_local = req->only_local;
link->remote_type = ack_hdr->type; link->remote_type = req->type;
// For CHANNEL_INIT (0x04): if link already initialized - no reset, otherwise reset // For CHANNEL_INIT (0x04): if link already initialized - no reset, otherwise reset
// For INIT_REQUEST (0x02): always reset // For INIT_REQUEST (0x02): always reset
@ -1478,14 +1458,14 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
link->init_timer = NULL; link->init_timer = NULL;
} }
} else { } else {
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] NO existing link for id=%d, socket=[%s]", conn->log_name, ack_hdr->link_id, e_sock->name); DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] NO existing link for id=%d, socket=[%s]", conn->log_name, req->link_id, e_sock->name);
// Create new link // Create new link
link = etcp_link_new(conn, e_sock, &addr, 1); 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; }// облом 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_link_id = req->link_id;
link->remote_socket_id = ack_hdr->remote_socket_id; link->remote_socket_id = req->socket_id;
link->remote_only_local = ack_hdr->only_local; link->remote_only_local = req->only_local;
link->remote_type = ack_hdr->type; link->remote_type = req->type;
// For new links: check session_id to avoid false reinit // For new links: check session_id to avoid false reinit
if (conn->session_id != session_id) { if (conn->session_id != session_id) {
@ -1494,71 +1474,80 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
conn->session_id = session_id; conn->session_id = session_id;
etcp_conn_reinit(conn); etcp_conn_reinit(conn);
} }
link->keepalive_interval=(ack_hdr->keepalive[0]<<8) | ack_hdr->keepalive[1]; link->keepalive_interval=(req->keepalive[0]<<8) | req->keepalive[1];
link->recovery_interval=((ack_hdr->recovery[0]<<8) | ack_hdr->recovery[1])*100;// timebase в link, timebase/100 в кодограмме link->recovery_interval=((req->recovery[0]<<8) | req->recovery[1])*100;// timebase в link, timebase/100 в кодограмме
if (link->keepalive_interval < 10) link->keepalive_interval = 10; if (link->keepalive_interval < 10) link->keepalive_interval = 10;
DEBUG_DEBUG(DEBUG_CATEGORY_KEEPALIVE, "set keepalive for link=%d", link->keepalive_interval); DEBUG_DEBUG(DEBUG_CATEGORY_KEEPALIVE, "set keepalive for link=%d", link->keepalive_interval);
} }
link->mtu_remote = (ack_hdr->mtu[0] << 8) | ack_hdr->mtu[1]; link->mtu_remote = (req->mtu[0] << 8) | req->mtu[1];
if (link->mtu_remote > PACKET_DATA_MAX_MTU) link->mtu_remote = PACKET_DATA_MAX_MTU; if (link->mtu_remote > PACKET_DATA_MAX_MTU) link->mtu_remote = PACKET_DATA_MAX_MTU;
link->mtu = link->mtu_local < link->mtu_remote ? link->mtu_local : link->mtu_remote; link->mtu = link->mtu_local < link->mtu_remote ? link->mtu_local : link->mtu_remote;
etcp_update_mtu(link->etcp); etcp_update_mtu(link->etcp);
struct { struct ETCP_INIT_RESPONSE_PKT* resp = (struct ETCP_INIT_RESPONSE_PKT*)pkt->data;
uint8_t code;
uint8_t id[8];
uint8_t session_id[4];
uint8_t mtu[2];
uint8_t link_id;
uint8_t remote_socket_id;
uint8_t only_local;
uint8_t type;
uint8_t peer_ipv4[4];
uint8_t peer_port[2];
} *ack_repl_hdr=(void*)&pkt->data[0];
// Set response code: 0x03 (with reset) or 0x05 (without reset) // Set response code: 0x03 (with reset) or 0x05 (without reset)
// response with init (0x03) only if reinit was actually done on server side // response with init (0x03) only if reinit was actually done on server side
if (send_reset != 0) { if (send_reset != 0) {
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "send init_response with reset"); DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "send init_response with reset");
ack_repl_hdr->code = ETCP_INIT_RESPONSE; // 0x03 - with reset resp->code = ETCP_INIT_RESPONSE; // 0x03 - with reset
} else { } else {
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "send init_response without reset"); DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "send init_response without reset");
ack_repl_hdr->code = ETCP_INIT_RESPONSE_NOINIT; // 0x05 - without reset resp->code = ETCP_INIT_RESPONSE_NOINIT; // 0x05 - without reset
} }
*(uint64_t*)ack_repl_hdr->id = htobe64(e_sock->instance->node_id); *(uint64_t*)resp->node_id = htobe64(e_sock->instance->node_id);
*(uint32_t*)ack_repl_hdr->session_id = htobe32(conn->session_id); *(uint32_t*)resp->session_id = htobe32(conn->session_id);
ack_repl_hdr->mtu[0]=link->mtu_local>>8; resp->mtu[0]=link->mtu_local>>8;
ack_repl_hdr->mtu[1]=link->mtu_local; resp->mtu[1]=link->mtu_local;
ack_repl_hdr->link_id = link->local_link_id; resp->link_id = link->local_link_id;
ack_repl_hdr->remote_socket_id = ack_hdr->remote_socket_id; resp->remote_socket_id = req->socket_id;
ack_repl_hdr->only_local = e_sock->only_local; resp->only_local = e_sock->only_local;
ack_repl_hdr->type = e_sock->type; resp->type = e_sock->type;
// Add client's IP:port (so client behind NAT can know its external address) // Add client's IP:port (so client behind NAT can know its external address)
if (addr.ss_family == AF_INET) { if (addr.ss_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in*)&addr; struct sockaddr_in *sin = (struct sockaddr_in*)&addr;
memcpy(ack_repl_hdr->peer_ipv4, &sin->sin_addr.s_addr, 4); memcpy(resp->peer_ipv4, &sin->sin_addr.s_addr, 4);
uint16_t port = ntohs(sin->sin_port); uint16_t port = ntohs(sin->sin_port);
ack_repl_hdr->peer_port[0] = port >> 8; resp->peer_port[0] = port >> 8;
ack_repl_hdr->peer_port[1] = port & 0xFF; resp->peer_port[1] = port & 0xFF;
link->nat_ip = sin->sin_addr.s_addr; link->nat_ip = sin->sin_addr.s_addr;
link->nat_port = port; link->nat_port = port;
} else { } else {
// For IPv6, set to 0 (not supported for NAT traversal) // For IPv6, set to 0 (not supported for NAT traversal)
memset(ack_repl_hdr->peer_ipv4, 0, 4); memset(resp->peer_ipv4, 0, 4);
memset(ack_repl_hdr->peer_port, 0, 2); memset(resp->peer_port, 0, 2);
link->nat_ip = 0; link->nat_ip = 0;
link->nat_port = 0; link->nat_port = 0;
} }
// DIRECT detection: if client reports its own address and it matches observed source → real public IP
if (pkt_len >= ETCP_INIT_REQ_V2_SIZE && addr.ss_family == AF_INET && link->nat_ip != 0) {
uint32_t reported_ip;
memcpy(&reported_ip, req->src_ipv4, 4);
uint16_t reported_port = be16toh(*(uint16_t*)req->src_port);
if (reported_ip != 0 && reported_ip == link->nat_ip
&& reported_port == link->nat_port
&& !is_local_subnet(link->nat_ip))
{
link->nat_type = NAT_TYPE_DIRECT;
link->nat_check_status = NAT_CHECK_EIM;
if (link->etcp->instance->bgp) {
route_bgp_send_nat_info(link->etcp, link->remote_socket_id,
link->nat_ip, link->nat_port, NAT_TYPE_DIRECT);
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "DIRECT IP: %s:%u for %s",
ip_to_str(&link->nat_ip, AF_INET).str, link->nat_port,
link->etcp->log_name);
}
}
pkt->noencrypt_len=0; pkt->noencrypt_len=0;
pkt->link=link; pkt->link=link;
link->recv_keepalive = 1; link->recv_keepalive = 1;
link->last_recv_local_time = get_time_tb(); link->last_recv_local_time = get_time_tb();
link->last_recv_timestamp = pkt->timestamp; link->last_recv_timestamp = pkt->timestamp;
int xoffset=sizeof(*ack_repl_hdr); int xoffset=sizeof(struct ETCP_INIT_RESPONSE_PKT);
// padding // padding
int s = rand() % (link->handshake_maxsize - link->handshake_minsize) + link->handshake_minsize; int s = rand() % (link->handshake_maxsize - link->handshake_minsize) + link->handshake_minsize;
if (s > (int)(link->mtu)) s = (int)(link->mtu); if (s > (int)(link->mtu)) s = (int)(link->mtu);
@ -1625,9 +1614,9 @@ process_decrypted:
link->total_decrypted += pkt->data_len; link->total_decrypted += pkt->data_len;
size_t offset = 0; size_t offset = 0;
uint8_t code = pkt->data[offset++]; uint8_t pkt_code = pkt->data[offset++];
if (code == ETCP_KEEPALIVE) { if (pkt_code == ETCP_KEEPALIVE) {
if (pkt->data_len >= 3) { if (pkt->data_len >= 3) {
uint16_t peer_period = pkt->data[1] | ((uint16_t)pkt->data[2] << 8); uint16_t peer_period = pkt->data[1] | ((uint16_t)pkt->data[2] << 8);
link->keepalive_timeout = (uint32_t)peer_period * KA_TIMEOUT_MULT; link->keepalive_timeout = (uint32_t)peer_period * KA_TIMEOUT_MULT;
@ -1637,16 +1626,13 @@ process_decrypted:
return; // KA handled, nothing more to process return; // KA handled, nothing more to process
} }
if (code == ETCP_INIT_RESPONSE || code == ETCP_INIT_RESPONSE_NOINIT) { if (pkt_code == ETCP_INIT_RESPONSE || pkt_code == ETCP_INIT_RESPONSE_NOINIT) {
if (pkt_len < 22) { errorcode = 46; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "INIT_RESPONSE too short: pkt_len=%zu", pkt_len); goto ec_fr; } if (pkt_len < ETCP_INIT_RESP_V1_SIZE) { errorcode = 46; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "INIT_RESPONSE too short: pkt_len=%zu", pkt_len); goto ec_fr; }
// ETCP_INIT_RESPONSE (0x03) - reset entire ETCP_CONN // ETCP_INIT_RESPONSE (0x03) - reset entire ETCP_CONN
// ETCP_INIT_RESPONSE_NOINIT (0x05) - no reset // ETCP_INIT_RESPONSE_NOINIT (0x05) - no reset
uint64_t server_node_id = 0; struct ETCP_INIT_RESPONSE_PKT* resp = (struct ETCP_INIT_RESPONSE_PKT*)pkt->data;
for (int i = 0; i < 8; i++) { uint64_t server_node_id = be64toh(*(uint64_t*)resp->node_id);
server_node_id = (server_node_id << 8) | pkt->data[offset++]; uint32_t resp_session_id = be32toh(*(uint32_t*)resp->session_id);
}
uint32_t resp_session_id = (pkt->data[offset] << 24) | (pkt->data[offset+1] << 16) | (pkt->data[offset+2] << 8) | pkt->data[offset+3];
offset += 4;
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "INIT_RESPONSE session_id=%08x", resp_session_id); DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "INIT_RESPONSE session_id=%08x", resp_session_id);
// Check session_id: ignore response if it doesn't match our session // Check session_id: ignore response if it doesn't match our session
if (resp_session_id != link->etcp->session_id) { if (resp_session_id != link->etcp->session_id) {
@ -1655,23 +1641,20 @@ process_decrypted:
memory_pool_free(e_sock->instance->pkt_pool, pkt); memory_pool_free(e_sock->instance->pkt_pool, pkt);
return; return;
} }
link->mtu_remote = ((uint16_t)pkt->data[offset] << 8) | pkt->data[offset + 1]; link->mtu_remote = be16toh(*(uint16_t*)resp->mtu);
offset += 2;
if (link->mtu_remote > PACKET_DATA_MAX_MTU) link->mtu_remote = PACKET_DATA_MAX_MTU; if (link->mtu_remote > PACKET_DATA_MAX_MTU) link->mtu_remote = PACKET_DATA_MAX_MTU;
link->mtu = link->mtu_local < link->mtu_remote ? link->mtu_local : link->mtu_remote; link->mtu = link->mtu_local < link->mtu_remote ? link->mtu_local : link->mtu_remote;
etcp_update_mtu(link->etcp); etcp_update_mtu(link->etcp);
link->remote_link_id = pkt->data[offset++]; link->remote_link_id = resp->link_id;
link->remote_socket_id = pkt->data[offset++]; link->remote_socket_id = resp->remote_socket_id;
link->remote_only_local = pkt->data[offset++]; link->remote_only_local = resp->only_local;
link->remote_type = pkt->data[offset++]; link->remote_type = resp->type;
// Parse NAT IP:port from response (new format includes 4+2 bytes) // Parse NAT IP:port from response (new format includes 4+2 bytes)
if (pkt_len >= 24) { if (pkt_len >= ETCP_INIT_RESP_V2_SIZE) {
uint32_t new_nat_ip; uint32_t new_nat_ip;
memcpy(&new_nat_ip, &pkt->data[offset], 4); memcpy(&new_nat_ip, resp->peer_ipv4, 4);
offset += 4; uint16_t new_nat_port = be16toh(*(uint16_t*)resp->peer_port);
uint16_t new_nat_port = (pkt->data[offset] << 8) | pkt->data[offset+1];
offset += 2;
// Check if NAT address changed // Check if NAT address changed
if (link->nat_ip == 0 && link->nat_port == 0) { if (link->nat_ip == 0 && link->nat_port == 0) {
@ -1710,7 +1693,6 @@ process_decrypted:
link->etcp->log_name); link->etcp->log_name);
} }
if (offset > pkt_len) { errorcode=13; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: packet parsing overflow, offset=%zu, pkt_len=%zu", offset, pkt_len); goto ec_fr; }
// DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Received INIT_RESPONSE from server_node_id=%llu, mtu=%d", (unsigned long long)server_node_id, link->mtu); // DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Received INIT_RESPONSE from server_node_id=%llu, mtu=%d", (unsigned long long)server_node_id, link->mtu);
@ -1724,7 +1706,7 @@ process_decrypted:
link->init_timer = NULL; link->init_timer = NULL;
} }
if (code == ETCP_INIT_RESPONSE) { if (pkt_code == ETCP_INIT_RESPONSE) {
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "do reinit 3 %p", link->etcp); DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "do reinit 3 %p", link->etcp);
etcp_conn_reinit(link->etcp); etcp_conn_reinit(link->etcp);
} }

40
src/etcp_connections.h

@ -54,6 +54,45 @@ struct ETCP_DGRAM {// пакет (незашифрованный)
/* Размер базового ACK: type(1) + count(1) + last_delivered_id(4) + rx_dup_count(2) */ /* Размер базового ACK: type(1) + count(1) + last_delivered_id(4) + rx_dup_count(2) */
#define ETCP_ACK_BASE_SIZE (2 + 4 + 2) #define ETCP_ACK_BASE_SIZE (2 + 4 + 2)
// --- INIT packet wire formats (inside encrypted payload, before padding) ---
// INIT REQUEST: client → server
struct ETCP_INIT_REQUEST_PKT {
uint8_t code; // 0: ETCP_INIT_REQUEST (0x02) или ETCP_INIT_REQUEST_NOINIT (0x04)
uint8_t node_id[8]; // 1: sender node_id (big-endian)
uint8_t session_id[4]; // 9: session (big-endian)
uint8_t mtu[2]; // 13: client MTU (big-endian)
uint8_t keepalive[2]; // 15: keepalive interval (big-endian)
uint8_t recovery[2]; // 17: recovery interval/100 (big-endian)
uint8_t link_id; // 19: client's local link id
uint8_t socket_id; // 20: client's socket id
uint8_t only_local; // 21: client only_local flag
uint8_t type; // 22: client socket type (CFG_SERVER_TYPE_*)
// V2 fields (NAT_DIRECT detection):
uint8_t src_ipv4[4]; // 23: client interface_addr IPv4 (big-endian, 0 if N/A)
uint8_t src_port[2]; // 27: client interface_addr port (big-endian)
} __attribute__((packed));
#define ETCP_INIT_REQ_V1_SIZE 23 // size without src_ipv4/src_port (backward compat)
#define ETCP_INIT_REQ_V2_SIZE 29 // size with src_ipv4/src_port
// INIT RESPONSE: server → client
struct ETCP_INIT_RESPONSE_PKT {
uint8_t code; // 0: ETCP_INIT_RESPONSE (0x03) или ETCP_INIT_RESPONSE_NOINIT (0x05)
uint8_t node_id[8]; // 1: server node_id (big-endian)
uint8_t session_id[4]; // 9: session (big-endian)
uint8_t mtu[2]; // 13: server MTU (big-endian)
uint8_t link_id; // 15: server's local link id
uint8_t remote_socket_id; // 16: echo of client's socket_id
uint8_t only_local; // 17: server only_local flag
uint8_t type; // 18: server socket type (CFG_SERVER_TYPE_*)
// V2 fields:
uint8_t peer_ipv4[4]; // 19: client's external NAT address (big-endian)
uint8_t peer_port[2]; // 23: client's external NAT port (big-endian)
} __attribute__((packed));
#define ETCP_INIT_RESP_V1_SIZE 19 // size without peer_ipv4/peer_port
#define ETCP_INIT_RESP_V2_SIZE 25 // size with peer_ipv4/peer_port
#define ETCP_INIT_RESP_MIN_SIZE ETCP_INIT_RESP_V1_SIZE
typedef void (*etcp_ping_callback_t)(int success, uint16_t rtt, void* arg, uint64_t nonce, typedef void (*etcp_ping_callback_t)(int success, uint16_t rtt, void* arg, uint64_t nonce,
const uint8_t* resp_data, size_t resp_data_len); const uint8_t* resp_data, size_t resp_data_len);
struct PING_CONTEXT { struct PING_CONTEXT {
@ -106,6 +145,7 @@ struct ETCP_SOCKET {
#define NAT_TYPE_UNKNOWN 0 #define NAT_TYPE_UNKNOWN 0
#define NAT_TYPE_EIM 1 // Endpoint-Independent Mapping #define NAT_TYPE_EIM 1 // Endpoint-Independent Mapping
#define NAT_TYPE_STRICT 2 // Address/Restricted or Symmetric #define NAT_TYPE_STRICT 2 // Address/Restricted or Symmetric
#define NAT_TYPE_DIRECT 3 // real public IP, no NAT (detected during INIT handshake)
// 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

2
src/route_bgp.c

@ -663,6 +663,7 @@ static void route_bgp_extract_nat_addr(struct ETCP_CONN* conn, uint32_t* nat_ip,
void route_bgp_start_link_nat_check(struct ROUTE_BGP* bgp, struct ETCP_LINK* link) {// проверяем тип нат для линка link void route_bgp_start_link_nat_check(struct ROUTE_BGP* bgp, struct ETCP_LINK* link) {// проверяем тип нат для линка link
if (!bgp || !link || !link->conn || !link->etcp) return; if (!bgp || !link || !link->conn || !link->etcp) return;
if (link->nat_type == NAT_TYPE_DIRECT) return;
if (link->nat_check_status == NAT_CHECK_IN_PROGRESS) return; if (link->nat_check_status == NAT_CHECK_IN_PROGRESS) return;
// Skip NAT check for private sockets - they are not reachable from outside // Skip NAT check for private sockets - they are not reachable from outside
if (link->conn->type == CFG_SERVER_TYPE_PRIVATE) { if (link->conn->type == CFG_SERVER_TYPE_PRIVATE) {
@ -1012,6 +1013,7 @@ static void route_bgp_handle_nat_info(struct ROUTE_BGP* bgp, struct ETCP_CONN* f
uint8_t verified_type; uint8_t verified_type;
if (info->nat_type == NAT_TYPE_EIM) verified_type = NAT_VERIFIED_EIM; if (info->nat_type == NAT_TYPE_EIM) verified_type = NAT_VERIFIED_EIM;
else if (info->nat_type == NAT_TYPE_STRICT) verified_type = NAT_VERIFIED_STRICT; else if (info->nat_type == NAT_TYPE_STRICT) verified_type = NAT_VERIFIED_STRICT;
else if (info->nat_type == NAT_TYPE_DIRECT) verified_type = NAT_VERIFIED_DIRECT;
else verified_type = NAT_VERIFIED_UNKNOWN; else verified_type = NAT_VERIFIED_UNKNOWN;
int data_changed = 0; int data_changed = 0;

Loading…
Cancel
Save