@ -30,6 +30,7 @@
/* ═══════ forward-декларации ═══════ */
void conn_mgr_router_recv_handler ( struct ETCP_CONN * conn , struct ll_entry * entry ) ;
static void cm_start_parallel ( struct CONN_MGR_ENTRY * entry ) ;
/* ═══════ утилиты ═══════ */
@ -181,9 +182,11 @@ struct CONN_MGR_ENTRY* cm_ensure_entry(struct CONN_MGR* mgr, uint64_t node_id) {
void cm_entry_cleanup ( struct CONN_MGR_ENTRY * entry ) {
if ( ! entry | | entry - > state = = CONN_MGR_STATE_DISCONNECTED ) return ;
if ( entry - > main . timer ) { uasync_cancel_timeout ( entry - > mgr - > instance - > ua , entry - > main . timer ) ; entry - > main . timer = NULL ; }
if ( entry - > reverse_timer ) { uasync_cancel_timeout ( entry - > mgr - > instance - > ua , entry - > reverse_timer ) ; entry - > reverse_timer = NULL ; }
entry - > reverse_active = 0 ; entry - > indirect_active = 0 ; entry - > indirect_ready = 0 ;
{ struct cm_reverse_pending * * pp = & entry - > mgr - > reverse_pending ;
while ( * pp ) {
if ( ( * pp ) - > entry = = entry ) { struct cm_reverse_pending * rp = * pp ; * pp = rp - > next ; u_free ( rp ) ; }
if ( ( * pp ) - > entry = = entry ) { struct cm_reverse_pending * rp = * pp ; * pp = rp - > next ; if ( rp - > pkt ) u_free ( rp - > pkt ) ; u_free ( rp ) ; }
else pp = & ( * pp ) - > next ;
}
}
@ -192,7 +195,7 @@ void cm_entry_cleanup(struct CONN_MGR_ENTRY* entry) {
if ( ( * pp ) - > entry = = entry ) {
if ( ( * pp ) - > timer & & ( * pp ) - > timer ! = entry - > main . timer )
{ uasync_cancel_timeout ( entry - > mgr - > instance - > ua , ( * pp ) - > timer ) ; ( * pp ) - > timer = NULL ; }
struct cm_exchange_pending * ep = * pp ; * pp = ep - > next ; u_free ( ep ) ;
struct cm_exchange_pending * ep = * pp ; * pp = ep - > next ; if ( ep - > pkt ) u_free ( ep - > pkt ) ; u_free ( ep ) ;
} else pp = & ( * pp ) - > next ;
}
}
@ -251,14 +254,22 @@ void cm_ncd_callback(struct NODE_CONN_DIRECT* ncd_h, enum ncd_event ncd_ev, void
switch ( ncd_ev ) {
case NCD_EVENT_UP :
if ( entry - > state = = CONN_MGR_STATE_CONNECTED ) return ;
DEBUG_INFO ( DEBUG_CATEGORY_GENERAL , " link: ETCP handshake OK with 0x%016llx — connection ESTABLISHED " ,
( unsigned long long ) entry - > node_id ) ;
{ int won = entry - > reverse_active ? CONN_TYPE_REVERSE : CONN_TYPE_DIRECT ;
DEBUG_INFO ( DEBUG_CATEGORY_GENERAL , " link: ETCP handshake OK 0x%016llx — %s (reverse_active=%d indirect_active=%d) " ,
( unsigned long long ) entry - > node_id ,
won = = CONN_TYPE_REVERSE ? " REVERSE WON (indirect cancelled) " : " DIRECT " ,
entry - > reverse_active , entry - > indirect_active ) ; }
if ( entry - > main . timer ) { uasync_cancel_timeout ( entry - > mgr - > instance - > ua , entry - > main . timer ) ; entry - > main . timer = NULL ; }
if ( entry - > reverse_timer ) { uasync_cancel_timeout ( entry - > mgr - > instance - > ua , entry - > reverse_timer ) ; entry - > reverse_timer = NULL ; }
entry - > main_connect_state = CM_TRY_OK ;
entry - > conn_type = ( entry - > main . phase = = 2 ) ? CONN_TYPE_REVERSE : CONN_TYPE_DIRECT ;
entry - > conn_type = entry - > reverse_active ? CONN_TYPE_REVERSE : CONN_TYPE_DIRECT ;
entry - > state = CONN_MGR_STATE_CONNECTED ;
entry - > reverse_active = 0 ; entry - > indirect_active = 0 ; entry - > indirect_ready = 0 ;
{ struct cm_reverse_pending * * pp = & entry - > mgr - > reverse_pending ;
while ( * pp ) { if ( ( * pp ) - > entry = = entry ) { struct cm_reverse_pending * rp = * pp ; * pp = rp - > next ; u_free ( rp ) ; break ; } pp = & ( * pp ) - > next ; }
while ( * pp ) { if ( ( * pp ) - > entry = = entry ) { struct cm_reverse_pending * rp = * pp ; * pp = rp - > next ; if ( rp - > pkt ) u_free ( rp - > pkt ) ; u_free ( rp ) ; break ; } pp = & ( * pp ) - > next ; }
}
{ struct cm_exchange_pending * * pp = & entry - > mgr - > exchange_pending ;
while ( * pp ) { if ( ( * pp ) - > entry = = entry ) { struct cm_exchange_pending * ep = * pp ; * pp = ep - > next ; if ( ep - > pkt ) u_free ( ep - > pkt ) ; u_free ( ep ) ; break ; } pp = & ( * pp ) - > next ; }
}
cm_update_nodeinfo ( entry ) ;
{ struct ETCP_CONN * c = node_conn_direct_get_conn ( ncd_h ) ;
@ -287,19 +298,12 @@ void cm_ncd_callback(struct NODE_CONN_DIRECT* ncd_h, enum ncd_event ncd_ev, void
cm_entry_cleanup ( entry ) ;
return ;
}
/* Инициатор с handle'ами: выбор следующей фазы делаем только из DIRECT (phase==0).
* В REVERSE ( phase = = 2 ) э с к а л а ц и е й в л а д е е т с о б с т в е н н ы й 15 с т а й м е р , а п о в т о р н ы е
/* Инициатор с handle'ами: параллельный запуск REVERSE + INDIRECT. Повторные
* NCD - т а й м а у т ы ( ш а р и м ы й NCD о ж и в л я е т с я д р у г и м и м о д у л я м и ) и г н о р и р у е м — и н а ч е
* cm_start_phase_reverse у т е ч ё т с т а р ы м и т а й м е р а м и и в ы с т р е л и т в о с в о б о ж д ё н н ы й entry . */
if ( entry - > main . phase = = 2 ) return ;
{ struct TOPO_GROUP * g = entry - > mgr - > group ;
struct TOPO_GROUP_NODE * t = topo_node_find_by_id ( g , entry - > node_id ) ;
if ( cm_has_direct_ip ( entry - > mgr , g - > local_node ) & & ! ( t & & cm_has_direct_ip ( entry - > mgr , t ) ) )
{ cm_start_phase_reverse ( entry ) ; return ; } /* keep ncd_handle: target will reuse this conn for incoming INIT */
{ struct NODE_CONN_DIRECT * ncd = entry - > ncd_handle ; entry - > ncd_handle = NULL ;
if ( ncd ) node_conn_direct_force_close ( ncd ) ; }
cm_start_phase_indirect ( entry ) ; return ;
}
* п р о т е к у т т а й м е р ы и в ы с т р е л я т в о с в о б о ж д ё н н ы й entry . */
if ( entry - > reverse_active | | entry - > indirect_active ) return ;
cm_start_parallel ( entry ) ;
break ;
case NCD_EVENT_DOWN :
DEBUG_WARN ( DEBUG_CATEGORY_GENERAL , " link: ETCP link DROPPED to 0x%016llx " ,
( unsigned long long ) entry - > node_id ) ;
@ -624,11 +628,7 @@ static void cm_direct_add_links(struct ETCP_CONN* conn, struct CONN_MGR_ENTRY* e
DEBUG_INFO ( DEBUG_CATEGORY_GENERAL , " conn_mgr: db_node 0x%016llx direct failed (no compatible addr/socket) " , ( unsigned long long ) entry - > node_id ) ;
cm_cleanup_db_node ( entry ) ; cm_deliver_event ( entry , CONN_EVENT_TIMEOUT ) ;
} else {
struct TOPO_GROUP * g = entry - > mgr - > group ;
struct TOPO_GROUP_NODE * t = topo_node_find_by_id ( g , entry - > node_id ) ;
if ( cm_has_direct_ip ( entry - > mgr , g - > local_node ) & & ! ( t & & cm_has_direct_ip ( entry - > mgr , t ) ) )
{ cm_start_phase_reverse ( entry ) ; return ; }
cm_start_phase_indirect ( entry ) ;
cm_start_parallel ( entry ) ;
}
}
@ -652,27 +652,33 @@ void cm_start_phase_direct(struct CONN_MGR_ENTRY* entry) {
cm_direct_add_links ( node_conn_direct_get_conn ( entry - > ncd_handle ) , entry , ni , entry - > db_loaded ) ;
}
/* ═══════ REVERSE фаза ═══════ */
/* ═══════ REVERSE + INDIRECT (параллельный запуск) ═══════ */
void cm_reverse_timeout_cb ( void * arg ) {
struct CONN_MGR_ENTRY * entry = ( struct CONN_MGR_ENTRY * ) arg ; entry - > main . timer = NULL ;
{ struct cm_reverse_pending * * pp = & entry - > mgr - > reverse_pending ;
while ( * pp ) { if ( ( * pp ) - > entry = = entry ) { struct cm_reverse_pending * rp = * pp ; * pp = rp - > next ; u_free ( rp ) ; break ; } pp = & ( * pp ) - > next ; }
}
{ struct NODE_CONN_DIRECT * ncd = entry - > ncd_handle ; entry - > ncd_handle = NULL ;
if ( ncd ) node_conn_direct_force_close ( ncd ) ; }
cm_start_phase_indirect ( entry ) ;
}
/* REVERSE фаза: у нас прямой IP, у цели нет. Отправляем DIRECT_REQ с НАШИМИ
* а д р е с а м и ц е л и ч е р е з BGP - м а р ш р у т ( " подключись ко мне " ) . Ж д ё м о б р а т н о г о INIT .
* Т а й м а у т 15 с — п р и п р о в а л е п е р е х о д и м к INDIRECT . */
/* После провала DIRECT запускаем ОБА пути параллельно: INDIRECT (гарантированный
* fallback ч е р е з р е л е й ) и REVERSE ( о п т и м и з а ц и я — п р я м о й к о н н е к т , е с л и п и р д о з в о н и т с я ) .
* Reverse с т р о г о п р и о р и т е т н е е : е г о INIT п о б е ж д а е т , indirect о т к л а д ы в а е т с я ( indirect_ready ) . */
static void cm_start_parallel ( struct CONN_MGR_ENTRY * entry ) {
struct TOPO_GROUP * g = entry - > mgr - > group ;
struct TOPO_GROUP_NODE * t = topo_node_find_by_id ( g , entry - > node_id ) ;
int can_reverse = cm_has_direct_ip ( entry - > mgr , g - > local_node ) & & ! ( t & & cm_has_direct_ip ( entry - > mgr , t ) ) ;
int can_indirect = entry - > mgr - > best_candidate_count > 0 ;
DEBUG_INFO ( DEBUG_CATEGORY_GENERAL , " conn_mgr: direct failed 0x%016llx — parallel start reverse=%d indirect=%d (candidates=%u) " ,
( unsigned long long ) entry - > node_id , can_reverse , can_indirect , entry - > mgr - > best_candidate_count ) ;
if ( can_indirect ) cm_start_phase_indirect ( entry ) ;
if ( can_reverse ) cm_start_phase_reverse ( entry ) ; /* keep ncd_handle: target will reuse this conn for incoming INIT */
else { struct NODE_CONN_DIRECT * ncd = entry - > ncd_handle ; entry - > ncd_handle = NULL ; if ( ncd ) node_conn_direct_force_close ( ncd ) ; }
if ( ! entry - > reverse_active & & ! entry - > indirect_active ) cm_deliver_event ( entry , CONN_EVENT_TIMEOUT ) ;
}
/* REVERSE фаза: у нас прямой IP, у цели нет. Отправляем DIRECT_REQ с НАШИМИ адресами
* ц е л и ч е р е з BGP - м а р ш р у т ( " подключись ко мне " ) . Ж д ё м о б р а т н о г о INIT ( д е д л а й н 3 с ) ,
* п е р и о д и ч е с к и р е т р а н с м и т и м DIRECT_REQ ( 1 с ) — н а д ё ж н о с т ь н а с л у ч а й п о т е р и п а к е т а . */
void cm_start_phase_reverse ( struct CONN_MGR_ENTRY * entry ) {
struct TOPO_GROUP * group = entry - > mgr - > group ;
struct TOPO_GROUP_NODE * local = group - > local_node ;
struct TOPO_NODE * lni = local ? topo_node_registry_find ( entry - > mgr - > instance - > topo_groups , local - > node_id ) : NULL ;
if ( ! local | | ! lni ) { cm_deliver_event ( entry , CONN_EVENT_TIMEOUT ) ; return ; }
uint32_t req_id = + + entry - > mgr - > next_request_id ; entry - > main . request_id = req_id ; entry - > main . phase = 2 ;
if ( ! local | | ! lni ) return ;
uint32_t req_id = + + entry - > mgr - > next_request_id ; entry - > main . request_id = req_id ;
uint8_t dc = 0 ; struct { uint8_t t ; uint8_t ip [ 4 ] ; uint16_t port ; uint8_t sid ; } addrs [ 8 ] ;
for ( const struct TOPO_ADDR4 * a = lni - > v4_addrs ; a & & dc < 8 ; a = a - > next ) {
if ( a - > type ! = TOPO_ADDR_NAT & & a - > type ! = TOPO_ADDR_INTERFACE ) continue ;
@ -682,26 +688,72 @@ void cm_start_phase_reverse(struct CONN_MGR_ENTRY* entry) {
{ addrs [ dc ] = ( typeof ( addrs [ 0 ] ) ) { a - > type } ; memcpy ( addrs [ dc ] . ip , a - > addr , 4 ) ; addrs [ dc ] . port = htons ( a - > port ) ; addrs [ dc ] . sid = a - > socket_id ; dc + + ; break ; }
}
}
if ( ! dc ) { cm_start_phase_indirect ( entry ) ; return ; }
if ( ! dc ) return ;
size_t sz = CM_DIRECT_REQ_H_SIZE + ( size_t ) dc * 8 ; uint8_t * pkt = u_malloc ( sz ) ;
if ( ! pkt ) { cm_deliver_event ( entry , CONN_EVENT_TIMEOUT ) ; return ; }
if ( ! pkt ) return ;
struct CM_DIRECT_REQ * req = ( struct CM_DIRECT_REQ * ) pkt ; memset ( req , 0 , sizeof ( * req ) ) ;
req - > cmd = ETCP_RT_ID_CONN_MGR ; req - > subcmd = CM_SUBCMD_DIRECT_REQ ; req - > request_id = req_id ; req - > addr_count = dc ;
uint8_t * p = pkt + CM_DIRECT_REQ_H_SIZE ;
for ( uint8_t i = 0 ; i < dc ; i + + ) { * p + + = addrs [ i ] . t ; memcpy ( p , addrs [ i ] . ip , 4 ) ; p + = 4 ; memcpy ( p , & addrs [ i ] . port , 2 ) ; p + = 2 ; * p + + = addrs [ i ] . sid ; }
struct ll_entry * qe = queue_entry_new ( 0 ) ;
if ( ! qe ) { u_free ( pkt ) ; cm_deliver_event ( entry , CONN_EVENT_TIMEOUT ) ; return ; }
qe - > dgram = u_malloc ( 1 + sz ) ; qe - > dgram [ 0 ] = ETCP_RT_ID_CONN_MGR ; memcpy ( qe - > dgram + 1 , pkt , sz ) ; u_free ( pkt ) ; qe - > len = ( uint16_t ) ( 1 + sz ) ;
etcp_route_send ( entry - > mgr - > instance , group - > group_id , entry - > node_id , qe , 1 , 0 ) ;
if ( ! qe ) { u_free ( pkt ) ; return ; }
qe - > dgram = u_malloc ( 1 + sz ) ;
if ( ! qe - > dgram ) { queue_entry_free ( qe ) ; u_free ( pkt ) ; return ; }
qe - > dgram [ 0 ] = ETCP_RT_ID_CONN_MGR ; memcpy ( qe - > dgram + 1 , pkt , sz ) ; qe - > len = ( uint16_t ) ( 1 + sz ) ;
/* копия для ретрансмита (etcp_route_send забирает владение qe) */
struct cm_reverse_pending * rp = u_calloc ( 1 , sizeof ( * rp ) ) ;
if ( rp ) { rp - > request_id = req_id ; rp - > entry = entry ; rp - > next = entry - > mgr - > reverse_pending ; entry - > mgr - > reverse_pending = rp ; }
if ( entry - > main . timer ) { uasync_cancel_timeout ( entry - > mgr - > instance - > ua , entry - > main . timer ) ; entry - > main . timer = NULL ; }
entry - > main . timer = uasync_set_timeout ( entry - > mgr - > instance - > ua , CONN_MGR_CONNECT_REVERSE_TIMEOUT_MS * 10 ,
entry , cm_reverse_timeout_cb , " conn_mgr_reverse " ) ;
if ( rp ) {
rp - > request_id = req_id ; rp - > entry = entry ;
rp - > pkt = u_malloc ( 1 + sz ) ;
if ( rp - > pkt ) { memcpy ( rp - > pkt , qe - > dgram , 1 + sz ) ; rp - > pkt_len = 1 + sz ; }
rp - > next = entry - > mgr - > reverse_pending ; entry - > mgr - > reverse_pending = rp ;
}
etcp_route_send ( entry - > mgr - > instance , group - > group_id , entry - > node_id , qe , 1 , 0 ) ;
u_free ( pkt ) ;
entry - > reverse_active = 1 ;
entry - > reverse_deadline_tb = get_time_tb ( ) + CONN_MGR_CONNECT_REVERSE_TIMEOUT_MS * 10 ;
entry - > reverse_timer = uasync_set_timeout ( entry - > mgr - > instance - > ua , CONN_MGR_REVERSE_RETRANSMIT_MS * 10 ,
entry , cm_reverse_tick_cb , " conn_mgr_reverse " ) ;
DEBUG_INFO ( DEBUG_CATEGORY_GENERAL , " conn_mgr: phase 2 (reverse) sent DIRECT_REQ to 0x%016llx with %u addrs " ,
( unsigned long long ) entry - > node_id , dc ) ;
}
/* Периодический тик REVERSE: ретрансмит DIRECT_REQ, по дедлайну — сдаёмся, закрываем
* NCD ; е с л и indirect у ж е з а в е р ш и л о б м е н ( indirect_ready ) — ф и н а л и з и р у е м INDIRECT . */
void cm_reverse_tick_cb ( void * arg ) {
struct CONN_MGR_ENTRY * entry = ( struct CONN_MGR_ENTRY * ) arg ;
entry - > reverse_timer = NULL ;
if ( ! entry - > reverse_active ) return ;
if ( get_time_tb ( ) > = entry - > reverse_deadline_tb ) {
entry - > reverse_active = 0 ;
{ struct cm_reverse_pending * * pp = & entry - > mgr - > reverse_pending ;
while ( * pp ) { if ( ( * pp ) - > entry = = entry ) { struct cm_reverse_pending * rp = * pp ; * pp = rp - > next ; if ( rp - > pkt ) u_free ( rp - > pkt ) ; u_free ( rp ) ; break ; } pp = & ( * pp ) - > next ; }
}
{ struct NODE_CONN_DIRECT * ncd = entry - > ncd_handle ; entry - > ncd_handle = NULL ;
if ( ncd ) node_conn_direct_force_close ( ncd ) ; }
DEBUG_INFO ( DEBUG_CATEGORY_GENERAL , " conn_mgr: reverse TIMEOUT 0x%016llx — indirect_ready=%d indirect_active=%d " ,
( unsigned long long ) entry - > node_id , entry - > indirect_ready , entry - > indirect_active ) ;
if ( entry - > indirect_ready ) cm_finalize_indirect ( entry ) ;
else if ( ! entry - > indirect_active ) cm_deliver_event ( entry , CONN_EVENT_TIMEOUT ) ;
return ;
}
for ( struct cm_reverse_pending * x = entry - > mgr - > reverse_pending ; x ; x = x - > next ) {
if ( x - > entry = = entry & & x - > pkt ) {
struct ll_entry * qe = queue_entry_new ( 0 ) ;
if ( qe ) {
qe - > dgram = u_malloc ( x - > pkt_len ) ;
if ( qe - > dgram ) { memcpy ( qe - > dgram , x - > pkt , x - > pkt_len ) ; qe - > len = ( uint16_t ) x - > pkt_len ;
etcp_route_send ( entry - > mgr - > instance , entry - > mgr - > group - > group_id , entry - > node_id , qe , 1 , 0 ) ; }
else queue_entry_free ( qe ) ;
}
DEBUG_DEBUG ( DEBUG_CATEGORY_GENERAL , " conn_mgr: reverse retransmit DIRECT_REQ 0x%016llx " , ( unsigned long long ) entry - > node_id ) ;
break ;
}
}
entry - > reverse_timer = uasync_set_timeout ( entry - > mgr - > instance - > ua , CONN_MGR_REVERSE_RETRANSMIT_MS * 10 ,
entry , cm_reverse_tick_cb , " conn_mgr_reverse " ) ;
}
/* ═══════ обработка DIRECT_REQ (REVERSE входящие) ═══════ */
/* Принимающая сторона REVERSE: получили DIRECT_REQ от инициатора — открываем
@ -725,7 +777,7 @@ void cm_handle_direct_req(uint64_t src, struct CONN_MGR* mgr, const uint8_t* dat
if ( entry & & entry - > state = = CONN_MGR_STATE_CONNECTING ) return ;
if ( ! entry ) entry = cm_ensure_entry ( mgr , src ) ;
if ( ! entry ) return ;
entry - > state = CONN_MGR_STATE_CONNECTING ; entry - > main . phase = 2 ;
entry - > state = CONN_MGR_STATE_CONNECTING ; entry - > reverse_active = 1 ;
entry - > main_connect_state = CM_TRY_PENDING ; entry - > db_loaded = 0 ; entry - > conn_type = CONN_TYPE_NONE ;
struct TOPO_NODE nbuf ; memset ( & nbuf , 0 , sizeof ( nbuf ) ) ; nbuf . node_id = src ; memcpy ( nbuf . public_key , src_ni - > public_key , SC_PUBKEY_SIZE ) ;