@ -38,14 +38,14 @@ static struct test_ctx {
struct UASYNC * ua ;
int round ; /* 0..7 */
int step ;
int keep_tcp ;
int ip_changes_on_last ;
char cur_iface [ IFNAMSIZ ] ;
char prev_iface [ IFNAMSIZ ] ;
struct ETCP_CONN * srv_conn ;
int connected ; /* etcp_connect callback fired */
uint64_t srv_node_id ;
uint8_t srv_pubkey [ SC_PUBKEY_SIZE ] ;
int result ; /* 0=running, 1=fail, 2=pass */
uint32_t recv_at_ip_change ; /* total_recv на момент смены IP */
/* traffic */
uint32_t send_seq , send_count , pong_count , total_recv ;
@ -58,16 +58,15 @@ static char scf[256], ccf[256];
/* ── rounds ── */
static const struct {
const char * ifname , * ip1 , * ip2 ;
int keep_tcp ;
} rounds [ ] = {
{ " dummy_cli1 " , " 10.90.0.2/24 " , " 10.90.1.2/24 " , 1 } ,
{ " dummy_cli2 " , " 10.90.0.3/24 " , " 10.90.1.3/24 " , 1 } ,
{ " dummy_cli3 " , " 10.90.0.4/24 " , " 10.90.1.4/24 " , 0 } ,
{ " dummy_cli4 " , " 10.90.0.5/24 " , " 10.90.1.5/24 " , 0 } ,
{ " dummy_cli5 " , " 10.90.0.6/24 " , " 10.90.1.6/24 " , 1 } ,
{ " dummy_cli6 " , " 10.90.0.7/24 " , " 10.90.1.7/24 " , 1 } ,
{ " dummy_cli7 " , " 10.90.0.8/24 " , " 10.90.1.8/24 " , 0 } ,
{ " dummy_cli8 " , " 10.90.0.9/24 " , " 10.90.1.9/24 " , 0 } ,
{ " dummy_cli1 " , " 10.90.0.2/16 " , " 10.90.1.2/16 " } ,
{ " dummy_cli2 " , " 10.90.0.3/16 " , " 10.90.1.3/16 " } ,
{ " dummy_cli3 " , " 10.90.0.4/16 " , " 10.90.1.4/16 " } ,
{ " dummy_cli4 " , " 10.90.0.5/16 " , " 10.90.1.5/16 " } ,
{ " dummy_cli5 " , " 10.90.0.6/16 " , " 10.90.1.6/16 " } ,
{ " dummy_cli6 " , " 10.90.0.7/16 " , " 10.90.1.7/16 " } ,
{ " dummy_cli7 " , " 10.90.0.8/16 " , " 10.90.1.8/16 " } ,
{ " dummy_cli8 " , " 10.90.0.9/16 " , " 10.90.1.9/16 " } ,
} ;
# define N_ROUNDS (int)(sizeof(rounds) / sizeof(rounds[0]))
@ -88,35 +87,21 @@ static void fail(const char* msg) {
}
static void to_cb ( void * arg ) { ( void ) arg ; fprintf ( stderr , " TIMEOUT \n " ) ; ctx . result = 1 ; }
static int count_links_to_srv ( void ) {
static int link_on_iface ( const struct ETCP_LINK * l , const char * ifname ) {
if ( ! l - > conn | | ! l - > conn - > name | | ! ifname | | ! ifname [ 0 ] ) return 1 ;
/* socket name: as_<ifname>_<v4/v6>_<udp/tcp> */
size_t ifl = strlen ( ifname ) ;
return strncmp ( l - > conn - > name + 3 , ifname , ifl ) = = 0 & & l - > conn - > name [ 3 + ifl ] = = ' _ ' ;
}
static int count_links_to_srv ( const char * ifname ) {
int n = 0 ; struct ll_entry * e = ctx . client - > connections - > head ;
while ( e ) { struct conn_queue_entry * ce = ( struct conn_queue_entry * ) e - > data ;
if ( ce - > conn - > peer_node_id = = ctx . srv_node_id ) {
struct ETCP_LINK * l = ce - > conn - > links ;
while ( l ) { if ( l - > initialized & & l - > link_status ) n + + ; l = l - > next ; }
while ( l ) { if ( l - > initialized & & l - > link_status & & link_on_iface ( l , ifname ) ) n + + ; l = l - > next ; }
} e = e - > next ; }
return n ;
}
static int all_links_are_type ( void ) {
struct ll_entry * e = ctx . client - > connections - > head ;
while ( e ) { struct conn_queue_entry * ce = ( struct conn_queue_entry * ) e - > data ;
if ( ce - > conn - > peer_node_id = = ctx . srv_node_id ) {
struct ETCP_LINK * l = ce - > conn - > links ;
while ( l ) { if ( l - > initialized & & l - > link_status & & l - > is_tcp ! = ctx . keep_tcp ) return 0 ; l = l - > next ; }
} e = e - > next ; }
return 1 ;
}
/* ── filter sockets ── */
static void keep_only_socket_type ( int keep_tcp ) {
struct ETCP_SOCKET * s = ctx . client - > etcp_sockets , * rm [ 16 ] ;
int n = 0 ;
while ( s ) { if ( s - > local_addr . ss_family = = AF_INET & & s - > is_tcp ! = keep_tcp & & n < 16 ) rm [ n + + ] = s ; s = s - > next ; }
for ( int i = 0 ; i < n ; i + + ) {
ncd_remove_socket_links ( ctx . client , rm [ i ] ) ;
etcp_socket_remove ( rm [ i ] ) ;
}
}
/* ── server node (2 addrs: UDP + TCP) ── */
static struct TOPO_GROUP_NODE * mk_srv_node ( void ) {
@ -148,7 +133,7 @@ static struct TOPO_GROUP_NODE* mk_srv_node(void) {
return nq ;
}
/* ── traffic ── */
# define TD(fmt, ...) (void)0
static void srv_traffic_handler ( struct ETCP_CONN * conn , struct ll_entry * entry ) {
if ( ! entry | | entry - > len < 5 ) { if ( entry ) queue_entry_free ( entry ) ; return ; }
struct ll_entry * reply = queue_entry_new ( 0 ) ;
@ -168,12 +153,13 @@ static void cli_traffic_handler(struct ETCP_CONN* conn, struct ll_entry* entry)
static void traffic_send_timer ( void * arg ) {
( void ) arg ;
if ( ctx . result ) return ;
if ( ctx . srv_conn & & ctx . srv_conn - > state ! = 2 ) {
struct ETCP_CONN * conn = instance_find_conn ( ctx . client , ctx . srv_node_id ) ;
if ( conn & & conn - > state ! = 2 ) {
uint8_t buf [ 5 ] ; buf [ 0 ] = ETCP_RT_ID_TEST ;
ctx . send_seq + + ; memcpy ( buf + 1 , & ctx . send_seq , 4 ) ;
struct ll_entry * e = queue_entry_new ( 0 ) ;
if ( e ) { e - > dgram = u_malloc ( 5 ) ; if ( e - > dgram ) { memcpy ( e - > dgram , buf , 5 ) ; e - > len = 5 ;
etcp_send ( ctx . srv_c onn , e ) ; ctx . send_count + + ; } else queue_entry_free ( e ) ; }
etcp_send ( conn , e ) ; ctx . send_count + + ; } else queue_entry_free ( e ) ; }
}
if ( ! ctx . result ) timeout_handle = uasync_set_timeout ( ctx . ua , TRAF_SEND_TB , NULL , traffic_send_timer , " traf_snd " ) ;
}
@ -188,9 +174,8 @@ static void traffic_monitor_timer(void* arg) {
/* ── etcp_connect callback ── */
static void connect_cb ( void * arg , struct ETCP_CONN * conn , int type ) {
( void ) arg ;
if ( type = = ETCP_CONNECT_EARLY | | type = = ETCP_CONNECT_LATE )
{ if ( conn ) { ctx . srv_conn = conn ; ctx . step = 9 ; } }
( void ) arg ; ( void ) conn ;
if ( type = = ETCP_CONNECT_EARLY | | type = = ETCP_CONNECT_LATE ) ctx . connected = 1 ;
}
static void start_traffic ( void ) {
@ -230,6 +215,7 @@ static void phase_del_prev(void* arg);
static void phase_check_add ( void * arg ) ;
static void phase_add ( void * arg ) ;
static void phase_del_last ( void * arg ) ;
static void phase_check_del_last ( void * arg ) ;
static void phase_done ( void * arg ) ;
static void phase_done ( void * arg ) {
@ -244,32 +230,42 @@ static void phase_del_last(void* arg) {
if ( ctx . result ) return ;
ctx . step = 11 ;
ip_link_del ( rounds [ N_ROUNDS - 1 ] . ifname ) ;
uasync_set_timeout ( ctx . ua , STEP_TB , NULL , ( timeout_cb ) phase_del_last , " chk_del_last " ) ;
return ; /* перепланируем один раз для проверки */
uasync_set_timeout ( ctx . ua , STEP_TB , NULL , phase_check_del_last , " chk_del_last " ) ;
}
/* ══ check wrappers ══ */
static void do_check ( void ) {
int l = count_links_to_srv ( ) ;
int l = count_links_to_srv ( ctx . cur_iface ) ;
if ( l < 1 ) { fail ( " no links " ) ; return ; }
if ( ! all_links_are_type ( ) ) { fail ( " wrong link type " ) ; return ; }
}
static void phase_check_del_last ( void * arg ) {
( void ) arg ;
ctx . step = 12 ;
int l = count_links_to_srv ( ) ;
if ( l > 0 ) { fail ( " links survived last del " ) ; return ; }
/* повторная проверка через 300ms — после второго захода считаем ОК */
static int cnt = 0 ;
if ( + + cnt < 2 ) { uasync_set_timeout ( ctx . ua , STEP_TB , NULL , ( timeout_cb ) phase_check_del_last , " chk_del_last " ) ; return ; }
int l = count_links_to_srv ( rounds [ N_ROUNDS - 1 ] . ifname ) ;
if ( l > 0 ) {
/* повторная проверка через 300ms — после второго захода считаем ОК */
static int cnt = 0 ;
if ( + + cnt < 2 ) { uasync_set_timeout ( ctx . ua , STEP_TB , NULL , ( timeout_cb ) phase_check_del_last , " chk_del_last " ) ; return ; }
fail ( " links survived last del " ) ; return ;
}
fprintf ( stderr , " r=%d s=%d: no links after del_last (OK) \n " , ctx . round , ctx . step ) ; fflush ( stderr ) ;
uasync_set_timeout ( ctx . ua , STEP_TB , NULL , phase_done , " done " ) ;
}
static void phase_check_ip ( void * arg ) {
( void ) arg ; if ( ctx . result ) return ;
static uint64_t wait_start = 0 ;
ctx . step = 6 ;
if ( count_links_to_srv ( ctx . cur_iface ) = = 0 | | ctx . total_recv < = ctx . recv_at_ip_change ) {
if ( ! wait_start ) wait_start = get_time_tb ( ) ;
if ( get_time_tb ( ) - wait_start < ( uint64_t ) STEP_TB * 10 ) {
uasync_set_timeout ( ctx . ua , STEP_TB / 3 , NULL , phase_check_ip , " chk_ip " ) ; return ;
}
fail ( " traffic not recovered after IP change " ) ;
return ;
}
wait_start = 0 ;
do_check ( ) ; if ( ctx . result ) return ;
if ( ctx . round = = N_ROUNDS - 1 & & ctx . ip_changes_on_last < 2 ) {
@ -282,6 +278,7 @@ static void phase_check_ip(void* arg) {
fflush ( stderr ) ;
uasync_set_timeout ( ctx . ua , STEP_TB , NULL , phase_del_last , " del_last " ) ;
} else {
strncpy ( ctx . prev_iface , ctx . cur_iface , IFNAMSIZ - 1 ) ;
ctx . round + + ;
uasync_set_timeout ( ctx . ua , STEP_TB , NULL , phase_add , " next_add " ) ;
}
@ -289,12 +286,14 @@ static void phase_check_ip(void* arg) {
static void phase_change_ip ( void * arg ) {
( void ) arg ; if ( ctx . result ) return ;
TD ( " CHG r=%d start " , ctx . round ) ;
ctx . step = 5 ;
const char * old_ip = ( ctx . ip_changes_on_last > = 2 ) ? rounds [ ctx . round ] . ip2 : rounds [ ctx . round ] . ip1 ;
const char * new_ip = ( ctx . ip_changes_on_last > = 2 ) ? rounds [ ctx . round ] . ip1 : rounds [ ctx . round ] . ip2 ;
ip_addr_del ( rounds [ ctx . round ] . ifname , old_ip ) ;
ip_addr_add ( rounds [ ctx . round ] . ifname , new_ip ) ;
keep_only_socket_type ( ctx . keep_tcp ) ;
ctx . recv_at_ip_change = ctx . total_recv ;
TD ( " CHG r=%d sys done " , ctx . round ) ;
uasync_set_timeout ( ctx . ua , STEP_TB , NULL , phase_check_ip , " chk_ip " ) ;
}
@ -302,7 +301,7 @@ static void phase_check_del(void* arg) {
( void ) arg ; if ( ctx . result ) return ;
ctx . step = 4 ;
do_check ( ) ; if ( ctx . result ) return ;
fprintf ( stderr , " r=%d s=%d: del_prev OK links=%d \n " , ctx . round , ctx . step , count_links_to_srv ( ) ) ;
fprintf ( stderr , " r=%d s=%d: del_prev OK links=%d \n " , ctx . round , ctx . step , count_links_to_srv ( ctx . cur_iface ) ) ;
fflush ( stderr ) ;
uasync_set_timeout ( ctx . ua , STEP_TB , NULL , phase_change_ip , " chg_ip " ) ;
}
@ -316,16 +315,16 @@ static void phase_del_prev(void* arg) {
static void phase_check_add ( void * arg ) {
( void ) arg ; if ( ctx . result ) return ;
TD ( " CHKADD r=%d start " , ctx . round ) ;
ctx . step = 2 ;
/* wait for etcp_connect on round 0 */
if ( ctx . round = = 0 & & ctx . step = = 2 & & ! ctx . srv_ conn) {
if ( ctx . round = = 0 & & ! ctx . connected ) {
uasync_set_timeout ( ctx . ua , STEP_TB / 3 , NULL , phase_check_add , " chk_add " ) ;
return ;
}
if ( ctx . round = = 0 & & ! ctx . srv_conn ) { fail ( " etcp_connect didn't fire " ) ; return ; }
do_check ( ) ; if ( ctx . result ) return ;
fprintf ( stderr , " r=%d s=%d: add OK links=%d type=%s \n " ,
ctx . round , ctx . step , count_links_to_srv ( ) , ctx . keep_tcp ? " TCP " : " UDP " ) ;
fprintf ( stderr , " r=%d s=%d: add OK links=%d \n " ,
ctx . round , ctx . step , count_links_to_srv ( ctx . cur_iface ) ) ;
fflush ( stderr ) ;
if ( ctx . round = = 0 ) start_traffic ( ) ;
@ -339,14 +338,14 @@ static void phase_check_add(void* arg) {
static void phase_add ( void * arg ) {
( void ) arg ; if ( ctx . result ) return ;
TD ( " ADD r=%d start " , ctx . round ) ;
ctx . step = 1 ;
ctx . keep_tcp = rounds [ ctx . round ] . keep_tcp ;
strncpy ( ctx . cur_iface , rounds [ ctx . round ] . ifname , IFNAMSIZ - 1 ) ;
ip_link_add ( ctx . cur_iface ) ;
ip_addr_add ( ctx . cur_iface , rounds [ ctx . round ] . ip1 ) ;
ip_link_up ( ctx . cur_iface ) ;
keep_only_socket_type ( ctx . keep_tcp ) ;
TD ( " ADD r=%d sys done " , ctx . round ) ;
if ( ctx . round = = 0 ) {
struct TOPO_GROUP_NODE * sn = mk_srv_node ( ) ;
@ -367,7 +366,6 @@ static void cleanup_ifaces(void) {
static void setup ( void ) {
if ( geteuid ( ) ! = 0 ) { fprintf ( stderr , " SKIP: test requires root \n " ) ; exit ( 77 ) ; }
atexit ( cleanup_ifaces ) ;
cleanup_ifaces ( ) ;
test_mkdtemp ( tdir ) ;
snprintf ( scf , sizeof ( scf ) , " %s/s.conf " , tdir ) ;
@ -379,7 +377,7 @@ static void setup(void) {
/* server: fixed [server] on dummy_srv */
ip_link_add ( " dummy_srv " ) ;
ip_addr_add ( " dummy_srv " , " 10.90.0.1/24 " ) ;
ip_addr_add ( " dummy_srv " , " 10.90.0.1/16 " ) ;
ip_link_up ( " dummy_srv " ) ;
/* client: first interface */
@ -387,7 +385,7 @@ static void setup(void) {
ip_addr_add ( rounds [ 0 ] . ifname , rounds [ 0 ] . ip1 ) ;
ip_link_up ( rounds [ 0 ] . ifname ) ;
/* write initial configs */
/* Step 1: write m inimal configs → genera te keys + node_id v ia config_en sure */
wf ( scf , " [global] \n tun_ip=10.99.0.1/24 \n tun_ifname=tun_srv \n tun_test_mode=1 \n "
" auto_sockets=no \n db_path=%s/db_srv \n "
" [server: fixed] \n addr=10.90.0.1:9001 \n type=public \n [allowed_keys] \n allow_all=1 \n " , tdir ) ;
@ -396,33 +394,23 @@ static void setup(void) {
config_ensure_keys_and_node_id ( scf ) ;
config_ensure_keys_and_node_id ( ccf ) ;
{ struct utun_config * cs = parse_config ( scf ) ; ctx . srv_node_id = cs - > global . my_node_id ; free_config ( cs ) ; }
{ struct utun_config * cc = parse_config ( ccf ) ; ( void ) cc - > global . my_node_id ; free_config ( cc ) ; }
/* Step 2: read generated keys and node_id */
char * spub = gv ( scf , " pub " ) , * spriv = gv ( scf , " priv " ) ;
char * cpub = gv ( ccf , " pub " ) , * cpriv = gv ( ccf , " priv " ) ;
{ struct utun_config * cs = parse_config ( scf ) ; ctx . srv_node_id = cs - > global . my_node_id ; free_config ( cs ) ; }
wf ( scf , " [global] \n my_private_key=%s \n my_public_key=%s \n tun_ip=10.99.0.1/24 \n tun_ifname=tun_srv \n "
" tun_test_mode=1 \n auto_sockets=no \n db_path=%s/db_srv \n "
/* Step 3: write final configs with all values embedded */
wf ( scf , " [global] \n my_private_key=%s \n my_public_key=%s \n my_node_id=0x%016llx \n "
" tun_ip=10.99.0.1/24 \n tun_ifname=tun_srv \n tun_test_mode=1 \n "
" auto_sockets=no \n db_path=%s/db_srv \n "
" [server: fixed] \n addr=10.90.0.1:9001 \n type=public \n [allowed_keys] \n allow_all=1 \n " ,
spriv , spub , tdir ) ;
wf ( ccf , " [global] \n my_private_key=%s \n my_public_key=%s \n tun_ip=10.99.0.2/24 \n tun_ifname=tun_cli \n "
" tun_test_mode=1 \n auto_sockets=yes \n db_path=%s/db_cli \n [allowed_keys] \n allow_all=1 \n " ,
spriv , spub , ( unsigned long long ) ctx . srv_node_id , tdir ) ;
wf ( ccf , " [global] \n my_private_key=%s \n my_public_key=%s \n "
" tun_ip=10.99.0.2/24 \n tun_ifname=tun_cli \n tun_test_mode=1 \n "
" auto_sockets=yes \n db_path=%s/db_cli \n [allowed_keys] \n allow_all=1 \n " ,
cpriv , cpub , tdir ) ;
/* verify config */
{ struct utun_config * ck = parse_config ( ccf ) ;
fprintf ( stderr , " [setup] client: auto_sockets=%d node_id=0x%016llx \n " ,
ck ? ck - > global . auto_sockets : - 1 ,
( unsigned long long ) ( ck ? ck - > global . my_node_id : 0 ) ) ;
free_config ( ck ) ;
ck = parse_config ( scf ) ;
fprintf ( stderr , " [setup] server: auto_sockets=%d node_id=0x%016llx \n " ,
ck ? ck - > global . auto_sockets : - 1 ,
( unsigned long long ) ( ck ? ck - > global . my_node_id : 0 ) ) ;
fflush ( stderr ) ;
free_config ( ck ) ; }
/* extract server pubkey binary */
struct utun_config * cs2 = parse_config ( scf ) ;
if ( cs2 & & cs2 - > global . my_public_key_hex )
sc_hex_to_binary ( cs2 - > global . my_public_key_hex , ctx . srv_pubkey , SC_PUBKEY_SIZE ) ;