|
|
|
|
@ -160,7 +160,6 @@ static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset, uint8_t c
|
|
|
|
|
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); |
|
|
|
|
*(uint64_t*)req->reset_id = htobe64(link->etcp->reset_id); |
|
|
|
|
*(uint16_t*)req->mtu = htobe16(link->mtu_local); |
|
|
|
|
*(uint16_t*)req->keepalive = htobe16(link->etcp->instance->keepalive_interval); |
|
|
|
|
@ -423,6 +422,19 @@ static void etcp_link_update_remote_addr(struct ETCP_LINK* link, struct ETCP_SOC
|
|
|
|
|
link->etcp->log_name, sockaddr_storage_to_str(new_addr).str); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Выбор линка для отправки collision INIT: outbound-линк (is_server==0), чей remote_addr
|
|
|
|
|
* совпадает с адресом пришедшего INIT (симметричный сокет). NULL — симметричного сокета |
|
|
|
|
* нет (асимметрия/NAT) → коллизия не срабатывает, inbound принимается как есть. */ |
|
|
|
|
struct ETCP_LINK* etcp_select_collision_link(struct ETCP_CONN* conn, const struct sockaddr_storage* addr) { |
|
|
|
|
if (!conn || !addr) return NULL; |
|
|
|
|
struct ETCP_LINK* l = conn->links; |
|
|
|
|
while (l) { |
|
|
|
|
if (l->is_server == 0 && sockaddr_equal(&l->remote_addr, addr)) return l; |
|
|
|
|
l = l->next; |
|
|
|
|
} |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct ETCP_LINK* etcp_link_find_by_remote_id(struct ETCP_CONN* conn, uint8_t remote_link_id) { |
|
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
|
|
|
if (!conn || remote_link_id == 0) return NULL; |
|
|
|
|
@ -1059,19 +1071,12 @@ void etcp_link_enter_ready_tcp(struct ETCP_LINK *link) {
|
|
|
|
|
/* Синхронизация got_initial_pkt: если я грязный (1) а пир чистый (0) → сбросить */ |
|
|
|
|
if (link->tcp_link) { |
|
|
|
|
uint8_t peer_gop = stcp_link_get_peer_got_initial_pkt(link->tcp_link); |
|
|
|
|
uint32_t peer_sid = stcp_link_get_peer_session_id(link->tcp_link); |
|
|
|
|
uint64_t peer_rid = stcp_link_get_peer_reset_id(link->tcp_link); |
|
|
|
|
if (etcp->got_initial_pkt == 1 && peer_gop == 0 && peer_rid != etcp->reset_id) { |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] TCP ready: I'm dirty (gop=1) but peer clean (gop=0) → reinit", |
|
|
|
|
etcp->log_name); |
|
|
|
|
etcp_conn_reinit(etcp, "tcp peer clean"); |
|
|
|
|
} |
|
|
|
|
if (etcp->session_id != peer_sid) { |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] TCP ready: session_id mismatch my=%08x peer=%08x → reinit", |
|
|
|
|
etcp->log_name, etcp->session_id, peer_sid); |
|
|
|
|
etcp_conn_reinit(etcp, "tcp session changed"); |
|
|
|
|
} |
|
|
|
|
etcp->session_id = peer_sid; |
|
|
|
|
etcp_conn_apply_peer_reset_id(etcp, etcp->peer_node_id, peer_rid); |
|
|
|
|
|
|
|
|
|
link->peer_device_type = stcp_link_get_peer_device_type(link->tcp_link); |
|
|
|
|
@ -1612,7 +1617,6 @@ static void send_init_response(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pk
|
|
|
|
|
resp->code = ETCP_INIT_RESPONSE_NOINIT; // 0x05 - without reset
|
|
|
|
|
} |
|
|
|
|
*(uint64_t*)resp->node_id = htobe64(e_sock->instance->node_id); |
|
|
|
|
*(uint32_t*)resp->session_id = htobe32(conn->session_id); |
|
|
|
|
*(uint64_t*)resp->reset_id = htobe64(conn->reset_id); |
|
|
|
|
|
|
|
|
|
resp->mtu[0]=link->mtu_local>>8; |
|
|
|
|
@ -1747,16 +1751,8 @@ static int handle_init_response_client(struct ETCP_SOCKET* e_sock, struct ETCP_D
|
|
|
|
|
// ETCP_INIT_RESPONSE_NOINIT (0x05) - no reset
|
|
|
|
|
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); |
|
|
|
|
uint64_t resp_reset_id = be64toh(*(uint64_t*)resp->reset_id); |
|
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "INIT_RESPONSE session_id=%08x rid=%016llx", resp_session_id, (unsigned long long)resp_reset_id); |
|
|
|
|
// Check session_id: ignore response if it doesn't match our session
|
|
|
|
|
if (resp_session_id != link->etcp->session_id) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "[%s] INIT_RESPONSE session_id mismatch: got %08x, expected %08x, ignoring", |
|
|
|
|
link->etcp->log_name, resp_session_id, link->etcp->session_id); |
|
|
|
|
memory_pool_free(e_sock->instance->pkt_pool, pkt); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "INIT_RESPONSE rid=%016llx", (unsigned long long)resp_reset_id); |
|
|
|
|
etcp_conn_apply_peer_reset_id(link->etcp, server_node_id, resp_reset_id); |
|
|
|
|
link->mtu_remote = be16toh(*(uint16_t*)resp->mtu); |
|
|
|
|
if (link->mtu_remote > PACKET_DATA_MAX_MTU) link->mtu_remote = PACKET_DATA_MAX_MTU; |
|
|
|
|
@ -1766,7 +1762,7 @@ static int handle_init_response_client(struct ETCP_SOCKET* e_sock, struct ETCP_D
|
|
|
|
|
link->remote_socket_id = resp->remote_socket_id; |
|
|
|
|
link->remote_only_local = resp->only_local; |
|
|
|
|
link->remote_type = resp->type; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] INIT_RESPONSE received (link=%d, mtu=%d, session=%08x)", link->etcp->log_name, link->remote_link_id, link->mtu, resp_session_id); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] INIT_RESPONSE received (link=%d, mtu=%d, rid=%016llx)", link->etcp->log_name, link->remote_link_id, link->mtu, (unsigned long long)resp_reset_id); |
|
|
|
|
|
|
|
|
|
// Parse NAT IP:port from response (new format includes 4+2 bytes)
|
|
|
|
|
if (pkt_len >= ETCP_INIT_RESP_SIZE) { |
|
|
|
|
@ -1837,12 +1833,6 @@ static int handle_init_response_client(struct ETCP_SOCKET* e_sock, struct ETCP_D
|
|
|
|
|
link->init_timer = NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (pkt_code == ETCP_INIT_RESPONSE && link->etcp->got_initial_pkt) { |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] REINIT from client: INIT_RESPONSE(0x03) received, reinit conn=%p", |
|
|
|
|
link->etcp->log_name, (void*)link->etcp); |
|
|
|
|
etcp_conn_reinit(link->etcp, "server requested"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (link->etcp->initialized == 0) { |
|
|
|
|
etcp_conn_ready(link->etcp); |
|
|
|
|
} |
|
|
|
|
@ -2058,13 +2048,12 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
|
|
|
|
|
|
|
|
|
struct ETCP_INIT_REQUEST_PKT* req = (struct ETCP_INIT_REQUEST_PKT*)pkt->data; |
|
|
|
|
peer_id = be64toh(*(uint64_t*)req->node_id); |
|
|
|
|
uint32_t session_id = be32toh(*(uint32_t*)req->session_id); |
|
|
|
|
uint64_t reset_id = be64toh(*(uint64_t*)req->reset_id); |
|
|
|
|
uint16_t mtu = be16toh(*(uint16_t*)req->mtu); |
|
|
|
|
uint16_t src_port = be16toh(*(uint16_t*)req->src_port); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "INIT received: peer=0x%016llx mtu=%u link=%u sock=%u session=%08x rid=%016llx src=%s", |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "INIT received: peer=0x%016llx mtu=%u link=%u sock=%u rid=%016llx src=%s", |
|
|
|
|
(unsigned long long)peer_id, mtu, req->link_id, req->socket_id, |
|
|
|
|
session_id, (unsigned long long)reset_id, sockaddr_storage_to_str(&addr).str); |
|
|
|
|
(unsigned long long)reset_id, sockaddr_storage_to_str(&addr).str); |
|
|
|
|
|
|
|
|
|
struct ETCP_CONN* conn = NULL; |
|
|
|
|
{ |
|
|
|
|
@ -2119,12 +2108,6 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
|
|
|
|
}// коллизия - peer id совпал а ключи разные.
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Синхронизация эпохи ресета (для нового conn — просто принять эпоху пира)
|
|
|
|
|
if (new_conn) { |
|
|
|
|
conn->reset_id = reset_id; |
|
|
|
|
conn->peer_reset_id = reset_id; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (conn->fin_wait) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] received INIT during fin_wait, clearing", conn->log_name); |
|
|
|
|
conn->fin_wait = 0; |
|
|
|
|
@ -2213,14 +2196,11 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
|
|
|
|
if (req->collision) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] INIT collision=1 from peer=%016llx — remote is master, becoming slave", |
|
|
|
|
conn->log_name, (unsigned long long)peer_id); |
|
|
|
|
conn->session_id = session_id; |
|
|
|
|
etcp_conn_reinit(conn, "collision yield"); |
|
|
|
|
send_reset = 1; |
|
|
|
|
} else { |
|
|
|
|
// Check if WE have an outbound (master) link on this conn
|
|
|
|
|
struct ETCP_LINK* ml = conn->links; |
|
|
|
|
while (ml) { if (ml->is_server == 0) break; ml = ml->next; } |
|
|
|
|
int yielding = 0; |
|
|
|
|
struct ETCP_LINK* ml = etcp_select_collision_link(conn, &addr); |
|
|
|
|
if (ml) { |
|
|
|
|
if (conn->instance->node_id < peer_id) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] COLLISION: we=master node_id=%016llx < peer=%016llx — sending collision INIT", |
|
|
|
|
@ -2231,23 +2211,19 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
|
|
|
|
} |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] COLLISION: peer smaller, yielding master, processing as slave", |
|
|
|
|
conn->log_name); |
|
|
|
|
yielding = 1; |
|
|
|
|
if (ml->init_timer) { uasync_cancel_timeout(conn->instance->ua, ml->init_timer); ml->init_timer = NULL; } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Sync session_id; reset only if remote session changed */ |
|
|
|
|
{ int sess_changed = (conn->session_id != session_id); |
|
|
|
|
conn->session_id = session_id; |
|
|
|
|
if (req->code == ETCP_INIT_REQUEST) { |
|
|
|
|
if (sess_changed || conn->got_initial_pkt) { |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] INIT_REQUEST on existing link: code=0x%02x sess_ch=%d got_init=%d → reinit", |
|
|
|
|
conn->log_name, req->code, sess_changed, conn->got_initial_pkt); |
|
|
|
|
if (conn->got_initial_pkt) { |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] INIT_REQUEST on existing link: code=0x%02x got_init=%d → reinit", |
|
|
|
|
conn->log_name, req->code, conn->got_initial_pkt); |
|
|
|
|
etcp_conn_reinit(conn, "duplicate INIT"); |
|
|
|
|
} |
|
|
|
|
send_reset = 0; |
|
|
|
|
} else { |
|
|
|
|
if (conn->got_initial_pkt == 0) send_reset = 1; |
|
|
|
|
} } |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Cancel existing timers
|
|
|
|
|
@ -2270,13 +2246,11 @@ create_new_link:
|
|
|
|
|
if (req->collision) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] INIT collision=1 from peer=%016llx — remote is master, becoming slave", |
|
|
|
|
conn->log_name, (unsigned long long)peer_id); |
|
|
|
|
conn->session_id = session_id; |
|
|
|
|
etcp_conn_reinit(conn, "collision yield"); |
|
|
|
|
send_reset = 1; |
|
|
|
|
} else { |
|
|
|
|
// Check if WE have an outbound (master) link
|
|
|
|
|
struct ETCP_LINK* ml = conn->links; |
|
|
|
|
while (ml) { if (ml->is_server == 0) break; ml = ml->next; } |
|
|
|
|
struct ETCP_LINK* ml = etcp_select_collision_link(conn, &addr); |
|
|
|
|
if (ml && conn->instance->node_id < peer_id) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] COLLISION: we=master node_id=%016llx < peer=%016llx — sending collision INIT", |
|
|
|
|
conn->log_name, (unsigned long long)conn->instance->node_id, (unsigned long long)peer_id); |
|
|
|
|
@ -2285,17 +2259,7 @@ create_new_link:
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Always sync session_id; reset only if remote session changed
|
|
|
|
|
{ int sess_changed = (conn->session_id != session_id); |
|
|
|
|
conn->session_id = session_id; |
|
|
|
|
if (sess_changed) { |
|
|
|
|
send_reset = 1; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] REINIT new link: code=0x%02x sess=%08x→%08x got_init=%d initialized=%d links_up=%d", |
|
|
|
|
conn->log_name, code, conn->session_id, session_id, conn->got_initial_pkt, conn->initialized, conn->links_up); |
|
|
|
|
etcp_conn_reinit(conn, "session changed"); |
|
|
|
|
} else if (conn->got_initial_pkt == 0) { |
|
|
|
|
send_reset = 1; |
|
|
|
|
} } |
|
|
|
|
if (conn->got_initial_pkt == 0) send_reset = 1; |
|
|
|
|
} |
|
|
|
|
uint16_t client_ka = (req->keepalive[0]<<8) | req->keepalive[1]; |
|
|
|
|
link->peer_device_type = req->device_type; |
|
|
|
|
@ -2315,8 +2279,8 @@ create_new_link:
|
|
|
|
|
|
|
|
|
|
memcpy(conn->peer_ed25519_pubkey, req->ed25519_pubkey, SC_PUBKEY_SIZE); |
|
|
|
|
|
|
|
|
|
// Применяем эпоху ресета пира ПОСЛЕ всей reinit-логики (adopt должен быть финальным словом)
|
|
|
|
|
if (!new_conn) etcp_conn_apply_peer_reset_id(conn, peer_id, reset_id); |
|
|
|
|
// Применяем эпоху ресета пира ПОСЛЕ всей reinit-логики
|
|
|
|
|
etcp_conn_apply_peer_reset_id(conn, peer_id, reset_id); |
|
|
|
|
|
|
|
|
|
send_init_response(e_sock, pkt, link, conn, req, &addr, send_reset, req_src_ip, req_src_port, pkt_len); |
|
|
|
|
return; |
|
|
|
|
|