@ -36,6 +36,7 @@
// Вспомогательные функции
// ============================================================================
/* Шлёт пиру запрос полной таблицы узлов группы (REQUEST_TABLE). */
static void topo_group_send_table_request ( struct TOPO_GROUP * group , struct ETCP_CONN * conn ) {
if ( ! group | | ! conn ) return ;
DEBUG_DEBUG ( DEBUG_CATEGORY_BGP , " Sending table request to %s grp=%016llx " , conn - > log_name , ( unsigned long long ) group - > group_id ) ;
@ -50,6 +51,7 @@ static void topo_group_send_table_request(struct TOPO_GROUP* group, struct ETCP_
if ( etcp_send ( conn , e ) ! = 0 ) { u_free ( req ) ; queue_entry_free ( e ) ; }
}
/* Запрашивает у пира членство в группе (JOIN_GROUP). */
static void topo_group_send_join_group ( struct TOPO_GROUP * group , struct ETCP_CONN * conn ) {
if ( ! group | | ! conn ) return ;
DEBUG_DEBUG ( DEBUG_CATEGORY_BGP , " Sending join group request to %s grp=%016llx " , conn - > log_name , ( unsigned long long ) group - > group_id ) ;
@ -64,6 +66,7 @@ static void topo_group_send_join_group(struct TOPO_GROUP* group, struct ETCP_CON
if ( etcp_send ( conn , e ) ! = 0 ) { u_free ( req ) ; queue_entry_free ( e ) ; }
}
/* Шлёт пиру RESYNC — просьбу заново анонсировать свои группы. */
static void topo_group_send_resync ( struct ETCP_CONN * conn ) {
if ( ! conn ) return ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Sending RESYNC to %s " , conn - > log_name ) ;
@ -77,6 +80,7 @@ static void topo_group_send_resync(struct ETCP_CONN* conn) {
if ( etcp_send ( conn , e ) ! = 0 ) { u_free ( req ) ; queue_entry_free ( e ) ; }
}
/* Сигнализирует пиру об окончании начальной синхронизации таблицы. */
static void topo_group_send_table_complete ( struct TOPO_GROUP * group , struct ETCP_CONN * conn ) {
if ( ! group | | ! conn ) return ;
struct TOPOMSG_TABLE_REQ * req = u_calloc ( 1 , sizeof ( struct TOPOMSG_TABLE_REQ ) ) ;
@ -98,6 +102,7 @@ static void topo_group_handle_join_group(struct TOPO_GROUP* group, struct ETCP_C
static void topo_group_send_resync ( struct ETCP_CONN * conn ) ;
static void topo_group_handle_resync ( struct UTUN_INSTANCE * instance , struct ETCP_CONN * conn ) ;
/* Краткий DEBUG-дамп принятого NODEINFO (id/ver/счётчики/hops). */
static void nodeinfo_dump_log ( const uint8_t * data , size_t len ) {
if ( ! data | | len < sizeof ( struct TOPOMSG_NODEINFO_PKT ) ) return ;
struct TOPOMSG_NODEINFO_PKT * pkt = ( struct TOPOMSG_NODEINFO_PKT * ) data ;
@ -107,6 +112,7 @@ static void nodeinfo_dump_log(const uint8_t* data, size_t len) {
ni - > local_v4_sockets , ni - > local_v4_addrs , ni - > hop_count ) ;
}
/* Форматирует NODEINFO в читаемую строку для лога (имя, подсети, адреса). */
static char * nodeinfo_format ( const uint8_t * data , size_t len ) {
if ( ! data | | len < sizeof ( struct TOPOMSG_NODEINFO_PKT ) ) return NULL ;
struct TOPOMSG_NODEINFO_PKT * pkt = ( struct TOPOMSG_NODEINFO_PKT * ) data ;
@ -139,6 +145,7 @@ static char* nodeinfo_format(const uint8_t* data, size_t len) {
return buf ;
}
/* Читаемое имя sub-команды BGP для логов. */
static const char * group_subcmd_name ( uint8_t subcmd ) {
switch ( subcmd ) {
case TOPO_SUBCMD_NODEINFO : return " NODEINFO " ;
@ -157,6 +164,7 @@ static const char* group_subcmd_name(uint8_t subcmd) {
// Broadcast / Withdraw
// ============================================================================
/* Рассылает WITHDRAW всем BGP-пирам (кроме exclude) об удалении узла. */
static void topo_group_broadcast_withdraw ( struct TOPO_GROUP * group , uint64_t node_id , uint64_t wd_source , struct ETCP_CONN * exclude ) {
if ( ! group ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " group is NULL " ) ; return ; }
struct TOPOMSG_WITHDRAW_PKT * pkt = u_calloc ( 1 , sizeof ( struct TOPOMSG_WITHDRAW_PKT ) ) ;
@ -180,6 +188,7 @@ static void topo_group_broadcast_withdraw(struct TOPO_GROUP* group, uint64_t nod
// Приём пакетов
// ============================================================================
/* Приёмник BGP-пакетов от ETCP: диспетчер по subcmd (NODEINFO/WITHDRAW/...). */
static void topo_group_receive_cbk ( struct ETCP_CONN * from_conn , struct ll_entry * entry ) {
if ( ! from_conn | | ! entry | | entry - > len < 2 ) { if ( entry ) { queue_dgram_free ( entry ) ; queue_entry_free ( entry ) ; } return ; }
struct UTUN_INSTANCE * instance = from_conn - > instance ;
@ -237,6 +246,7 @@ static void topo_group_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
// Init / Destroy / New / Remove conn
// ============================================================================
/* Callback статуса ETCP-соединения: UP — добавить пира в группы; DOWN — удалить. */
static void topo_group_conn_status ( struct ETCP_CONN * conn , int status , void * arg ) {
struct TOPO_GROUPS * groups = ( struct TOPO_GROUPS * ) arg ;
if ( ! conn | | ! groups ) return ;
@ -279,6 +289,7 @@ static void topo_group_conn_status(struct ETCP_CONN* conn, int status, void* arg
}
}
/* Создаёт группу: очереди nodes/senders, local_node, conn_mgr, broadcast. */
static struct TOPO_GROUP * topo_group_create ( struct UTUN_INSTANCE * instance , uint64_t group_id , uint8_t group_type ) {
if ( ! instance ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " instance is NULL " ) ; return NULL ; }
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " group_id=%016llx node_id=%016llx " , ( unsigned long long ) group_id , ( unsigned long long ) instance - > node_id ) ;
@ -311,6 +322,7 @@ static struct TOPO_GROUP* topo_group_create(struct UTUN_INSTANCE* instance, uint
return group ;
}
/* Полностью разрушает группу: recovery, connect, broadcast, conn_mgr, узлы. */
static void topo_group_destroy ( struct TOPO_GROUP * group ) {
if ( ! group ) return ;
DEBUG_INFO ( DEBUG_CATEGORY_SYS , " [GRP_DESTROY] 0 enter grp=%p id=0x%016llx rec_list=%p connect=%p " ,
@ -356,6 +368,7 @@ static void topo_group_destroy(struct TOPO_GROUP* group) {
DEBUG_INFO ( DEBUG_CATEGORY_SYS , " [GRP_DESTROY] 5 done " ) ;
}
/* Удаляет группу из контейнера (кроме UTUN-группы по умолчанию). */
void topo_groups_remove_group ( struct TOPO_GROUPS * g , uint64_t group_id ) {
if ( ! g | | ! g - > group_list | | group_id = = TOPO_GROUP_UTUN ) return ;
struct TOPO_GROUP * group = topo_groups_find ( g , group_id ) ;
@ -374,6 +387,7 @@ void topo_groups_remove_group(struct TOPO_GROUPS* g, uint64_t group_id) {
queue_entry_free ( & group - > ll ) ;
}
/* Инициализация контейнера групп: реестр, пулы, SQLite, default-группа, подписки. */
struct TOPO_GROUPS * topo_groups_init ( struct UTUN_INSTANCE * instance ) {
if ( ! instance ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " instance is NULL " ) ; return NULL ; }
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " node_id=%016llx " , ( unsigned long long ) instance - > node_id ) ;
@ -442,6 +456,7 @@ struct TOPO_GROUPS* topo_groups_init(struct UTUN_INSTANCE* instance) {
return g ;
}
/* Callback смены active/standby: обновляет свой nodeinfo и рассылает его пирам. */
void topo_group_on_activity_change ( struct UTUN_INSTANCE * instance , int active , void * arg ) {
( void ) active ; ( void ) arg ;
if ( ! instance | | ! instance - > topo_groups | | ! instance - > topo_groups - > group_list ) return ;
@ -461,6 +476,7 @@ void topo_group_on_activity_change(struct UTUN_INSTANCE* instance, int active, v
}
}
/* Полный teardown контейнера: отписки, уничтожение групп, реестра, пулов. */
void topo_groups_destroy ( struct UTUN_INSTANCE * instance ) {
if ( ! instance ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " instance is NULL " ) ; return ; }
if ( ! instance - > topo_groups ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " topo_groups is NULL " ) ; return ; }
@ -515,16 +531,19 @@ void topo_groups_destroy(struct UTUN_INSTANCE* instance) {
DEBUG_INFO ( DEBUG_CATEGORY_SYS , " [TOPO_DESTROY] 9 done " ) ;
}
/* Возвращает UTUN-группу по умолчанию (group_id=TOPO_GROUP_UTUN). */
struct TOPO_GROUP * topo_groups_get_default ( struct TOPO_GROUPS * g ) {
return topo_groups_find ( g , TOPO_GROUP_UTUN ) ;
}
/* Ищет группу по group_id (NULL, если нет). */
struct TOPO_GROUP * topo_groups_find ( struct TOPO_GROUPS * g , uint64_t group_id ) {
if ( ! g | | ! g - > group_list ) return NULL ;
struct ll_entry * e = queue_find_data_by_index ( g - > group_list , & group_id ) ;
return e ? ( struct TOPO_GROUP * ) e : NULL ;
}
/* Создаёт и регистрирует новую группу; добавляет существующие conn, для CHAT — автоподключение. */
struct TOPO_GROUP * topo_groups_create_group ( struct TOPO_GROUPS * g , uint64_t group_id , uint8_t group_type , const char * channel_id ) {
if ( ! g | | ! g - > instance ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " invalid args " ) ; return NULL ; }
if ( topo_groups_find ( g , group_id ) ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " group_id=%016llx already exists " , ( unsigned long long ) group_id ) ; return NULL ; }
@ -564,15 +583,18 @@ struct TOPO_GROUP* topo_groups_create_group(struct TOPO_GROUPS* g, uint64_t grou
return group ;
}
/* Разрешить/запретить NAT-проверку локальных подсетей (делегат в nat_detection). */
void topo_group_set_nat_check_local ( struct TOPO_GROUP * group , int allow ) {
if ( ! group | | ! group - > instance | | ! group - > instance - > nat_det ) return ;
nat_detection_set_allow_local ( group - > instance - > nat_det , allow ) ;
}
/* Устанавливает колбэк сохранения узла в БД (используется chatgui/member_sync). */
void topo_groups_set_node_updated_cb ( struct TOPO_GROUPS * groups , topo_node_updated_fn fn ) {
if ( groups ) groups - > node_updated_cb = fn ;
}
/* Добавляет пира в группу при UP: дедуп, добавление в senders, запрос таблицы. */
void topo_group_new_conn ( struct TOPO_GROUP * group , struct ETCP_CONN * conn ) {
if ( ! group | | ! conn ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " invalid args " ) ; return ; }
if ( ! conn - > instance ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " conn->instance is NULL " ) ; return ; }
@ -626,6 +648,7 @@ void topo_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
topo_group_connect_on_up ( group , conn ) ;
}
/* Обрабатывает DOWN пира: чистит пути, каскадно удаляет узлы, шлёт withdraw. */
void topo_group_remove_conn ( struct TOPO_GROUP * group , struct ETCP_CONN * conn ) {
if ( ! group | | ! conn ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " invalid args " ) ; return ; }
if ( ! conn - > instance ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " invalid instance " ) ; return ; }
@ -712,6 +735,7 @@ void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
// NODEINFO BASED IMPLEMENTATION
// ============================================================================
/* Ищет лучшее ETCP-соединение до узла (мин. hop_count среди живых путей). */
struct ETCP_CONN * topo_group_find_conn_for_node ( struct TOPO_GROUP * group , uint64_t node_id ) {
if ( ! group ) return NULL ;
struct TOPO_GROUP_NODE * nq = topo_node_find_by_id ( group , node_id ) ;
@ -728,6 +752,7 @@ struct ETCP_CONN* topo_group_find_conn_for_node(struct TOPO_GROUP* group, uint64
return best_live ? best_live - > conn : ( best_any ? best_any - > conn : NULL ) ;
}
/* Добавляет путь (conn + hop_list + rtt) в paths узла. */
int topo_group_add_path ( struct TOPO_GROUP_NODE * nq , struct ETCP_CONN * conn , uint64_t * hop_list , uint8_t hop_count , uint16_t cumulative_rtt ) {
if ( ! nq | | ! conn | | hop_count > MAX_HOPS | | ! hop_list ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " add_path: invalid args " ) ; return - 1 ; }
if ( ! nq - > paths ) { nq - > paths = queue_new ( conn - > instance - > ua , 0 , 0 , 0 , " node_paths " ) ; if ( ! nq - > paths ) return - 1 ; }
@ -740,6 +765,7 @@ int topo_group_add_path(struct TOPO_GROUP_NODE* nq, struct ETCP_CONN* conn, uint
return 0 ;
}
/* Удаляет пути, содержащие wd_source в hop_list; 1 — если путей не осталось. */
static int topo_group_remove_path_by_hop ( struct TOPO_GROUP_NODE * nq , uint64_t wd_source ) {
if ( ! nq | | ! nq - > paths ) return 0 ;
int removed = 0 ;
@ -755,6 +781,7 @@ static int topo_group_remove_path_by_hop(struct TOPO_GROUP_NODE* nq, uint64_t wd
return removed ;
}
/* Удаляет путь через конкретный conn; 1 — если путей не осталось. */
int topo_group_remove_path ( struct TOPO_GROUP_NODE * nq , struct ETCP_CONN * conn ) {
if ( ! nq | | ! conn | | ! nq - > paths ) return - 1 ;
struct ll_entry * e = nq - > paths - > head ;
@ -766,6 +793,7 @@ int topo_group_remove_path(struct TOPO_GROUP_NODE* nq, struct ETCP_CONN* conn) {
// ===== NODEINFO process =====
/* Шлёт пиру ошибку несоответствия типа группы (ERR_GROUP_MISMATCH). */
static void topo_group_send_err_group_mismatch ( struct TOPO_GROUP * group , struct ETCP_CONN * conn , uint8_t expected_type , uint8_t received_flags ) {
if ( ! group | | ! conn ) return ;
struct TOPOMSG_ERR_GROUP_MISMATCH * pkt = u_calloc ( 1 , sizeof ( struct TOPOMSG_ERR_GROUP_MISMATCH ) ) ;
@ -779,6 +807,7 @@ static void topo_group_send_err_group_mismatch(struct TOPO_GROUP* group, struct
if ( etcp_send ( conn , e ) ! = 0 ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " etcp_send ERR_GROUP_MISMATCH failed " ) ; u_free ( pkt ) ; queue_entry_free ( e ) ; }
}
/* Обрабатывает NODEINFO: проверка подписи/версии, обновление узла, пути, роутинг, форвард. */
int topo_group_process_nodeinfo ( struct TOPO_GROUP * group , struct ETCP_CONN * from , const uint8_t * data , size_t len ) {
if ( ! group | | ! from | | len < sizeof ( struct TOPOMSG_NODEINFO_PKT ) ) return - 1 ;
@ -986,6 +1015,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
return 0 ;
}
/* Обрабатывает WITHDRAW: удаляет узел/пути, чистит роутинг, форвардит withdraw. */
int topo_group_process_withdraw ( struct TOPO_GROUP * group , struct ETCP_CONN * sender , const uint8_t * data , size_t len ) {
if ( ! group | | len < sizeof ( struct TOPOMSG_WITHDRAW_PKT ) ) return - 1 ;
struct TOPOMSG_WITHDRAW_PKT * wp = ( struct TOPOMSG_WITHDRAW_PKT * ) data ;
@ -1017,6 +1047,7 @@ int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* send
return 0 ;
}
/* Сериализует и шлёт NODEINFO узла конкретному пиру. */
void topo_group_send_nodeinfo ( struct TOPO_GROUP * group , struct TOPO_GROUP_NODE * node , struct ETCP_CONN * conn , uint16_t cumulative_rtt ) {
if ( ! group | | ! node | | ! conn ) return ;
@ -1051,6 +1082,7 @@ void topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE*
if ( etcp_send ( conn , e ) ! = 0 ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " send_nodeinfo: etcp_send FAILED for node %016llx to %s " , ( unsigned long long ) node - > node_id , conn - > log_name ) ; u_free ( p ) ; queue_entry_free ( e ) ; }
}
/* Добавляет conn в senders_list (дедуп), если его там ещё нет. */
static void topo_group_add_to_senders ( struct TOPO_GROUP * group , struct ETCP_CONN * conn ) {
if ( ! group | | ! conn | | ! group - > senders_list ) return ;
bool already = false ;
@ -1067,6 +1099,7 @@ static void topo_group_add_to_senders(struct TOPO_GROUP* group, struct ETCP_CONN
}
}
/* Решает, надо ли слать узел пиру (не шлём, если пир уже есть в hop_list пути). */
static bool topo_group_should_send_to ( const struct TOPO_GROUP_NODE * nq , uint64_t target_id ) {
if ( ! nq | | ! nq - > paths ) return false ;
if ( target_id = = nq - > node_id ) return false ;
@ -1081,6 +1114,7 @@ static bool topo_group_should_send_to(const struct TOPO_GROUP_NODE* nq, uint64_t
return false ;
}
/* Шлёт пиру полную таблицу узлов группы (все узлы, кроме достижимых через него). */
static void topo_group_send_full_table ( struct TOPO_GROUP * group , struct ETCP_CONN * conn ) {
if ( ! group | | ! conn ) return ;
uint64_t target = conn - > peer_node_id ;
@ -1094,6 +1128,7 @@ static void topo_group_send_full_table(struct TOPO_GROUP* group, struct ETCP_CON
}
}
/* Обрабатывает REQUEST_TABLE: шлёт свой nodeinfo + полную таблицу + TABLE_COMPLETE. */
static void topo_group_handle_request_table ( struct TOPO_GROUP * group , struct ETCP_CONN * conn ) {
if ( ! group | | ! conn ) return ;
topo_group_send_nodeinfo ( group , group - > local_node , conn , 0 ) ;
@ -1103,6 +1138,7 @@ static void topo_group_handle_request_table(struct TOPO_GROUP* group, struct ETC
topo_group_send_table_complete ( group , conn ) ;
}
/* Обрабатывает JOIN_GROUP: для UTUN — добавить запросившего и инициировать BGP обратно. */
static void topo_group_handle_join_group ( struct TOPO_GROUP * group , struct ETCP_CONN * conn ) {
if ( ! group | | ! conn ) return ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " handle_join_group: from %s grp=%016llx type=%d " , conn - > log_name , ( unsigned long long ) group - > group_id , group - > group_type ) ;
@ -1111,6 +1147,7 @@ static void topo_group_handle_join_group(struct TOPO_GROUP* group, struct ETCP_C
topo_group_new_conn ( group , conn ) ;
}
/* Обрабатывает RESYNC: если мы VPN-клиент пира — заново шлём JOIN_GROUP по группам. */
static void topo_group_handle_resync ( struct UTUN_INSTANCE * instance , struct ETCP_CONN * conn ) {
if ( ! instance | | ! conn | | ! conn - > instance | | ! instance - > topo_groups ) return ;
/* общий сигнал от пассивной стороны: рестартуем обмен, если мы для этого пира VPN-клиент */
@ -1127,6 +1164,7 @@ static void topo_group_handle_resync(struct UTUN_INSTANCE* instance, struct ETCP
/* ── BGP node event callbacks ── */
/* Регистрирует подписчика на события узлов группы (NEW/UPDATE/REMOVE). */
void topo_group_add_node_cbk ( struct TOPO_GROUP * group , topo_node_event_fn fn , void * arg ) {
if ( ! group | | ! fn ) return ;
struct topo_node_cbk_entry * e = u_malloc ( sizeof ( * e ) ) ;
@ -1136,6 +1174,7 @@ void topo_group_add_node_cbk(struct TOPO_GROUP* group, topo_node_event_fn fn, vo
group - > node_cbks = e ;
}
/* Снимает подписчика событий узлов группы. */
void topo_group_remove_node_cbk ( struct TOPO_GROUP * group , topo_node_event_fn fn , void * arg ) {
if ( ! group | | ! fn ) return ;
struct topo_node_cbk_entry * * p = & group - > node_cbks ;
@ -1151,6 +1190,7 @@ void topo_group_remove_node_cbk(struct TOPO_GROUP* group, topo_node_event_fn fn,
/* ── Global nodeinfo callbacks (instance-level, any node change) ── */
/* Регистрирует глобальный колбэк изменения nodeinfo (уровень инстанса). */
void utun_add_nodeinfo_cbk ( struct UTUN_INSTANCE * instance , nodeinfo_cbk_fn fn , void * arg ) {
if ( ! instance | | ! fn ) return ;
struct nodeinfo_cbk_entry * e = u_malloc ( sizeof ( * e ) ) ;
@ -1160,6 +1200,7 @@ void utun_add_nodeinfo_cbk(struct UTUN_INSTANCE* instance, nodeinfo_cbk_fn fn, v
instance - > nodeinfo_cbks = e ;
}
/* Снимает глобальный колбэк nodeinfo. */
void utun_remove_nodeinfo_cbk ( struct UTUN_INSTANCE * instance , nodeinfo_cbk_fn fn , void * arg ) {
if ( ! instance | | ! fn ) return ;
struct nodeinfo_cbk_entry * * p = & instance - > nodeinfo_cbks ;
@ -1173,12 +1214,14 @@ void utun_remove_nodeinfo_cbk(struct UTUN_INSTANCE* instance, nodeinfo_cbk_fn fn
}
}
/* Вызывает все глобальные nodeinfo-колбэки для узла. */
void topo_fire_nodeinfo_cbk ( struct UTUN_INSTANCE * instance , struct TOPO_GROUP * group , struct TOPO_GROUP_NODE * node ) {
if ( ! instance | | ! group | | ! node ) return ;
struct nodeinfo_cbk_entry * c = instance - > nodeinfo_cbks ;
while ( c ) { c - > fn ( group , node , c - > arg ) ; c = c - > next ; }
}
/* Публичная рассылка WITHDRAW об удалении узла. */
void topo_group_send_withdraw ( struct TOPO_GROUP * group , uint64_t node_id ) {
if ( ! group ) return ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " node=%016llx " , ( unsigned long long ) node_id ) ;