@ -51,6 +51,20 @@
# define SET_INTERFACE
# endif
# ifdef USE_NFTABLES
# include <ctype.h>
# include <linux/netfilter.h>
# include <linux/netfilter/nf_tables.h>
# include <libmnl/libmnl.h>
# include <libnftnl/set.h>
/* the datatypes enum is picked from libnftables/datatype.h
to avoid to depend libnftables */
enum datatypes {
TYPE_IPADDR = 7 ,
TYPE_IP6ADDR
} ;
# endif
# include "netutils.h"
# include "utils.h"
# include "acl.h"
@ -278,6 +292,207 @@ stop_server(EV_P_ server_t *server)
server - > stage = STAGE_STOP ;
}
# ifdef USE_NFTABLES
struct nftbl_set_info {
uint32_t family ;
char * table ;
char * name ;
uint32_t type ;
} * nftbl_badip_sets [ 16 ] ;
static struct nftnl_set *
nftbl_build_set ( const char * table , const char * name , void * addr , size_t len )
{
struct nftnl_set * set = nftnl_set_alloc ( ) ;
if ( set = = NULL ) return NULL ;
nftnl_set_set_str ( set , NFTNL_SET_TABLE , table ) ;
nftnl_set_set_str ( set , NFTNL_SET_NAME , name ) ;
struct nftnl_set_elem * elem = nftnl_set_elem_alloc ( ) ;
if ( elem = = NULL ) {
nftnl_set_free ( set ) ;
return NULL ;
}
nftnl_set_elem_set ( elem , NFTNL_SET_ELEM_KEY , addr , len ) ;
nftnl_set_elem_add ( set , elem ) ;
return set ;
}
static uint32_t
nftbl_build_nlmsg ( void * buf , size_t * len , uint32_t family ,
struct nftnl_set * set )
{
uint32_t seq = time ( NULL ) ;
struct nlmsghdr * nlh ;
struct mnl_nlmsg_batch * batch = mnl_nlmsg_batch_start ( buf , * len ) ;
nftnl_batch_begin ( mnl_nlmsg_batch_current ( batch ) , seq ) ;
mnl_nlmsg_batch_next ( batch ) ;
nlh = nftnl_nlmsg_build_hdr ( mnl_nlmsg_batch_current ( batch ) ,
NFT_MSG_NEWSETELEM , family ,
NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK ,
+ + seq ) ;
nftnl_set_elems_nlmsg_build_payload ( nlh , set ) ;
mnl_nlmsg_batch_next ( batch ) ;
nftnl_batch_end ( mnl_nlmsg_batch_current ( batch ) , seq + 1 ) ;
mnl_nlmsg_batch_next ( batch ) ;
* len = mnl_nlmsg_batch_size ( batch ) ;
mnl_nlmsg_batch_stop ( batch ) ;
return seq ;
}
static int
nftbl_send_request ( void * request , size_t len , uint32_t seq ,
mnl_cb_t cb , void * data )
{
struct mnl_socket * nl = mnl_socket_open ( NETLINK_NETFILTER ) ;
if ( nl = = NULL ) return - 1 ;
int ret = - 1 ;
uint8_t buf [ MNL_SOCKET_BUFFER_SIZE ] ;
if ( mnl_socket_bind ( nl , 0 , MNL_SOCKET_AUTOPID ) = = 0 & &
mnl_socket_sendto ( nl , request , len ) > = 0 ) {
uint32_t portid = mnl_socket_get_portid ( nl ) ;
while ( ( ret = mnl_socket_recvfrom ( nl , buf , sizeof ( buf ) ) ) > 0 ) {
ret = mnl_cb_run ( buf , ret , seq , portid , cb , data ) ;
if ( ret ! = MNL_CB_OK )
break ;
}
mnl_socket_close ( nl ) ;
}
return ret ;
}
static void
nftbl_report_addr ( const struct sockaddr * addr )
{
uint32_t type ;
void * data ;
size_t size ;
if ( addr - > sa_family = = AF_INET ) {
type = TYPE_IPADDR ;
data = & ( ( struct sockaddr_in * ) addr ) - > sin_addr ;
size = sizeof ( struct in_addr ) ;
} else if ( addr - > sa_family = = AF_INET6 ) {
type = TYPE_IP6ADDR ;
data = & ( ( struct sockaddr_in6 * ) addr ) - > sin6_addr ;
size = sizeof ( struct in6_addr ) ;
} else {
return ;
}
char buf [ MNL_SOCKET_BUFFER_SIZE ] ;
for ( int i = 0 ; nftbl_badip_sets [ i ] ; + + i ) {
struct nftbl_set_info * si = nftbl_badip_sets [ i ] ;
struct nftnl_set * set ;
if ( si - > type = = type & &
( set = nftbl_build_set ( si - > table , si - > name , data , size ) ) ) {
size_t len = sizeof ( buf ) ;
uint32_t seq = nftbl_build_nlmsg ( buf , & len , si - > family , set ) ;
nftnl_set_free ( set ) ;
if ( nftbl_send_request ( buf , len , seq , NULL , NULL ) < 0 & &
errno ! = EEXIST )
ERROR ( " nftbl_report_addr " ) ;
}
}
}
static int
nftbl_check_cb ( const struct nlmsghdr * nlh , void * data )
{
struct nftnl_set * set = ( struct nftnl_set * ) data ;
if ( nftnl_set_nlmsg_parse ( nlh , set ) < 0 )
return MNL_CB_ERROR ;
uint32_t type = nftnl_set_get_u32 ( set , NFTNL_SET_KEY_TYPE ) ;
if ( type ! = TYPE_IPADDR & & type ! = TYPE_IP6ADDR )
return MNL_CB_OK ;
uint32_t len ;
const char * name = nftnl_set_get_data ( set , NFTNL_SET_NAME , & len ) ;
for ( int i = 0 ; nftbl_badip_sets [ i ] ; + + i ) {
struct nftbl_set_info * si = nftbl_badip_sets [ i ] ;
if ( ! memcmp ( name , si - > name , len ) ) {
name = nftnl_set_get_data ( set , NFTNL_SET_TABLE , & len ) ;
if ( ! si - > table ) {
size_t l = strlen ( si - > name ) + 1 ;
si = realloc ( si , sizeof ( * si ) + l + len ) ;
si - > name = ( char * ) ( si + 1 ) ;
si - > table = memcpy ( si - > name + l , name , len ) ;
nftbl_badip_sets [ i ] = si ;
} else if ( memcmp ( name , si - > table , len ) ) {
continue ; /* table name not match */
}
si - > family = nftnl_set_get_u32 ( set , NFTNL_SET_FAMILY ) ;
si - > type = type ;
}
}
return MNL_CB_OK ;
}
static int
nftbl_check ( void )
{
struct nftnl_set * set = nftnl_set_alloc ( ) ;
if ( ! set ) return - 1 ;
int ret ;
char buf [ MNL_SOCKET_BUFFER_SIZE ] ;
uint32_t seq = time ( NULL ) ;
struct nlmsghdr * nlh ;
nlh = nftnl_set_nlmsg_build_hdr ( buf , NFT_MSG_GETSET , NFPROTO_UNSPEC ,
NLM_F_DUMP | NLM_F_ACK , seq ) ;
nftnl_set_nlmsg_build_payload ( nlh , set ) ;
ret = nftbl_send_request ( nlh , nlh - > nlmsg_len , seq , nftbl_check_cb , set ) ;
nftnl_set_free ( set ) ;
if ( ret < 0 ) return ret ;
for ( int i = 0 ; nftbl_badip_sets [ i ] ; + + i ) {
struct nftbl_set_info * si = nftbl_badip_sets [ i ] ;
if ( si - > family = = NFPROTO_UNSPEC ) {
if ( si - > table )
LOGE ( " set '%s' not found in table '%s' " , si - > name , si - > table ) ;
else
LOGE ( " set '%s' not found " , si - > name ) ;
ret = - 1 ;
}
}
if ( ret < 0 )
FATAL ( " Check nftables configuration. " ) ;
return ret ;
}
static int
nftbl_init ( const char * set_str )
{
struct nftbl_set_info * si ;
const char * p0 = set_str , * p = p0 , * d = NULL ;
int i = 0 ;
do {
if ( * p = = ' : ' ) {
d = p ;
} else if ( * p = = ' , ' | | * p = = ' \0 ' ) {
size_t l = p - p0 + 1 ;
si = malloc ( sizeof ( * si ) + l ) ;
memset ( si , 0 , sizeof ( * si ) ) ;
si - > name = memcpy ( si + 1 , p0 , l ) ;
si - > name [ l - 1 ] = ' \0 ' ;
if ( d ) {
si - > table = si - > name ;
si - > name = si - > table + ( d - p0 ) ;
* ( si - > name + + ) = ' \0 ' ;
d = NULL ;
}
nftbl_badip_sets [ i + + ] = si ;
if ( i = = sizeof ( nftbl_badip_sets ) / sizeof ( * si ) - 1 )
break ;
while ( * p & & isspace ( * ( + + p ) ) ) ;
p0 = p ;
}
} while ( * ( p + + ) ) ;
return nftbl_check ( ) ;
}
# endif
static void
report_addr ( int fd , const char * info )
{
@ -286,6 +501,13 @@ report_addr(int fd, const char *info)
if ( peer_name ! = NULL ) {
LOGE ( " failed to handshake with %s: %s " , peer_name , info ) ;
}
# ifdef USE_NFTABLES
struct sockaddr_in6 addr ;
socklen_t len = sizeof ( struct sockaddr_in6 ) ;
if ( ! getpeername ( fd , ( struct sockaddr * ) & addr , & len ) )
nftbl_report_addr ( ( struct sockaddr * ) & addr ) ;
# endif
}
int
@ -1614,6 +1836,9 @@ main(int argc, char **argv)
{ " key " , required_argument , NULL , GETOPT_VAL_KEY } ,
# ifdef __linux__
{ " mptcp " , no_argument , NULL , GETOPT_VAL_MPTCP } ,
# ifdef USE_NFTABLES
{ " nftables-sets " , required_argument , NULL , GETOPT_VAL_NFTABLES_SETS } ,
# endif
# endif
{ NULL , 0 , NULL , 0 }
} ;
@ -1671,6 +1896,11 @@ main(int argc, char **argv)
case GETOPT_VAL_TCP_OUTGOING_RCVBUF :
tcp_outgoing_rcvbuf = atoi ( optarg ) ;
break ;
# ifdef USE_NFTABLES
case GETOPT_VAL_NFTABLES_SETS :
nftbl_init ( optarg ) ;
break ;
# endif
case ' s ' :
if ( server_num < MAX_REMOTE_NUM ) {
parse_addr ( optarg , & server_addr [ server_num + + ] ) ;