@ -2,95 +2,111 @@
* @ file route_bgp . c
* @ brief BGP - like о б м е н м а р ш р у т а м и — и с п р а в л е н н а я в е р с и я п о д н о в у ю route_lib
*/
# include <stdlib.h>
# include <string.h>
# include <stdio.h>
# include "../lib/platform_compat.h"
# include "route_node.h"
# include "route_lib.h"
# include "route_bgp.h"
# include "../lib/debug_config.h"
# include "../lib/mem.h"
# include "utun_instance.h"
# include "etcp_api.h"
# include "etcp.h"
# include "etcp_connections.h"
# include "etcp_debug.h"
# include "utun_instance.h"
# include "config_parser.h"
# include "../lib/debug_config.h"
# include "../lib/mem.h"
# include "route_node.h"
# include "route_lib.h"
# include "route_bgp.h"
// ============================================================================
// Вспомогательные функции
// ============================================================================
// ============================================================================
// Отправка (только preferred_conn + hop_list)
// Вспомогательные функции
// ============================================================================
/* Old route packet sending function removed - using NODEINFO only now */
static void route_bgp_send_table_request ( struct ROUTE_BGP * bgp , struct ETCP_CONN * conn ) {
if ( ! bgp | | ! conn ) return ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Sending table request to %s " , conn - > log_name ) ;
route_bgp_send_nodeinfo ( bgp , conn ) ;
struct BGP_ROUTE_REQUEST * req = u_calloc ( 1 , sizeof ( struct BGP_ROUTE_REQUEST ) ) ;
if ( ! req ) return ;
req - > cmd = ETCP_ID_ROUTE_ENTRY ;
req - > subcmd = ROUTE_SUBCMD_REQUEST_TABLE ;
struct ll_entry * e = queue_entry_new ( 0 ) ;
if ( ! e ) {
u_free ( req ) ;
return ;
}
e - > dgram = ( uint8_t * ) req ;
e - > len = sizeof ( struct BGP_ROUTE_REQUEST ) ;
etcp_send ( conn , e ) ;
}
/* Old full table sending removed - now using NODEINFO */
static void route_bgp_add_to_senders ( struct ROUTE_BGP * bgp , struct ETCP_CONN * conn ) ;
static bool route_bgp_should_send_to ( const struct NODEINFO_Q * nq , uint64_t target_id ) ;
static void route_bgp_send_full_table ( struct ROUTE_BGP * bgp , struct ETCP_CONN * conn ) ;
static void route_bgp_handle_request_table ( struct ROUTE_BGP * bgp , struct ETCP_CONN * conn ) ;
static char * nodeinfo_format ( const uint8_t * data , size_t len ) {
if ( ! data | | len < sizeof ( struct BGP_NODEINFO_PACKET ) ) return NULL ;
struct BGP_NODEINFO_PACKET * pkt = ( struct BGP_NODEINFO_PACKET * ) data ;
struct NODEINFO * ni = & pkt - > node ;
uint64_t node_id = ni - > node_id ;
const uint8_t * dyn = data + sizeof ( struct BGP_NODEINFO_PACKET ) ;
size_t off = 0 ;
char name_buf [ 64 ] = { 0 } ;
size_t nl = ni - > node_name_len ;
if ( nl > 0 & & nl < 63 & & off + nl < len - sizeof ( struct BGP_NODEINFO_PACKET ) ) {
memcpy ( name_buf , dyn + off , nl ) ; off + = nl ;
}
off + = ni - > local_v4_sockets * sizeof ( struct NODEINFO_IPV4_SOCKET ) ;
off + = ni - > local_v6_sockets * sizeof ( struct NODEINFO_IPV6_SOCKET ) ;
char subs_buf [ 512 ] = { 0 } ;
if ( ni - > local_v4_subnets > 0 ) {
const struct NODEINFO_IPV4_SUBNET * subs = ( const struct NODEINFO_IPV4_SUBNET * ) ( dyn + off ) ;
size_t sl = 0 ;
for ( uint8_t i = 0 ; i < ni - > local_v4_subnets & & sl < 400 ; i + + ) {
uint32_t addr = ( subs [ i ] . addr [ 0 ] < < 24 ) | ( subs [ i ] . addr [ 1 ] < < 16 ) | ( subs [ i ] . addr [ 2 ] < < 8 ) | subs [ i ] . addr [ 3 ] ;
char tmp [ 64 ] ; snprintf ( tmp , sizeof ( tmp ) , " %d.%d.%d.%d/%u " , ( addr > > 24 ) & 255 , ( addr > > 16 ) & 255 , ( addr > > 8 ) & 255 , addr & 255 , subs [ i ] . prefix_length ) ;
strcat ( subs_buf , tmp ) ; sl + = strlen ( tmp ) ;
}
}
int need = snprintf ( NULL , 0 , " NODEINFO nid=%016llx ver=%u name= \" %s \" v4subs= \" %s \" v4s=%u v6s=%u v4subcnt=%u hop=%u " , ( unsigned long long ) node_id , ( unsigned ) ni - > ver , name_buf , subs_buf , ( unsigned ) ni - > local_v4_sockets , ( unsigned ) ni - > local_v6_sockets , ( unsigned ) ni - > local_v4_subnets , ( unsigned ) ni - > hop_count ) ;
char * buf = u_malloc ( need + 1 ) ;
if ( ! buf ) return NULL ;
snprintf ( buf , need + 1 , " NODEINFO nid=%016llx ver=%u name= \" %s \" v4subs= \" %s \" v4s=%u v6s=%u v4subcnt=%u hop=%u " , ( unsigned long long ) node_id , ( unsigned ) ni - > ver , name_buf , subs_buf , ( unsigned ) ni - > local_v4_sockets , ( unsigned ) ni - > local_v6_sockets , ( unsigned ) ni - > local_v4_subnets , ( unsigned ) ni - > hop_count ) ;
return buf ;
}
/* Old full table sending removed - now using NODEINFO broadcast now */
// ============================================================================
// Broadcast / Withdraw
// Broadcast / Withdraw: node_id - удаляемый узел, wd_source - от кого получена команда удалить
// ============================================================================
/* Old broadcast route function removed - using NODEINFO broadcast now */
static void route_bgp_broadcast_withdraw ( struct ROUTE_BGP * bgp , uint64_t node_id ,
struct ETCP_CONN * exclude ) {
if ( ! bgp ) {
DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " route_bgp_broadcast_withdraw: bgp is NULL " ) ;
return ;
}
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " route_bgp_broadcast_withdraw: node_id=%016llx exclude=%p " ,
( unsigned long long ) node_id , ( void * ) exclude ) ;
static void route_bgp_broadcast_withdraw ( struct ROUTE_BGP * bgp , uint64_t node_id , uint64_t wd_source , struct ETCP_CONN * exclude ) {
if ( ! bgp ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " bgp is NULL " ) ; return ; }
struct BGP_WITHDRAW_PACKET * pkt = u_calloc ( 1 , sizeof ( struct BGP_WITHDRAW_PACKET ) ) ;
if ( ! pkt ) return ;
pkt - > cmd = ETCP_ID_ROUTE_ENTRY ;
pkt - > subcmd = ROUTE_SUBCMD_WITHDRAW ;
pkt - > node_id = htobe64 ( node_id ) ;
struct ll_entry * send_entry = queue_entry_new ( 0 ) ;
if ( ! send_entry ) { u_free ( pkt ) ; return ; }
send_entry - > dgram = ( uint8_t * ) pkt ;
send_entry - > len = sizeof ( struct BGP_WITHDRAW_PACKET ) ;
struct ll_entry * e = bgp - > senders_list - > head ;
pkt - > node_id = node_id ;
pkt - > wd_source = wd_source ;
struct ll_entry * e = bgp - > senders_list ? bgp - > senders_list - > head : NULL ;
while ( e ) {
struct ROUTE_BGP_CONN_ITEM * item = ( struct ROUTE_BGP_CONN_ITEM * ) e - > data ;
if ( item - > conn ! = exclude ) {
if ( item & & item - > conn & & item - > conn ! = exclude ) {
struct ll_entry * copy = queue_entry_new ( 0 ) ;
if ( copy ) {
copy - > dgram = u_malloc ( send_entry - > len ) ;
memcpy ( copy - > dgram , send_entr y- > dgram , send_entry - > len ) ;
copy - > len = send_entry - > len ;
copy - > dgram = u_malloc ( sizeof ( * pkt ) ) ;
if ( copy - > dgram ) memcpy ( cop y- > dgram , pkt , sizeof ( * pkt ) ) ;
copy - > len = sizeof ( * pkt ) ;
etcp_send ( item - > conn , copy ) ;
}
}
e = e - > next ;
}
queue_entry_free ( send_entry ) ;
u_free ( pkt ) ;
}
// ============================================================================
// Callback на изменение таблицы (insert / update / delete)
// ============================================================================
/* Old route change callback removed - now using NODEINFO updates */
// ============================================================================
// Приём пакетов
@ -128,12 +144,12 @@ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
from_conn - > log_name , subcmd , entry - > len ) ;
if ( subcmd = = ROUTE_SUBCMD_NODEINFO ) {
char * s = nodeinfo_format ( data , entry - > len ) ; if ( s ) { DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s from %s " , s , from_conn - > log_name ) ; u_free ( s ) ; }
route_bgp_process_nodeinfo ( bgp , from_conn , data , entry - > len ) ;
} else if ( subcmd = = ROUTE_SUBCMD_WITHDRAW ) {
route_bgp_process_withdraw ( bgp , data , entry - > len ) ;
route_bgp_process_withdraw ( bgp , from_conn , data , entry - > len ) ;
} else if ( subcmd = = ROUTE_SUBCMD_REQUEST_TABLE ) {
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Received table request from %s " , from_conn - > log_name ) ;
route_bgp_send_nodeinfo ( bgp , from_conn ) ;
route_bgp_handle_request_table ( bgp , from_conn ) ;
}
queue_dgram_free ( entry ) ;
@ -181,7 +197,8 @@ struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) {
return NULL ;
}
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " route_bgp_init: node_id=%016llx " , ( unsigned long long ) instance - > node_id ) ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " route_bgp_init: node_id=%016llx " ,
( unsigned long long ) instance - > node_id ) ;
struct ROUTE_BGP * bgp = u_calloc ( 1 , sizeof ( struct ROUTE_BGP ) ) ;
if ( ! bgp ) {
@ -207,11 +224,28 @@ struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) {
return NULL ;
}
// Build initial my_nodeinfo
if ( route_bgp_build_my_nodeinfo ( instance , bgp ) ! = 0 ) {
DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " route_bgp_init: build_my_nodeinfo failed " ) ;
int vc = 0 ;
struct CFG_ROUTE_ENTRY * s = instance - > config - > my_subnets ;
while ( s ) {
if ( s - > ip . family = = AF_INET ) vc + + ;
s = s - > next ;
}
size_t d = vc * sizeof ( struct NODEINFO_IPV4_SUBNET ) ;
bgp - > local_node = u_calloc ( 1 , sizeof ( struct NODEINFO_Q ) + d ) ;
if ( ! bgp - > local_node ) {
queue_free ( bgp - > nodes ) ;
queue_free ( bgp - > senders_list ) ;
u_free ( bgp ) ;
DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " route_bgp_init: local_node alloc failed " ) ;
return NULL ;
}
bgp - > local_node - > node . node_id = instance - > node_id ;
bgp - > local_node - > node . hop_count = 0 ;
bgp - > local_node - > node . ver = 1 ;
bgp - > local_node - > dirty = 0 ;
route_bgp_update_my_nodeinfo ( instance , bgp ) ;
etcp_bind ( instance , ETCP_ID_ROUTE_ENTRY , route_bgp_receive_cbk ) ;
// Устанавливаем callback для новых ETCP соединений
@ -231,25 +265,26 @@ void route_bgp_destroy(struct UTUN_INSTANCE* instance) {
return ;
}
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " route_bgp_destroy: node_id=%016llx " , ( unsigned long long ) instance - > node_id ) ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " route_bgp_destroy: node_id=%016llx " ,
( unsigned long long ) instance - > node_id ) ;
etcp_unbind ( instance , ETCP_ID_ROUTE_ENTRY ) ;
// очистка списка senders
struct ll_entry * e ;
while ( ( e = queue_data_get ( instance - > bgp - > senders_list ) ) ! = NULL ) {
queue_entry_free ( e ) ;
}
queue_free ( instance - > bgp - > senders_list ) ;
// очистка nodes
if ( instance - > bgp - > nodes ) {
queue_free ( instance - > bgp - > nodes ) ;
if ( instance - > bgp - > local_node ) {
if ( instance - > bgp - > local_node - > paths ) {
queue_free ( instance - > bgp - > local_node - > paths ) ;
}
u_free ( instance - > bgp - > local_node ) ;
}
// Free my_nodeinfo
if ( instance - > bgp - > my_nodeinfo ) {
u_free ( instance - > bgp - > my_nodeinfo ) ;
if ( instance - > bgp - > nodes ) {
queue_free ( instance - > bgp - > nodes ) ;
}
u_free ( instance - > bgp ) ;
@ -275,32 +310,9 @@ void route_bgp_new_conn(struct ETCP_CONN* conn) {
}
struct ROUTE_BGP * bgp = conn - > instance - > bgp ;
struct ROUTE_TABLE * rt = conn - > instance - > rt ;
// === 1. Проверяем, уже есть ли это соединение в списке ===
bool already_exists = false ;
struct ll_entry * e = bgp - > senders_list - > head ;
while ( e ) {
struct ROUTE_BGP_CONN_ITEM * item = ( struct ROUTE_BGP_CONN_ITEM * ) e - > data ;
if ( item - > conn = = conn ) {
already_exists = true ;
break ;
}
e = e - > next ;
}
// === 2. Если нет — добавляем (только один раз) ===
if ( ! already_exists ) {
struct ll_entry * item_entry = queue_entry_new ( sizeof ( struct ROUTE_BGP_CONN_ITEM ) ) ;
if ( ! item_entry ) return ;
route_bgp_add_to_senders ( bgp , conn ) ;
( ( struct ROUTE_BGP_CONN_ITEM * ) item_entry - > data ) - > conn = conn ;
queue_data_put ( bgp - > senders_list , item_entry ) ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " New connection added to senders_list " ) ;
}
// === 3. Отправляем запрос на получение таблицы (обе стороны при on_up) ===
route_bgp_send_table_request ( bgp , conn ) ;
}
@ -314,8 +326,9 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
return ;
}
// SAFETY: проверяем что conn ещё есть в senders_list
struct ROUTE_BGP * bgp = conn - > instance - > bgp ;
// SAFETY: проверяем что conn ещё есть в senders_list
bool found_in_list = false ;
struct ll_entry * e = bgp - > senders_list - > head ;
while ( e ) {
@ -332,17 +345,37 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
return ;
}
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " route_bgp_remove_conn: peer=%016llx " , ( unsigned long long ) conn - > peer_node_id ) ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " route_bgp_remove_conn: peer=%016llx " ,
( unsigned long long ) conn - > peer_node_id ) ;
struct ROUTE_TABLE * rt = conn - > instance - > rt ;
// Remove this connection from all nodes' path lists
// and send WITHDRAW if a node becomes unreachable
bool need_withdraw = false ;
struct ll_entry * node_entry = bgp - > nodes ? bgp - > nodes - > head : NULL ;
while ( node_entry ) {
struct NODEINFO_Q * nq = ( struct NODEINFO_Q * ) node_entry - > data ;
route_bgp_remove_path ( nq , conn ) ;
node_entry = node_entry - > next ;
struct ll_entry * next = node_entry - > next ;
struct NODEINFO_Q * nq = ( struct NODEINFO_Q * ) node_entry ;
if ( route_bgp_remove_path ( nq , conn ) = = 1 ) {
need_withdraw = true ;
if ( rt ) {
route_delete ( rt , nq ) ;
}
nq - > dirty = 1 ;
if ( nq - > paths ) {
queue_free ( nq - > paths ) ;
nq - > paths = NULL ;
}
uint64_t key = nq - > node . node_id ;
struct ll_entry * entry = node_entry ;
if ( entry ) {
queue_remove_data ( bgp - > nodes , entry ) ;
queue_entry_free ( entry ) ;
}
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Removed node %016llx after link down " , ( unsigned long long ) key ) ;
}
node_entry = next ;
}
// Удаляем из списка рассылки
@ -357,81 +390,348 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
e = e - > next ;
}
if ( need_withdraw ) {
route_bgp_broadcast_withdraw ( bgp , conn - > peer_node_id , conn - > instance - > node_id , NULL ) ;
}
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Connection removed, paths updated " ) ;
}
/* ================================================
* NEW NODEINFO BASED IMPLEMENTATION
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
struct NODEINFO_Q * route_bgp_get_node ( struct ROUTE_BGP * bgp , uint64_t node_id ) {
if ( ! bgp | | ! bgp - > nodes ) return NULL ;
uint64_t key = htobe64 ( node_id ) ;
if ( ! bgp | | ! bgp - > nodes ) {
return NULL ;
}
uint64_t key = node_id ;
struct ll_entry * e = queue_find_data_by_index ( bgp - > nodes , & key , 8 ) ;
return e ? ( struct NODEINFO_Q * ) e - > data : NULL ;
return e ? ( struct NODEINFO_Q * ) e : NULL ;
}
int route_bgp_add_path ( struct NODEINFO_Q * nq , struct ETCP_CONN * conn ) {
if ( ! nq | | ! conn ) return - 1 ;
DEBUG_TRACE ( DEBUG_CATEGORY_BGP , " Added path for node %016llx via conn %s " ,
( unsigned long long ) nq - > node . node_id , conn - > log_name ) ;
int route_bgp_add_path ( struct NODEINFO_Q * nq , struct ETCP_CONN * conn , uint64_t * hop_list , uint8_t hop_count ) {
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 , " node_paths " ) ;
if ( ! nq - > paths ) return - 1 ;
}
size_t hop_size = hop_count * 8 ;
size_t path_size = sizeof ( struct NODEINFO_PATH ) - sizeof ( struct ll_entry ) + hop_size ;
struct ll_entry * pe = queue_entry_new ( path_size ) ;
if ( ! pe ) return - 1 ;
struct NODEINFO_PATH * path = ( struct NODEINFO_PATH * ) pe ;
path - > conn = conn ;
path - > hop_count = hop_count ;
uint64_t * stored = ( uint64_t * ) ( ( uint8_t * ) path + sizeof ( struct NODEINFO_PATH ) ) ;
memcpy ( stored , hop_list , hop_size ) ;
queue_data_put ( nq - > paths , pe ) ;
DEBUG_TRACE ( DEBUG_CATEGORY_BGP , " Added path for node %016llx via conn %s with %d hops " , ( unsigned long long ) nq - > node . node_id , conn - > log_name , hop_count ) ;
return 0 ;
}
int route_bgp_remove_path ( struct NODEINFO_Q * nq , struct ETCP_CONN * conn ) {
if ( ! nq | | ! conn ) return - 1 ;
DEBUG_TRACE ( DEBUG_CATEGORY_BGP , " Removed path for node %016llx via conn %s " ,
( unsigned long long ) nq - > node . node_id , conn - > log_name ) ;
int route_bgp_remove_path_by_hop ( struct NODEINFO_Q * nq , uint64_t wd_source ) {
if ( ! nq | | ! nq - > paths ) return 0 ;
int removed = 0 ;
struct ll_entry * e = nq - > paths - > head ;
while ( e ) {
struct NODEINFO_PATH * path = ( struct NODEINFO_PATH * ) e ;
// parse hoplist in dyn after hop_count (after fixed PATH)
uint64_t * hop = ( uint64_t * ) ( ( uint8_t * ) path + sizeof ( struct NODEINFO_PATH ) ) ;
bool has_wd = false ;
for ( uint8_t i = 0 ; i < path - > hop_count ; i + + ) {
if ( hop [ i ] = = wd_source ) {
has_wd = true ;
break ;
}
}
if ( has_wd ) {
struct ll_entry * next = e - > next ;
queue_remove_data ( nq - > paths , e ) ;
queue_entry_free ( e ) ;
removed + + ;
e = next ;
continue ;
}
e = e - > next ;
}
if ( removed > 0 & & nq - > paths & & queue_entry_count ( nq - > paths ) = = 0 ) {
queue_free ( nq - > paths ) ;
nq - > paths = NULL ;
return 1 ; // unreachable
}
return removed ;
}
int route_bgp_remove_path ( struct NODEINFO_Q * nq , struct ETCP_CONN * conn )
{
if ( ! nq | | ! conn | | ! nq - > paths ) {
return - 1 ;
}
// Remove conn from paths queue
struct ll_entry * e = nq - > paths - > head ;
while ( e ) {
struct NODEINFO_PATH * path = ( struct NODEINFO_PATH * ) e ;
if ( path - > conn = = conn ) {
queue_remove_data ( nq - > paths , e ) ;
queue_entry_free ( e ) ;
break ;
}
e = e - > next ;
}
int remaining = nq - > paths ? queue_entry_count ( nq - > paths ) : 0 ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Removed path for node %016llx via %s, remaining paths: %d " ,
( unsigned long long ) nq - > node . node_id , conn - > log_name , remaining ) ;
// If no paths left - node is unreachable
if ( remaining = = 0 ) {
if ( nq - > paths ) {
queue_free ( nq - > paths ) ;
nq - > paths = NULL ;
}
return 1 ; // signal that node became unreachable
}
return 0 ;
}
int route_bgp_process_nodeinfo ( struct ROUTE_BGP * bgp , struct ETCP_CONN * from ,
const uint8_t * data , size_t len ) {
int nodeinfo_dyn_size ( struct NODEINFO * node ) {
return node - > node_name_len +
node - > local_v4_sockets * sizeof ( struct NODEINFO_IPV4_SOCKET ) +
node - > local_v6_sockets * sizeof ( struct NODEINFO_IPV6_SOCKET ) +
node - > local_v4_subnets * sizeof ( struct NODEINFO_IPV4_SUBNET ) +
node - > local_v6_subnets * sizeof ( struct NODEINFO_IPV6_SUBNET ) +
node - > tranzit_nodes * 8 +
node - > hop_count * 8 ;
}
int route_bgp_process_nodeinfo ( struct ROUTE_BGP * bgp , struct ETCP_CONN * from , const uint8_t * data , size_t len ) {
if ( ! bgp | | ! from | | len < sizeof ( struct BGP_NODEINFO_PACKET ) ) return - 1 ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Processing NODEINFO from %s " , from - > log_name ) ;
struct BGP_NODEINFO_PACKET * pkt = ( struct BGP_NODEINFO_PACKET * ) data ;
struct NODEINFO * ni = & pkt - > node ;
int dyn_size = nodeinfo_dyn_size ( ni ) ;
if ( len ! = dyn_size + sizeof ( struct BGP_NODEINFO_PACKET ) ) {
DEBUG_WARN ( DEBUG_CATEGORY_BGP , " Incorrect packet size (%s) " , from - > log_name ) ;
return - 1 ;
}
uint64_t node_id = ni - > node_id ;
if ( ni - > hop_count > = MAX_HOPS ) {
DEBUG_WARN ( DEBUG_CATEGORY_BGP , " NODEINFO from %s dropped: too many hops (%d) " ,
from - > log_name , ni - > hop_count ) ;
return - 1 ;
}
struct NODEINFO_Q * nodeinfo1 = route_bgp_get_node ( bgp , node_id ) ;
uint8_t new_ver = ni - > ver ;
if ( nodeinfo1 & & ( int8_t ) ( nodeinfo1 - > last_ver - new_ver ) > = 0 ) {
DEBUG_TRACE ( DEBUG_CATEGORY_BGP , " NODEINFO from %s ignored (old ver %d <= %d) " ,
from - > log_name , new_ver , nodeinfo1 - > last_ver ) ;
return 0 ;
}
int new_data_size = sizeof ( struct NODEINFO_Q ) - sizeof ( struct ll_entry ) + dyn_size + 8 ;
struct ll_queue * paths = NULL ;
int need_alloc = 0 ;
if ( nodeinfo1 ) { // remove old node
paths = nodeinfo1 - > paths ;
if ( nodeinfo1 - > ll . size < new_data_size ) {
need_alloc = 1 ;
}
} else need_alloc = 1 ;
if ( need_alloc ) {
if ( nodeinfo1 ) {
queue_remove_data ( bgp - > nodes , & nodeinfo1 - > ll ) ;
queue_entry_free ( & nodeinfo1 - > ll ) ;
}
nodeinfo1 = ( struct NODEINFO_Q * ) queue_entry_new ( new_data_size ) ;
paths = queue_new ( bgp - > instance - > ua , 0 , " node_paths " ) ;
memcpy ( & nodeinfo1 - > node , ni , sizeof ( struct NODEINFO ) + dyn_size ) ;
queue_data_put_with_index ( bgp - > nodes , & nodeinfo1 - > ll , offsetof ( struct NODEINFO_Q , node . node_id ) - sizeof ( struct ll_entry ) , 8 ) ;
}
else memcpy ( & nodeinfo1 - > node , ni , sizeof ( struct NODEINFO ) + dyn_size ) ;
nodeinfo1 - > paths = paths ;
nodeinfo1 - > last_ver = new_ver ;
// add last hop
uint64_t * hop_list = ( uint64_t * ) ( ( uint8_t * ) & nodeinfo1 - > node + sizeof ( struct NODEINFO ) + dyn_size ) ;
uint64_t sender = from - > peer_node_id ;
memcpy ( hop_list , & sender , 8 ) ;
nodeinfo1 - > node . hop_count + + ;
// update node path
route_bgp_remove_path_by_hop ( nodeinfo1 , from - > peer_node_id ) ;
route_bgp_add_path ( nodeinfo1 , from , hop_list , nodeinfo1 - > node . hop_count ) ;
if ( bgp - > instance - > rt ) {
route_insert ( bgp - > instance - > rt , nodeinfo1 ) ;
}
int hop_count = nodeinfo1 - > node . hop_count ;
// рассылаем по узлам которых нет в hop_list этой ноды (loop prevention)
struct ll_entry * e = bgp - > senders_list ? bgp - > senders_list - > head : NULL ;
while ( e ) {
struct ROUTE_BGP_CONN_ITEM * item = ( struct ROUTE_BGP_CONN_ITEM * ) e - > data ;
if ( item - > conn ) {
uint64_t id = item - > conn - > peer_node_id ;
int found = 0 ;
for ( int i = 0 ; i < hop_count ; i + + ) if ( hop_list [ i ] = = id ) found = 1 ;
if ( found = = 0 ) {
route_bgp_send_nodeinfo ( nodeinfo1 , item - > conn ) ;
}
else DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Skip send NODEINFO to node %016llx " , id ) ;
}
e = e - > next ;
}
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Processed NODEINFO from %s (node=%016llx,ver=%d,paths=%d) " ,
from - > log_name , ( unsigned long long ) node_id , new_ver ,
nodeinfo1 - > paths ? queue_entry_count ( nodeinfo1 - > paths ) : 0 ) ;
return 0 ;
}
int route_bgp_process_withdraw ( struct ROUTE_BGP * bgp , const uint8_t * data , size_t len ) {
int route_bgp_process_withdraw ( struct ROUTE_BGP * bgp , struct ETCP_CONN * sender , const uint8_t * data , size_t len ) {
if ( ! bgp | | len < sizeof ( struct BGP_WITHDRAW_PACKET ) ) return - 1 ;
struct BGP_WITHDRAW_PACKET * wp = ( struct BGP_WITHDRAW_PACKET * ) data ;
uint64_t node_id = be64toh ( wp - > node_id ) ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Processing WITHDRAW for node %016llx " , ( unsigned long long ) node_id ) ;
uint64_t node_id = wp - > node_id ;
uint64_t wd_source = wp - > wd_source ;
struct NODEINFO_Q * nq = route_bgp_get_node ( bgp , node_id ) ;
if ( ! nq ) {
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " node not found " ) ;
return 0 ;
}
int ret = route_bgp_remove_path_by_hop ( nq , wd_source ) ;
if ( ret > 0 | | ! nq - > paths | | ( nq - > paths & & queue_entry_count ( nq - > paths ) = = 0 ) ) {
if ( bgp - > instance & & bgp - > instance - > rt ) {
route_delete ( bgp - > instance - > rt , nq ) ;
}
nq - > dirty = 1 ;
if ( nq - > paths ) {
queue_free ( nq - > paths ) ;
nq - > paths = NULL ;
}
uint64_t key = node_id ;
struct ll_entry * entry = queue_find_data_by_index ( bgp - > nodes , & key , 8 ) ;
if ( entry ) {
queue_remove_data ( bgp - > nodes , entry ) ;
queue_entry_free ( entry ) ;
}
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Removed node %016llx after WITHDRAW " , ( unsigned long long ) node_id ) ;
route_bgp_broadcast_withdraw ( bgp , node_id , wd_source , sender ) ;
}
return 0 ;
}
void route_bgp_send_nodeinfo ( struct ROUTE_BGP * bgp , struct ETCP_CONN * conn ) {
if ( ! bgp | | ! conn | | ! bgp - > my_nodeinfo ) return ;
void route_bgp_send_nodeinfo ( struct NODEINFO_Q * node , struct ETCP_CONN * conn ) {
if ( ! node | | ! conn ) {
return ;
}
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Sending NODEINFO to %s " , conn - > log_name ) ;
struct ll_entry * entry = queue_entry_new ( 0 ) ;
if ( ! entry ) return ;
int dyn = nodeinfo_dyn_size ( & node - > node ) ;
entry - > dgram = u_malloc ( bgp - > my_nodeinfo_size ) ;
if ( ! entry - > dgram ) {
queue_entry_free ( entry ) ;
size_t ps = sizeof ( struct BGP_NODEINFO_PACKET ) + dyn ;
uint8_t * p = u_malloc ( ps ) ;
if ( ! p ) {
return ;
}
p [ 0 ] = ETCP_ID_ROUTE_ENTRY ;
p [ 1 ] = ROUTE_SUBCMD_NODEINFO ;
memcpy ( p + 2 , & node - > node , sizeof ( struct NODEINFO ) ) ;
uint8_t * ds = ( uint8_t * ) & node - > node + sizeof ( struct NODEINFO ) ;
memcpy ( p + sizeof ( struct BGP_NODEINFO_PACKET ) , ds , dyn ) ;
struct ll_entry * e = queue_entry_new ( 0 ) ;
if ( ! e ) {
u_free ( p ) ;
return ;
}
memcpy ( entry - > dgram , bgp - > my_nodeinfo , bgp - > my_nodeinfo_size ) ;
entry - > len = bgp - > my_nodeinfo_size ;
etcp_send ( conn , entry ) ;
e - > dgram = p ;
e - > len = ps ;
etcp_send ( conn , e ) ;
}
static void route_bgp_add_to_senders ( struct ROUTE_BGP * bgp , struct ETCP_CONN * conn ) {
if ( ! bgp | | ! conn | | ! bgp - > senders_list ) return ;
bool already = false ;
struct ll_entry * e = bgp - > senders_list - > head ;
while ( e ) {
if ( ( ( struct ROUTE_BGP_CONN_ITEM * ) e - > data ) - > conn = = conn ) { already = true ; break ; }
e = e - > next ;
}
if ( ! already ) {
struct ll_entry * item_entry = queue_entry_new ( sizeof ( struct ROUTE_BGP_CONN_ITEM ) ) ;
if ( item_entry ) {
( ( struct ROUTE_BGP_CONN_ITEM * ) item_entry - > data ) - > conn = conn ;
queue_data_put ( bgp - > senders_list , item_entry ) ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Added to senders_list " ) ;
}
}
}
static bool route_bgp_should_send_to ( const struct NODEINFO_Q * nq , uint64_t target_id ) {
if ( ! nq | | ! nq - > paths ) return false ;
struct ll_entry * e = nq - > paths - > head ;
while ( e ) {
struct NODEINFO_PATH * path = ( struct NODEINFO_PATH * ) e ;
uint64_t * hop = ( uint64_t * ) ( ( uint8_t * ) path + sizeof ( struct NODEINFO_PATH ) ) ;
bool has_id = false ;
for ( uint8_t i = 0 ; i < path - > hop_count ; i + + ) {
if ( hop [ i ] = = target_id ) { has_id = true ; break ; }
}
if ( ! has_id ) return true ;
e = e - > next ;
}
return false ;
}
void route_bgp_broadcast_nodeinfo ( struct ROUTE_BGP * bgp , struct ETCP_CONN * exclude ) {
if ( ! bgp ) return ;
DEBUG_TRACE ( DEBUG_CATEGORY_BGP , " Broadcasting NODEINFO " ) ;
struct ll_entry * e = bgp - > senders_list ? bgp - > senders_list - > head : NULL ;
static void route_bgp_send_full_table ( struct ROUTE_BGP * bgp , struct ETCP_CONN * conn ) {
if ( ! bgp | | ! conn ) return ;
uint64_t target = conn - > peer_node_id ;
struct ll_entry * e = bgp - > no des ? bgp - > no des - > head : NULL ;
while ( e ) {
struct ROUTE_BGP_CONN_ITEM * item = ( struct ROUTE_BGP_CONN_ITEM * ) e - > data ;
if ( item - > conn & & item - > conn ! = exclude ) {
route_bgp_send_nodeinfo ( bgp , item - > conn ) ;
struct NODEINFO_Q * nq = ( struct NODEINFO_Q * ) e ;
if ( nq - > node . hop_count = = 0 ) {
DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " local node in learned list " ) ;
e = e - > next ; continue ;
}
if ( ! nq - > paths ) {
DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " node has no paths " ) ;
e = e - > next ; continue ;
}
if ( route_bgp_should_send_to ( nq , target ) ) {
route_bgp_send_nodeinfo ( nq , conn ) ;
}
e = e - > next ;
}
}
void route_bgp_send_withdraw ( struct ROUTE_BGP * bgp , uint64_t node_id ) {
if ( ! bgp ) return ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Sending WITHDRAW for node %016llx " , ( unsigned long long ) node_id ) ;
static void route_bgp_handle_request_table ( struct ROUTE_BGP * bgp , struct ETCP_CONN * conn ) {
if ( ! bgp | | ! conn ) return ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " Received table request from %s " , conn - > log_name ) ;
route_bgp_send_nodeinfo ( bgp - > local_node , conn ) ;
route_bgp_send_full_table ( bgp , conn ) ;
route_bgp_add_to_senders ( bgp , conn ) ;
}