@ -1,6 +1,11 @@
/**
/**
* @ file nat_detection . c
* @ file nat_detection . c
* @ brief NAT - д е т е к ц и я : STUN - like п р о в е р к а ч е р е з т р е т ь е г о BGP - п и р а .
* @ brief NAT - д е т е к ц и я : STUN - like п р о в е р к а ч е р е з т р е т ь е г о BGP - п и р а .
*
* С а м о д о с т а т о ч н ы й м о д у л ь :
* - С а м б и н д и т с я н а ETCP_ID_NAT_DETECTION ( 0x02 ) и п р и н и м а е т с в о и п а к е т ы
* - С а м п о д п и с ы в а е т с я н а on_down н о в ы х ETCP - с о е д и н е н и й ( о т м е н а pending )
* - П о л у ч а е т trigger_checks и з topo_group п о с л е з а п о л н е н и я senders_list
*/
*/
# include <stdlib.h>
# include <stdlib.h>
# include <string.h>
# include <string.h>
@ -106,132 +111,15 @@ static void nat_detection_start_link_check(struct NAT_DETECTION* nd, struct TOPO
}
}
/* ====== Lifecycle ====== */
/* ====== Handle incoming NAT messages (static, dispatched from receive_cbk) ====== */
struct NAT_DETECTION * nat_detection_create ( struct UTUN_INSTANCE * inst ) {
if ( ! inst ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " nat_detection_create: inst is NULL " ) ; return NULL ; }
struct NAT_DETECTION * nd = u_calloc ( 1 , sizeof ( struct NAT_DETECTION ) ) ;
if ( ! nd ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " nat_detection_create: alloc failed " ) ; return NULL ; }
nd - > inst = inst ;
nd - > next_ping_req_id = 1 ;
nd - > allow_nat_check_local = 0 ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " NAT detection initialized " ) ;
return nd ;
}
void nat_detection_destroy ( struct NAT_DETECTION * nd ) {
if ( ! nd ) return ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " NAT detection destroy " ) ;
route_ping_destroy_pending ( nd ) ;
u_free ( nd ) ;
}
/* ====== BGP peer up/down ====== */
void nat_detection_bgp_peer_up ( struct NAT_DETECTION * nd , struct TOPO_GROUP * group ,
struct ETCP_CONN * conn ) {
if ( ! nd | | ! group | | ! conn | | ! conn - > instance ) return ;
struct ll_entry * entry = conn - > instance - > connections - > head ;
while ( entry ) {
struct conn_queue_entry * ce = ( struct conn_queue_entry * ) entry - > data ;
struct ETCP_LINK * l = ce - > conn - > links ;
while ( l ) {
if ( l - > initialized & & l - > conn & & l - > nat_check_status < NAT_CHECK_IN_PROGRESS )
nat_detection_start_link_check ( nd , group , l ) ;
l = l - > next ;
}
entry = entry - > next ;
}
}
void nat_detection_bgp_peer_down ( struct NAT_DETECTION * nd , struct TOPO_GROUP * group ,
struct ETCP_CONN * conn ) {
if ( ! nd | | ! group | | ! conn ) return ;
route_ping_cancel_for_conn ( nd , conn ) ;
struct ETCP_LINK * lnk = conn - > links ;
while ( lnk ) {
if ( lnk - > nat_check_status = = NAT_CHECK_IN_PROGRESS ) lnk - > nat_check_status = NAT_CHECK_NONE ;
lnk = lnk - > next ;
}
}
/* ====== Link lifecycle ====== */
void nat_detection_link_ready ( struct NAT_DETECTION * nd , struct TOPO_GROUP * group ,
struct ETCP_LINK * link ) {
if ( ! nd | | ! group | | ! link ) return ;
if ( link - > nat_check_status < NAT_CHECK_IN_PROGRESS )
nat_detection_start_link_check ( nd , group , link ) ;
}
void nat_detection_cancel_for_conn ( struct NAT_DETECTION * nd , struct ETCP_CONN * conn ) {
if ( ! nd | | ! conn ) return ;
route_ping_cancel_for_conn ( nd , conn ) ;
}
static void nat_detection_handle_nat_info ( struct NAT_DETECTION * nd , struct TOPO_GROUP * group ,
/* ====== Send NAT messages ====== */
struct ETCP_CONN * from_conn ,
const uint8_t * data , size_t len ) {
void nat_detection_send_nat_info ( struct NAT_DETECTION * nd , struct ETCP_CONN * to_conn ,
if ( ! nd | | ! group | | ! from_conn | | ! data | | len < sizeof ( struct NATDET_MSG_NAT_INFO )
uint8_t socket_id , uint32_t nat_ip , uint16_t nat_port ,
uint8_t nat_type ) {
( void ) nd ;
if ( ! to_conn ) return ;
struct TOPOMSG_NAT_INFO * pkt = u_calloc ( 1 , sizeof ( struct TOPOMSG_NAT_INFO ) ) ;
if ( ! pkt ) return ;
pkt - > cmd = ETCP_ID_TOPO_ENTRY ; pkt - > subcmd = TOPO_SUBCMD_NAT_INFO ;
pkt - > socket_id = socket_id ;
pkt - > nat_ip [ 0 ] = ( nat_ip > > 24 ) & 0xFF ; pkt - > nat_ip [ 1 ] = ( nat_ip > > 16 ) & 0xFF ;
pkt - > nat_ip [ 2 ] = ( nat_ip > > 8 ) & 0xFF ; pkt - > nat_ip [ 3 ] = nat_ip & 0xFF ;
pkt - > nat_port = htons ( nat_port ) ; pkt - > nat_type = nat_type ;
struct ll_entry * e = queue_entry_new ( 0 ) ;
if ( ! e ) { u_free ( pkt ) ; return ; }
e - > dgram = ( uint8_t * ) pkt ; e - > len = sizeof ( struct TOPOMSG_NAT_INFO ) ;
if ( etcp_send ( to_conn , e ) ! = 0 ) {
DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " etcp_send NAT_INFO failed " ) ;
u_free ( pkt ) ; queue_entry_free ( e ) ;
}
}
void nat_detection_send_nat_check_req ( struct NAT_DETECTION * nd , struct ETCP_CONN * to_conn ,
uint8_t socket_id ) {
( void ) nd ;
if ( ! to_conn ) return ;
struct TOPOMSG_NAT_CHECK_REQ * pkt = u_calloc ( 1 , sizeof ( struct TOPOMSG_NAT_CHECK_REQ ) ) ;
if ( ! pkt ) return ;
pkt - > cmd = ETCP_ID_TOPO_ENTRY ; pkt - > subcmd = TOPO_SUBCMD_NAT_CHECK_REQ ; pkt - > socket_id = socket_id ;
struct ETCP_LINK * l = to_conn - > links ;
while ( l ) {
if ( l - > remote_socket_id = = socket_id ) {
struct sockaddr_in * sin = ( struct sockaddr_in * ) & l - > conn - > interface_addr ;
if ( sin - > sin_family = = AF_INET ) {
pkt - > interface_ip = sin - > sin_addr . s_addr ;
pkt - > interface_port = sin - > sin_port ;
} break ;
}
l = l - > next ;
}
struct ll_entry * e = queue_entry_new ( 0 ) ;
if ( ! e ) { u_free ( pkt ) ; return ; }
e - > dgram = ( uint8_t * ) pkt ; e - > len = sizeof ( struct TOPOMSG_NAT_CHECK_REQ ) ;
if ( etcp_send ( to_conn , e ) ! = 0 ) {
DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " etcp_send NAT_CHECK_REQ failed " ) ;
u_free ( pkt ) ; queue_entry_free ( e ) ;
}
}
/* ====== Handle incoming NAT messages ====== */
void nat_detection_handle_nat_info ( struct NAT_DETECTION * nd , struct TOPO_GROUP * group ,
struct ETCP_CONN * from_conn ,
const uint8_t * data , size_t len ) {
if ( ! nd | | ! group | | ! from_conn | | ! data | | len < sizeof ( struct TOPOMSG_NAT_INFO )
| | ! group - > local_node | | ! group - > local_node - > node ) return ;
| | ! group - > local_node | | ! group - > local_node - > node ) return ;
const struct TOPO MSG_NAT_INFO* info = ( const struct TOPO MSG_NAT_INFO* ) data ;
const struct NATDET_MSG_NAT_INFO * info = ( const struct NATDET_MSG_NAT_INFO * ) data ;
uint32_t nat_ip = ( info - > nat_ip [ 0 ] < < 24 ) | ( info - > nat_ip [ 1 ] < < 16 )
uint32_t nat_ip = ( info - > nat_ip [ 0 ] < < 24 ) | ( info - > nat_ip [ 1 ] < < 16 )
| ( info - > nat_ip [ 2 ] < < 8 ) | info - > nat_ip [ 3 ] ;
| ( info - > nat_ip [ 2 ] < < 8 ) | info - > nat_ip [ 3 ] ;
uint16_t nat_port = ntohs ( info - > nat_port ) ;
uint16_t nat_port = ntohs ( info - > nat_port ) ;
@ -302,11 +190,11 @@ void nat_detection_handle_nat_info(struct NAT_DETECTION* nd, struct TOPO_GROUP*
while ( l ) { l - > nat_type = info - > nat_type ; l = l - > next ; }
while ( l ) { l - > nat_type = info - > nat_type ; l = l - > next ; }
}
}
void nat_detection_handle_nat_check_req ( struct NAT_DETECTION * nd , struct TOPO_GROUP * group ,
static void nat_detection_handle_nat_check_req ( struct NAT_DETECTION * nd , struct TOPO_GROUP * group ,
struct ETCP_CONN * from_conn ,
struct ETCP_CONN * from_conn ,
const uint8_t * data , size_t len ) {
const uint8_t * data , size_t len ) {
if ( ! nd | | ! group | | ! from_conn | | ! data | | len < sizeof ( struct TOPO MSG_NAT_CHECK_REQ) ) return ;
if ( ! nd | | ! group | | ! from_conn | | ! data | | len < sizeof ( struct NATDET_ MSG_NAT_CHECK_REQ) ) return ;
const struct TOPO MSG_NAT_CHECK_REQ* req = ( const struct TOPO MSG_NAT_CHECK_REQ* ) data ;
const struct NATDET_ MSG_NAT_CHECK_REQ* req = ( const struct NATDET_ MSG_NAT_CHECK_REQ* ) data ;
uint8_t socket_id = req - > socket_id ;
uint8_t socket_id = req - > socket_id ;
struct ETCP_LINK * target_link = NULL ;
struct ETCP_LINK * target_link = NULL ;
struct ETCP_LINK * l = from_conn - > links ;
struct ETCP_LINK * l = from_conn - > links ;
@ -320,6 +208,182 @@ void nat_detection_handle_nat_check_req(struct NAT_DETECTION* nd, struct TOPO_GR
}
}
/* ====== ETCP callbacks (self-registering) ====== */
static void nat_detection_on_conn_down ( struct ETCP_CONN * conn , void * arg ) {
struct NAT_DETECTION * nd = ( struct NAT_DETECTION * ) arg ;
if ( ! nd | | ! conn ) return ;
route_ping_cancel_for_conn ( nd , conn ) ;
struct ETCP_LINK * lnk = conn - > links ;
while ( lnk ) {
if ( lnk - > nat_check_status = = NAT_CHECK_IN_PROGRESS ) lnk - > nat_check_status = NAT_CHECK_NONE ;
lnk = lnk - > next ;
}
}
static void nat_detection_new_conn_cbk ( struct ETCP_CONN * conn , void * arg ) {
if ( ! conn | | ! conn - > instance ) return ;
struct NAT_DETECTION * nd = ( struct NAT_DETECTION * ) arg ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " NAT detection set callbacks: %s " , conn - > log_name ) ;
etcp_conn_add_down_cbk ( conn , nat_detection_on_conn_down , nd ) ;
}
/* ====== Receive callback (self-binded on ETCP_ID_NAT_DETECTION) ====== */
static const char * nat_subcmd_name ( uint8_t subcmd ) {
switch ( subcmd ) {
case NATDET_SUBCMD_PING_REQ : return " PING_REQ " ;
case NATDET_SUBCMD_PING_RESP : return " PING_RESP " ;
case NATDET_SUBCMD_NAT_INFO : return " NAT_INFO " ;
case NATDET_SUBCMD_NAT_CHECK_REQ : return " NAT_CHECK_REQ " ;
default : return " ? " ;
}
}
static void nat_detection_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 ;
if ( ! instance | | ! instance - > nat_det | | ! instance - > topo_groups ) {
DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " invalid instance/nat_det/topo_groups " ) ;
queue_dgram_free ( entry ) ; queue_entry_free ( entry ) ; return ;
}
struct TOPO_GROUP * group = topo_groups_get_default ( instance - > topo_groups ) ;
if ( ! group ) {
DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " no default group " ) ;
queue_dgram_free ( entry ) ; queue_entry_free ( entry ) ; return ;
}
struct NAT_DETECTION * nd = instance - > nat_det ;
uint8_t * data = entry - > dgram ; uint8_t subcmd = data [ 1 ] ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " NAT recv %s from %s len=%zu " ,
nat_subcmd_name ( subcmd ) , from_conn - > log_name , entry - > len ) ;
if ( subcmd = = NATDET_SUBCMD_PING_REQ )
route_ping_handle_req ( nd , group , from_conn , data , entry - > len ) ;
else if ( subcmd = = NATDET_SUBCMD_PING_RESP )
route_ping_handle_resp ( nd , from_conn , data , entry - > len ) ;
else if ( subcmd = = NATDET_SUBCMD_NAT_INFO )
nat_detection_handle_nat_info ( nd , group , from_conn , data , entry - > len ) ;
else if ( subcmd = = NATDET_SUBCMD_NAT_CHECK_REQ )
nat_detection_handle_nat_check_req ( nd , group , from_conn , data , entry - > len ) ;
queue_dgram_free ( entry ) ; queue_entry_free ( entry ) ;
}
/* ====== NAT check trigger (called from topo_group after senders_list updated) ====== */
void nat_detection_trigger_checks ( struct NAT_DETECTION * nd , struct TOPO_GROUP * group ) {
if ( ! nd | | ! group | | ! nd - > inst ) return ;
struct ll_entry * entry = nd - > inst - > connections - > head ;
while ( entry ) {
struct conn_queue_entry * ce = ( struct conn_queue_entry * ) entry - > data ;
struct ETCP_LINK * l = ce - > conn - > links ;
while ( l ) {
if ( l - > initialized & & l - > conn & & l - > nat_check_status < NAT_CHECK_IN_PROGRESS )
nat_detection_start_link_check ( nd , group , l ) ;
l = l - > next ;
}
entry = entry - > next ;
}
}
/* ====== Lifecycle ====== */
struct NAT_DETECTION * nat_detection_create ( struct UTUN_INSTANCE * inst ) {
if ( ! inst ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " nat_detection_create: inst is NULL " ) ; return NULL ; }
struct NAT_DETECTION * nd = u_calloc ( 1 , sizeof ( struct NAT_DETECTION ) ) ;
if ( ! nd ) { DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " nat_detection_create: alloc failed " ) ; return NULL ; }
nd - > inst = inst ;
nd - > next_ping_req_id = 1 ;
nd - > allow_nat_check_local = 0 ;
etcp_bind ( inst , ETCP_ID_NAT_DETECTION , nat_detection_receive_cbk ) ;
etcp_add_new_conn_cbk ( inst , nat_detection_new_conn_cbk , nd ) ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " NAT detection initialized " ) ;
return nd ;
}
void nat_detection_destroy ( struct NAT_DETECTION * nd ) {
if ( ! nd ) return ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " NAT detection destroy " ) ;
etcp_remove_new_conn_cbk ( nd - > inst , nat_detection_new_conn_cbk , nd ) ;
etcp_unbind ( nd - > inst , ETCP_ID_NAT_DETECTION ) ;
route_ping_destroy_pending ( nd ) ;
u_free ( nd ) ;
}
/* ====== Link lifecycle ====== */
void nat_detection_link_ready ( struct NAT_DETECTION * nd , struct TOPO_GROUP * group ,
struct ETCP_LINK * link ) {
if ( ! nd | | ! group | | ! link ) return ;
if ( link - > nat_check_status < NAT_CHECK_IN_PROGRESS )
nat_detection_start_link_check ( nd , group , link ) ;
}
void nat_detection_cancel_for_conn ( struct NAT_DETECTION * nd , struct ETCP_CONN * conn ) {
if ( ! nd | | ! conn ) return ;
route_ping_cancel_for_conn ( nd , conn ) ;
}
/* ====== Send NAT messages ====== */
void nat_detection_send_nat_info ( struct NAT_DETECTION * nd , struct ETCP_CONN * to_conn ,
uint8_t socket_id , uint32_t nat_ip , uint16_t nat_port ,
uint8_t nat_type ) {
( void ) nd ;
if ( ! to_conn ) return ;
struct NATDET_MSG_NAT_INFO * pkt = u_calloc ( 1 , sizeof ( struct NATDET_MSG_NAT_INFO ) ) ;
if ( ! pkt ) return ;
pkt - > cmd = ETCP_ID_NAT_DETECTION ; pkt - > subcmd = NATDET_SUBCMD_NAT_INFO ;
pkt - > socket_id = socket_id ;
pkt - > nat_ip [ 0 ] = ( nat_ip > > 24 ) & 0xFF ; pkt - > nat_ip [ 1 ] = ( nat_ip > > 16 ) & 0xFF ;
pkt - > nat_ip [ 2 ] = ( nat_ip > > 8 ) & 0xFF ; pkt - > nat_ip [ 3 ] = nat_ip & 0xFF ;
pkt - > nat_port = htons ( nat_port ) ; pkt - > nat_type = nat_type ;
struct ll_entry * e = queue_entry_new ( 0 ) ;
if ( ! e ) { u_free ( pkt ) ; return ; }
e - > dgram = ( uint8_t * ) pkt ; e - > len = sizeof ( struct NATDET_MSG_NAT_INFO ) ;
if ( etcp_send ( to_conn , e ) ! = 0 ) {
DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " etcp_send NAT_INFO failed " ) ;
u_free ( pkt ) ; queue_entry_free ( e ) ;
}
}
static void nat_detection_send_nat_check_req ( struct NAT_DETECTION * nd , struct ETCP_CONN * to_conn ,
uint8_t socket_id ) {
( void ) nd ;
if ( ! to_conn ) return ;
struct NATDET_MSG_NAT_CHECK_REQ * pkt = u_calloc ( 1 , sizeof ( struct NATDET_MSG_NAT_CHECK_REQ ) ) ;
if ( ! pkt ) return ;
pkt - > cmd = ETCP_ID_NAT_DETECTION ; pkt - > subcmd = NATDET_SUBCMD_NAT_CHECK_REQ ;
pkt - > socket_id = socket_id ;
struct ETCP_LINK * l = to_conn - > links ;
while ( l ) {
if ( l - > remote_socket_id = = socket_id ) {
struct sockaddr_in * sin = ( struct sockaddr_in * ) & l - > conn - > interface_addr ;
if ( sin - > sin_family = = AF_INET ) {
pkt - > interface_ip = sin - > sin_addr . s_addr ;
pkt - > interface_port = sin - > sin_port ;
} break ;
}
l = l - > next ;
}
struct ll_entry * e = queue_entry_new ( 0 ) ;
if ( ! e ) { u_free ( pkt ) ; return ; }
e - > dgram = ( uint8_t * ) pkt ; e - > len = sizeof ( struct NATDET_MSG_NAT_CHECK_REQ ) ;
if ( etcp_send ( to_conn , e ) ! = 0 ) {
DEBUG_ERROR ( DEBUG_CATEGORY_BGP , " etcp_send NAT_CHECK_REQ failed " ) ;
u_free ( pkt ) ; queue_entry_free ( e ) ;
}
}
/* ====== Control API ====== */
/* ====== Control API ====== */
void nat_detection_request_check_all ( struct NAT_DETECTION * nd , struct TOPO_GROUP * group ) {
void nat_detection_request_check_all ( struct NAT_DETECTION * nd , struct TOPO_GROUP * group ) {
@ -346,18 +410,3 @@ void nat_detection_set_allow_local(struct NAT_DETECTION* nd, int allow) {
if ( ! nd ) return ;
if ( ! nd ) return ;
nd - > allow_nat_check_local = allow ? 1 : 0 ;
nd - > allow_nat_check_local = allow ? 1 : 0 ;
}
}
/* ====== BGP subcommand wrappers (for topo_group dispatch) ====== */
void nat_detection_handle_ping_req ( struct NAT_DETECTION * nd , struct TOPO_GROUP * group ,
struct ETCP_CONN * from_conn ,
const uint8_t * data , size_t len ) {
route_ping_handle_req ( nd , group , from_conn , data , len ) ;
}
void nat_detection_handle_ping_resp ( struct NAT_DETECTION * nd ,
struct ETCP_CONN * from_conn ,
const uint8_t * data , size_t len ) {
route_ping_handle_resp ( nd , from_conn , data , len ) ;
}