Browse Source

Fix mesh topology: try normal decryption first, then INIT

Reversed the decryption order:
1. If link exists with session_ready, try normal decryption first
2. If normal decryption succeeds, process packet normally (goto process_decrypted)
3. If normal decryption fails OR no link OR no session, try INIT decryption
4. If INIT succeeds, create new server-side connection and link

This is more efficient because:
- Most packets are regular data (use normal decryption)
- INIT packets are rare (only during connection setup)
- Works correctly for both standard client-server and mesh topologies

All 23 tests now pass.
nodeinfo-routing-update
Evgeny 2 months ago
parent
commit
a73e36b559
  1. 274
      src/etcp_connections.c

274
src/etcp_connections.c

@ -572,160 +572,140 @@ static void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
struct ETCP_LINK* link=etcp_link_find_by_addr(e_sock, &addr);
// printf("[ETCP DEBUG] Received packet, link=%p, recv_len=%zd\n", link, recv_len);
// If no link found OR found link is a client link (not server), treat as potential INIT
// Client links shouldn't receive incoming INIT packets - only server links should
if (link==NULL || link->is_server==0) {// пробуем расшифровать, возможно это init
// printf("[ETCP DEBUG] No existing link found, trying to decrypt as INIT packet\n");
struct secure_channel sc;
if (recv_len<=SC_PUBKEY_SIZE) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: packet too small for init, size=%zd", recv_len);
// If we have an existing link (client link), try normal decryption instead
if (link != NULL) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "INIT packet too small, trying normal decryption with existing link");
goto normal_decrypt;
}
errorcode=1;
goto ec_fr;
}
sc_init_ctx(&sc, &e_sock->instance->my_keys);
// printf("[ETCP DEBUG] Extracting peer public key from position %ld, total packet size=%zd\n", recv_len-SC_PUBKEY_SIZE, recv_len);
// printf("[ETCP DEBUG] Last 64 bytes of packet (PUBKEY): ");
// for (int i=0; i<SC_PUBKEY_SIZE; i++) DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "%02x ", data[recv_len-SC_PUBKEY_SIZE+i]);
// Расшифровываем public_key inplace используя собственный ключ
sc_sha_transcode(e_sock->instance->my_keys.public_key, SC_PUBKEY_SIZE, data + recv_len - SC_PUBKEY_SIZE, SC_PUBKEY_SIZE);
if (sc_set_peer_public_key(&sc, &data[recv_len-SC_PUBKEY_SIZE], SC_PEER_PUBKEY_BIN)!=SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to set peer public key during init");
// If we have an existing link (client link), try normal decryption instead
if (link != NULL) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "INIT peer key setup failed, trying normal decryption with existing link");
goto normal_decrypt;
}
errorcode=2;
goto ec_fr;
}
if (sc_decrypt(&sc, data, recv_len-SC_PUBKEY_SIZE, (uint8_t*)&pkt->timestamp, &pkt_len)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to decrypt init packet");
// If we have an existing link (client link), try normal decryption instead
if (link != NULL) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "INIT decryption failed, trying normal decryption with existing link");
goto normal_decrypt;
}
errorcode=3;
goto ec_fr;
// Try normal decryption first if we have an established link with session keys
// This is the common case for data packets and responses
if (link!=NULL && link->etcp!=NULL && link->etcp->crypto_ctx.session_ready) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Decrypt start (normal)");
if (!sc_decrypt(&link->etcp->crypto_ctx, data, recv_len, (uint8_t*)&pkt->timestamp, &pkt_len)) {
// Normal decryption succeeded - process packet normally
goto process_decrypted;
}
// printf("[ETCP DEBUG] Decrypt OK\n");
pkt->data_len=pkt_len-2;
pkt->noencrypt_len=0;
struct {
uint8_t code;
uint8_t id[8];
uint8_t mtu[2];
uint8_t keepalive[2];
uint8_t link_id;
uint8_t pubkey[SC_PUBKEY_SIZE];
} *ack_hdr=(void*)&pkt->data[0];
uint64_t peer_id;
memcpy(&peer_id, &ack_hdr->id[0], 8);
if (ack_hdr->code!=ETCP_INIT_REQUEST && ack_hdr->code!=ETCP_CHANNEL_INIT) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: not an init packet, code=%02x", ack_hdr->code);
// If we have an existing link (client link), try normal decryption instead
if (link != NULL) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Packet is not INIT (code=%02x), trying normal decryption with existing link", ack_hdr->code);
goto normal_decrypt;
}
errorcode=4;
goto ec_fr;
}// не init
struct ETCP_CONN* conn=e_sock->instance->connections;
while (conn) {// ищем есть ли подключение к этому пиру
if (conn->peer_node_id==peer_id) break;
conn=conn->next;
}
int new_conn=0;
if (!conn || conn->peer_node_id!=peer_id) {// создаём новое
new_conn=1;
conn=etcp_connection_create(e_sock->instance);
if (!conn) { errorcode=55; DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connections_read_callback: failed to create connection"); goto ec_fr; }// облом
memcpy(&conn->crypto_ctx, &sc, sizeof(sc));// добавляем ключ
conn->peer_node_id=peer_id;
etcp_update_log_name(conn); // Update log_name with peer_node_id
char buf[128];
addr_to_string(&addr, buf, sizeof(buf));
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "New connection from %s peer_id=%ld etcp=%p", buf, peer_id, conn);
// Add connection to instance list
conn->next = e_sock->instance->connections;
e_sock->instance->connections = conn;
e_sock->instance->connections_count++;
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Added incoming connection %p to instance, total count: %d", conn, e_sock->instance->connections_count);
}
else {// check keys если существующее подключение
if (memcmp(conn->crypto_ctx.peer_public_key, sc.peer_public_key, SC_PUBKEY_SIZE)) { errorcode=5; DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: peer key mismatch for node %llu", (unsigned long long)peer_id); goto ec_fr; }// коллизия - peer id совпал а ключи разные.
}
struct ETCP_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;
if (ack_hdr->code==0x02) etcp_conn_reset(conn);
struct {
uint8_t code;
uint8_t id[8];
uint8_t mtu[2];
uint8_t link_id;
uint8_t peer_ipv4[4];
uint8_t peer_port[2];
} *ack_repl_hdr=(void*)&pkt->data[0];
ack_repl_hdr->code+=1;
memcpy(ack_repl_hdr->id, &e_sock->instance->node_id, 8);
int mtu=e_sock->instance->config->global.mtu;
ack_repl_hdr->mtu[0]=mtu>>8;
ack_repl_hdr->mtu[1]=mtu;
ack_repl_hdr->link_id = link->local_link_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;
memcpy(ack_repl_hdr->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;
} 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);
// Normal decryption failed - might be INIT packet, fall through to INIT handling
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Normal decryption failed, trying INIT decryption");
}
// Try INIT decryption (for incoming connection requests)
// This handles: no link found, or link without session, or normal decrypt failed
if (recv_len<=SC_PUBKEY_SIZE) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: packet too small for init, size=%zd", recv_len);
errorcode=1;
goto ec_fr;
}
struct secure_channel sc;
sc_init_ctx(&sc, &e_sock->instance->my_keys);
// Расшифровываем public_key inplace используя собственный ключ
sc_sha_transcode(e_sock->instance->my_keys.public_key, SC_PUBKEY_SIZE, data + recv_len - SC_PUBKEY_SIZE, SC_PUBKEY_SIZE);
if (sc_set_peer_public_key(&sc, &data[recv_len-SC_PUBKEY_SIZE], SC_PEER_PUBKEY_BIN)!=SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to set peer public key during init");
errorcode=2;
goto ec_fr;
}
if (sc_decrypt(&sc, data, recv_len-SC_PUBKEY_SIZE, (uint8_t*)&pkt->timestamp, &pkt_len)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to decrypt init packet");
errorcode=3;
goto ec_fr;
}
// INIT decryption succeeded - process as new incoming connection
pkt->data_len=pkt_len-2;
pkt->noencrypt_len=0;
struct {
uint8_t code;
uint8_t id[8];
uint8_t mtu[2];
uint8_t keepalive[2];
uint8_t link_id;
uint8_t pubkey[SC_PUBKEY_SIZE];
} *ack_hdr=(void*)&pkt->data[0];
uint64_t peer_id;
memcpy(&peer_id, &ack_hdr->id[0], 8);
if (ack_hdr->code!=ETCP_INIT_REQUEST && ack_hdr->code!=ETCP_CHANNEL_INIT) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: not an init packet, code=%02x", ack_hdr->code);
errorcode=4;
goto ec_fr;
}// не init
struct ETCP_CONN* conn=e_sock->instance->connections;
while (conn) {// ищем есть ли подключение к этому пиру
if (conn->peer_node_id==peer_id) break;
conn=conn->next;
}
pkt->data_len=sizeof(*ack_repl_hdr);
pkt->noencrypt_len=0;
pkt->link=link;
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "Sending INIT RESPONSE, link=%p, local_link_id=%d, remote_link_id=%d", link, link->local_link_id, link->remote_link_id);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP DEBUG] Send INIT RESPONSE");
etcp_encrypt_send(pkt);
// printf("[ETCP DEBUG] Send INIT RESPONSE ok\n");
memory_pool_free(e_sock->instance->pkt_pool, pkt);
link->initialized = 1;// получен init request (server), считаем линк уже готовым к работе
loadbalancer_link_ready(link);
// Notify BGP about new connection to send routing table
if (link->etcp && link->etcp->instance && link->etcp->instance->bgp) {
route_bgp_new_conn(link->etcp);
int new_conn=0;
if (!conn || conn->peer_node_id!=peer_id) {// создаём новое
new_conn=1;
conn=etcp_connection_create(e_sock->instance);
if (!conn) { errorcode=55; DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connections_read_callback: failed to create connection"); goto ec_fr; }// облом
memcpy(&conn->crypto_ctx, &sc, sizeof(sc));// добавляем ключ
conn->peer_node_id=peer_id;
etcp_update_log_name(conn); // Update log_name with peer_node_id
char buf[128];
addr_to_string(&addr, buf, sizeof(buf));
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "New connection from %s peer_id=%ld etcp=%p", buf, peer_id, conn);
// Add connection to instance list
conn->next = e_sock->instance->connections;
e_sock->instance->connections = conn;
e_sock->instance->connections_count++;
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Added incoming connection %p to instance, total count: %d", conn, e_sock->instance->connections_count);
}
return;
else {// check keys если существующее подключение
if (memcmp(conn->crypto_ctx.peer_public_key, sc.peer_public_key, SC_PUBKEY_SIZE)) { errorcode=5; DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: peer key mismatch for node %llu", (unsigned long long)peer_id); goto ec_fr; }// коллизия - peer id совпал а ключи разные.
}
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;
if (ack_hdr->code==0x02) etcp_conn_reset(conn);
struct {
uint8_t code;
uint8_t id[8];
uint8_t mtu[2];
uint8_t link_id;
uint8_t peer_ipv4[4];
uint8_t peer_port[2];
} *ack_repl_hdr=(void*)&pkt->data[0];
ack_repl_hdr->code+=1;
memcpy(ack_repl_hdr->id, &e_sock->instance->node_id, 8);
int mtu=e_sock->instance->config->global.mtu;
ack_repl_hdr->mtu[0]=mtu>>8;
ack_repl_hdr->mtu[1]=mtu;
ack_repl_hdr->link_id = link->local_link_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;
memcpy(ack_repl_hdr->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;
} 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);
}
pkt->data_len=sizeof(*ack_repl_hdr);
pkt->noencrypt_len=0;
pkt->link=link;
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "Sending INIT RESPONSE, link=%p, local_link_id=%d, remote_link_id=%d", link, link->local_link_id, link->remote_link_id);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP DEBUG] Send INIT RESPONSE");
etcp_encrypt_send(pkt);
// printf("[ETCP DEBUG] Send INIT RESPONSE ok\n");
normal_decrypt:
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Decrypt start");
if (sc_decrypt(&link->etcp->crypto_ctx, data, recv_len, (uint8_t*)&pkt->timestamp, &pkt_len)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to decrypt packet from node %llu len=%d", (unsigned long long)link->etcp->instance->node_id, recv_len);
// log_dump("my_privkey",&link->etcp->crypto_ctx.pk->private_key, SC_PRIVKEY_SIZE);
// log_dump("my_pubkey",&link->etcp->crypto_ctx.pk->public_key, SC_PUBKEY_SIZE);
// log_dump("peer_pubkey",&link->etcp->crypto_ctx.peer_public_key, SC_PUBKEY_SIZE);
errorcode=6;
goto ec_fr;
memory_pool_free(e_sock->instance->pkt_pool, pkt);
link->initialized = 1;// получен init request (server), считаем линк уже готовым к работе
loadbalancer_link_ready(link);
// Notify BGP about new connection to send routing table
if (link->etcp && link->etcp->instance && link->etcp->instance->bgp) {
route_bgp_new_conn(link->etcp);
}
return;
process_decrypted:
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Decrypt end");
if (pkt_len<3) { errorcode=46; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: decrypted packet too small, size=%zu", pkt_len); goto ec_fr; }
pkt->data_len=pkt_len-2;

Loading…
Cancel
Save