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
This commit is contained in:
1 parent
89d675f15d
commit
69112ffde1
3 files changed
+144
-120
No files matched your search
+102
-120
@@ -112,41 +112,27 @@ static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset) {
|
||||
|
||||
dgram->link = link;
|
||||
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;
|
||||
dgram->data[offset++] = link->conn ? link->conn->sock_id : 0;
|
||||
dgram->data[offset++] = link->conn ? link->conn->only_local : 0;
|
||||
dgram->data[offset++] = link->conn ? link->conn->type : CFG_SERVER_TYPE_UNKNOWN;
|
||||
struct ETCP_INIT_REQUEST_PKT* req = (struct ETCP_INIT_REQUEST_PKT*)dgram->data;
|
||||
req->code = reset ? ETCP_INIT_REQUEST : ETCP_INIT_REQUEST_NOINIT;
|
||||
*(uint64_t*)req->node_id = htobe64(link->etcp->instance->node_id);
|
||||
*(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
|
||||
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;
|
||||
|
||||
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
|
||||
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->noencrypt_len=0;
|
||||
struct {
|
||||
uint8_t code;
|
||||
uint8_t id[8];
|
||||
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) {
|
||||
uint8_t code = pkt->data[0];
|
||||
uint64_t peer_id = be64toh(*(uint64_t*)(pkt->data + 1));
|
||||
if (code == ETCP_PING) {
|
||||
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);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
if (ack_hdr->code == ETCP_PONG) {
|
||||
if (code == ETCP_PONG) {
|
||||
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);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
if (ack_hdr->code!=ETCP_INIT_REQUEST && ack_hdr->code!=ETCP_INIT_REQUEST_NOINIT) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "not an init packet, code=%02x", ack_hdr->code);
|
||||
if (code!=ETCP_INIT_REQUEST && code!=ETCP_INIT_REQUEST_NOINIT) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "not an init packet, code=%02x", code);
|
||||
errorcode=4;
|
||||
goto ec_fr;
|
||||
}// не 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
|
||||
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);
|
||||
|
||||
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)
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
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
|
||||
link->conn=e_sock;
|
||||
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->remote_link_id = ack_hdr->link_id;
|
||||
link->remote_socket_id = ack_hdr->remote_socket_id;
|
||||
link->remote_only_local = ack_hdr->only_local;
|
||||
link->remote_type = ack_hdr->type;
|
||||
link->remote_link_id = req->link_id;
|
||||
link->remote_socket_id = req->socket_id;
|
||||
link->remote_only_local = req->only_local;
|
||||
link->remote_type = req->type;
|
||||
|
||||
// For CHANNEL_INIT (0x04): if link already initialized - no reset, otherwise 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;
|
||||
}
|
||||
} 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
|
||||
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;
|
||||
link->remote_only_local = ack_hdr->only_local;
|
||||
link->remote_type = ack_hdr->type;
|
||||
link->remote_link_id = req->link_id;
|
||||
link->remote_socket_id = req->socket_id;
|
||||
link->remote_only_local = req->only_local;
|
||||
link->remote_type = req->type;
|
||||
|
||||
// For new links: check session_id to avoid false reinit
|
||||
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;
|
||||
etcp_conn_reinit(conn);
|
||||
}
|
||||
link->keepalive_interval=(ack_hdr->keepalive[0]<<8) | ack_hdr->keepalive[1];
|
||||
link->recovery_interval=((ack_hdr->recovery[0]<<8) | ack_hdr->recovery[1])*100;// timebase в link, timebase/100 в кодограмме
|
||||
link->keepalive_interval=(req->keepalive[0]<<8) | req->keepalive[1];
|
||||
link->recovery_interval=((req->recovery[0]<<8) | req->recovery[1])*100;// timebase в link, timebase/100 в кодограмме
|
||||
if (link->keepalive_interval < 10) link->keepalive_interval = 10;
|
||||
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;
|
||||
link->mtu = link->mtu_local < link->mtu_remote ? link->mtu_local : link->mtu_remote;
|
||||
etcp_update_mtu(link->etcp);
|
||||
|
||||
struct {
|
||||
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];
|
||||
struct ETCP_INIT_RESPONSE_PKT* resp = (struct ETCP_INIT_RESPONSE_PKT*)pkt->data;
|
||||
|
||||
// Set response code: 0x03 (with reset) or 0x05 (without reset)
|
||||
// response with init (0x03) only if reinit was actually done on server side
|
||||
if (send_reset != 0) {
|
||||
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 {
|
||||
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);
|
||||
*(uint32_t*)ack_repl_hdr->session_id = htobe32(conn->session_id);
|
||||
*(uint64_t*)resp->node_id = htobe64(e_sock->instance->node_id);
|
||||
*(uint32_t*)resp->session_id = htobe32(conn->session_id);
|
||||
|
||||
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;
|
||||
ack_repl_hdr->only_local = e_sock->only_local;
|
||||
ack_repl_hdr->type = e_sock->type;
|
||||
resp->mtu[0]=link->mtu_local>>8;
|
||||
resp->mtu[1]=link->mtu_local;
|
||||
resp->link_id = link->local_link_id;
|
||||
resp->remote_socket_id = req->socket_id;
|
||||
resp->only_local = e_sock->only_local;
|
||||
resp->type = e_sock->type;
|
||||
// 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;
|
||||
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);
|
||||
ack_repl_hdr->peer_port[0] = port >> 8;
|
||||
ack_repl_hdr->peer_port[1] = port & 0xFF;
|
||||
resp->peer_port[0] = port >> 8;
|
||||
resp->peer_port[1] = port & 0xFF;
|
||||
link->nat_ip = 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);
|
||||
memset(resp->peer_ipv4, 0, 4);
|
||||
memset(resp->peer_port, 0, 2);
|
||||
link->nat_ip = 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->link=link;
|
||||
link->recv_keepalive = 1;
|
||||
link->last_recv_local_time = get_time_tb();
|
||||
link->last_recv_timestamp = pkt->timestamp;
|
||||
|
||||
int xoffset=sizeof(*ack_repl_hdr);
|
||||
int xoffset=sizeof(struct ETCP_INIT_RESPONSE_PKT);
|
||||
// padding
|
||||
int s = rand() % (link->handshake_maxsize - link->handshake_minsize) + link->handshake_minsize;
|
||||
if (s > (int)(link->mtu)) s = (int)(link->mtu);
|
||||
@@ -1625,9 +1614,9 @@ process_decrypted:
|
||||
link->total_decrypted += pkt->data_len;
|
||||
|
||||
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) {
|
||||
uint16_t peer_period = pkt->data[1] | ((uint16_t)pkt->data[2] << 8);
|
||||
link->keepalive_timeout = (uint32_t)peer_period * KA_TIMEOUT_MULT;
|
||||
@@ -1637,16 +1626,13 @@ process_decrypted:
|
||||
return; // KA handled, nothing more to process
|
||||
}
|
||||
|
||||
if (code == ETCP_INIT_RESPONSE || 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_code == ETCP_INIT_RESPONSE || pkt_code == ETCP_INIT_RESPONSE_NOINIT) {
|
||||
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_NOINIT (0x05) - no reset
|
||||
uint64_t server_node_id = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
server_node_id = (server_node_id << 8) | pkt->data[offset++];
|
||||
}
|
||||
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;
|
||||
struct ETCP_INIT_RESPONSE_PKT* resp = (struct ETCP_INIT_RESPONSE_PKT*)pkt->data;
|
||||
uint64_t server_node_id = be64toh(*(uint64_t*)resp->node_id);
|
||||
uint32_t resp_session_id = be32toh(*(uint32_t*)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
|
||||
if (resp_session_id != link->etcp->session_id) {
|
||||
@@ -1655,23 +1641,20 @@ process_decrypted:
|
||||
memory_pool_free(e_sock->instance->pkt_pool, pkt);
|
||||
return;
|
||||
}
|
||||
link->mtu_remote = ((uint16_t)pkt->data[offset] << 8) | pkt->data[offset + 1];
|
||||
offset += 2;
|
||||
link->mtu_remote = be16toh(*(uint16_t*)resp->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;
|
||||
etcp_update_mtu(link->etcp);
|
||||
link->remote_link_id = pkt->data[offset++];
|
||||
link->remote_socket_id = pkt->data[offset++];
|
||||
link->remote_only_local = pkt->data[offset++];
|
||||
link->remote_type = pkt->data[offset++];
|
||||
link->remote_link_id = resp->link_id;
|
||||
link->remote_socket_id = resp->remote_socket_id;
|
||||
link->remote_only_local = resp->only_local;
|
||||
link->remote_type = resp->type;
|
||||
|
||||
// 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;
|
||||
memcpy(&new_nat_ip, &pkt->data[offset], 4);
|
||||
offset += 4;
|
||||
uint16_t new_nat_port = (pkt->data[offset] << 8) | pkt->data[offset+1];
|
||||
offset += 2;
|
||||
memcpy(&new_nat_ip, resp->peer_ipv4, 4);
|
||||
uint16_t new_nat_port = be16toh(*(uint16_t*)resp->peer_port);
|
||||
|
||||
// Check if NAT address changed
|
||||
if (link->nat_ip == 0 && link->nat_port == 0) {
|
||||
@@ -1710,7 +1693,6 @@ process_decrypted:
|
||||
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);
|
||||
|
||||
@@ -1724,7 +1706,7 @@ process_decrypted:
|
||||
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);
|
||||
etcp_conn_reinit(link->etcp);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,45 @@ struct ETCP_DGRAM {// пакет (незашифрованный)
|
||||
/* Размер базового ACK: type(1) + count(1) + last_delivered_id(4) + rx_dup_count(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,
|
||||
const uint8_t* resp_data, size_t resp_data_len);
|
||||
struct PING_CONTEXT {
|
||||
@@ -106,6 +145,7 @@ struct ETCP_SOCKET {
|
||||
#define NAT_TYPE_UNKNOWN 0
|
||||
#define NAT_TYPE_EIM 1 // Endpoint-Independent Mapping
|
||||
#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)
|
||||
// Values above CFG_SERVER_TYPE_PRIVATE to avoid collision with config types
|
||||
|
||||
@@ -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
|
||||
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;
|
||||
// Skip NAT check for private sockets - they are not reachable from outside
|
||||
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;
|
||||
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_DIRECT) verified_type = NAT_VERIFIED_DIRECT;
|
||||
else verified_type = NAT_VERIFIED_UNKNOWN;
|
||||
|
||||
int data_changed = 0;
|
||||
|
||||
Reference in new issue
Block a user