2022-06-15 16:48:45 +03:00
// SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
# include <stdnoreturn.h>
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include <errno.h>
# include <unistd.h>
# include <getopt.h>
# include <signal.h>
# include <sys/types.h>
# include <bpf/bpf.h>
# include <bpf/libbpf.h>
# include <net/if.h>
# include <linux/if_link.h>
# include <linux/limits.h>
static unsigned int ifindex ;
static __u32 attached_prog_id ;
2022-06-15 16:48:47 +03:00
static bool attached_tc ;
2022-06-15 16:48:45 +03:00
static void noreturn cleanup ( int sig )
{
2022-06-15 16:48:47 +03:00
LIBBPF_OPTS ( bpf_xdp_attach_opts , opts ) ;
2022-06-15 16:48:45 +03:00
int prog_fd ;
int err ;
if ( attached_prog_id = = 0 )
exit ( 0 ) ;
2022-06-15 16:48:47 +03:00
if ( attached_tc ) {
LIBBPF_OPTS ( bpf_tc_hook , hook ,
. ifindex = ifindex ,
. attach_point = BPF_TC_INGRESS ) ;
err = bpf_tc_hook_destroy ( & hook ) ;
if ( err < 0 ) {
fprintf ( stderr , " Error: bpf_tc_hook_destroy: %s \n " , strerror ( - err ) ) ;
fprintf ( stderr , " Failed to destroy the TC hook \n " ) ;
exit ( 1 ) ;
}
exit ( 0 ) ;
}
2022-06-15 16:48:45 +03:00
prog_fd = bpf_prog_get_fd_by_id ( attached_prog_id ) ;
if ( prog_fd < 0 ) {
fprintf ( stderr , " Error: bpf_prog_get_fd_by_id: %s \n " , strerror ( - prog_fd ) ) ;
err = bpf_xdp_attach ( ifindex , - 1 , 0 , NULL ) ;
if ( err < 0 ) {
fprintf ( stderr , " Error: bpf_set_link_xdp_fd: %s \n " , strerror ( - err ) ) ;
fprintf ( stderr , " Failed to detach XDP program \n " ) ;
exit ( 1 ) ;
}
} else {
opts . old_prog_fd = prog_fd ;
err = bpf_xdp_attach ( ifindex , - 1 , XDP_FLAGS_REPLACE , & opts ) ;
close ( prog_fd ) ;
if ( err < 0 ) {
fprintf ( stderr , " Error: bpf_set_link_xdp_fd_opts: %s \n " , strerror ( - err ) ) ;
/* Not an error if already replaced by someone else. */
if ( err ! = - EEXIST ) {
fprintf ( stderr , " Failed to detach XDP program \n " ) ;
exit ( 1 ) ;
}
}
}
exit ( 0 ) ;
}
static noreturn void usage ( const char * progname )
{
2022-06-15 16:48:47 +03:00
fprintf ( stderr , " Usage: %s [--iface <iface>|--prog <prog_id>] [--mss4 <mss ipv4> --mss6 <mss ipv6> --wscale <wscale> --ttl <ttl>] [--ports <port1>,<port2>,...] [--single] [--tc] \n " ,
2022-06-15 16:48:45 +03:00
progname ) ;
exit ( 1 ) ;
}
static unsigned long parse_arg_ul ( const char * progname , const char * arg , unsigned long limit )
{
unsigned long res ;
char * endptr ;
errno = 0 ;
res = strtoul ( arg , & endptr , 10 ) ;
if ( errno ! = 0 | | * endptr ! = ' \0 ' | | arg [ 0 ] = = ' \0 ' | | res > limit )
usage ( progname ) ;
return res ;
}
static void parse_options ( int argc , char * argv [ ] , unsigned int * ifindex , __u32 * prog_id ,
2022-06-15 16:48:47 +03:00
__u64 * tcpipopts , char * * ports , bool * single , bool * tc )
2022-06-15 16:48:45 +03:00
{
static struct option long_options [ ] = {
{ " help " , no_argument , NULL , ' h ' } ,
{ " iface " , required_argument , NULL , ' i ' } ,
{ " prog " , required_argument , NULL , ' x ' } ,
{ " mss4 " , required_argument , NULL , 4 } ,
{ " mss6 " , required_argument , NULL , 6 } ,
{ " wscale " , required_argument , NULL , ' w ' } ,
{ " ttl " , required_argument , NULL , ' t ' } ,
{ " ports " , required_argument , NULL , ' p ' } ,
{ " single " , no_argument , NULL , ' s ' } ,
2022-06-15 16:48:47 +03:00
{ " tc " , no_argument , NULL , ' c ' } ,
2022-06-15 16:48:45 +03:00
{ NULL , 0 , NULL , 0 } ,
} ;
2022-11-11 11:08:36 +08:00
unsigned long mss4 , wscale , ttl ;
unsigned long long mss6 ;
2022-06-15 16:48:45 +03:00
unsigned int tcpipopts_mask = 0 ;
if ( argc < 2 )
usage ( argv [ 0 ] ) ;
* ifindex = 0 ;
* prog_id = 0 ;
* tcpipopts = 0 ;
* ports = NULL ;
* single = false ;
2023-02-03 00:53:35 +01:00
* tc = false ;
2022-06-15 16:48:45 +03:00
while ( true ) {
int opt ;
opt = getopt_long ( argc , argv , " " , long_options , NULL ) ;
if ( opt = = - 1 )
break ;
switch ( opt ) {
case ' h ' :
usage ( argv [ 0 ] ) ;
break ;
case ' i ' :
* ifindex = if_nametoindex ( optarg ) ;
if ( * ifindex = = 0 )
usage ( argv [ 0 ] ) ;
break ;
case ' x ' :
* prog_id = parse_arg_ul ( argv [ 0 ] , optarg , UINT32_MAX ) ;
if ( * prog_id = = 0 )
usage ( argv [ 0 ] ) ;
break ;
case 4 :
mss4 = parse_arg_ul ( argv [ 0 ] , optarg , UINT16_MAX ) ;
tcpipopts_mask | = 1 < < 0 ;
break ;
case 6 :
mss6 = parse_arg_ul ( argv [ 0 ] , optarg , UINT16_MAX ) ;
tcpipopts_mask | = 1 < < 1 ;
break ;
case ' w ' :
wscale = parse_arg_ul ( argv [ 0 ] , optarg , 14 ) ;
tcpipopts_mask | = 1 < < 2 ;
break ;
case ' t ' :
ttl = parse_arg_ul ( argv [ 0 ] , optarg , UINT8_MAX ) ;
tcpipopts_mask | = 1 < < 3 ;
break ;
case ' p ' :
* ports = optarg ;
break ;
case ' s ' :
* single = true ;
break ;
2022-06-15 16:48:47 +03:00
case ' c ' :
* tc = true ;
break ;
2022-06-15 16:48:45 +03:00
default :
usage ( argv [ 0 ] ) ;
}
}
if ( optind < argc )
usage ( argv [ 0 ] ) ;
if ( tcpipopts_mask = = 0xf ) {
if ( mss4 = = 0 | | mss6 = = 0 | | wscale = = 0 | | ttl = = 0 )
usage ( argv [ 0 ] ) ;
* tcpipopts = ( mss6 < < 32 ) | ( ttl < < 24 ) | ( wscale < < 16 ) | mss4 ;
} else if ( tcpipopts_mask ! = 0 ) {
usage ( argv [ 0 ] ) ;
}
if ( * ifindex ! = 0 & & * prog_id ! = 0 )
usage ( argv [ 0 ] ) ;
if ( * ifindex = = 0 & & * prog_id = = 0 )
usage ( argv [ 0 ] ) ;
}
2022-06-15 16:48:47 +03:00
static int syncookie_attach ( const char * argv0 , unsigned int ifindex , bool tc )
2022-06-15 16:48:45 +03:00
{
struct bpf_prog_info info = { } ;
__u32 info_len = sizeof ( info ) ;
char xdp_filename [ PATH_MAX ] ;
struct bpf_program * prog ;
struct bpf_object * obj ;
int prog_fd ;
int err ;
2022-09-01 22:22:53 +00:00
snprintf ( xdp_filename , sizeof ( xdp_filename ) , " %s_kern.bpf.o " , argv0 ) ;
2022-06-15 16:48:45 +03:00
obj = bpf_object__open_file ( xdp_filename , NULL ) ;
err = libbpf_get_error ( obj ) ;
if ( err < 0 ) {
fprintf ( stderr , " Error: bpf_object__open_file: %s \n " , strerror ( - err ) ) ;
return err ;
}
err = bpf_object__load ( obj ) ;
if ( err < 0 ) {
fprintf ( stderr , " Error: bpf_object__open_file: %s \n " , strerror ( - err ) ) ;
return err ;
}
2022-06-15 16:48:47 +03:00
prog = bpf_object__find_program_by_name ( obj , tc ? " syncookie_tc " : " syncookie_xdp " ) ;
2022-06-15 16:48:45 +03:00
if ( ! prog ) {
2022-06-15 16:48:47 +03:00
fprintf ( stderr , " Error: bpf_object__find_program_by_name: program was not found \n " ) ;
2022-06-15 16:48:45 +03:00
return - ENOENT ;
}
prog_fd = bpf_program__fd ( prog ) ;
2023-02-15 00:12:18 +01:00
err = bpf_prog_get_info_by_fd ( prog_fd , & info , & info_len ) ;
2022-06-15 16:48:45 +03:00
if ( err < 0 ) {
2023-02-15 00:12:18 +01:00
fprintf ( stderr , " Error: bpf_prog_get_info_by_fd: %s \n " ,
strerror ( - err ) ) ;
2022-06-15 16:48:45 +03:00
goto out ;
}
2022-06-15 16:48:47 +03:00
attached_tc = tc ;
2022-06-15 16:48:45 +03:00
attached_prog_id = info . id ;
signal ( SIGINT , cleanup ) ;
signal ( SIGTERM , cleanup ) ;
2022-06-15 16:48:47 +03:00
if ( tc ) {
LIBBPF_OPTS ( bpf_tc_hook , hook ,
. ifindex = ifindex ,
. attach_point = BPF_TC_INGRESS ) ;
LIBBPF_OPTS ( bpf_tc_opts , opts ,
. handle = 1 ,
. priority = 1 ,
. prog_fd = prog_fd ) ;
err = bpf_tc_hook_create ( & hook ) ;
if ( err < 0 ) {
fprintf ( stderr , " Error: bpf_tc_hook_create: %s \n " ,
strerror ( - err ) ) ;
goto fail ;
}
err = bpf_tc_attach ( & hook , & opts ) ;
if ( err < 0 ) {
fprintf ( stderr , " Error: bpf_tc_attach: %s \n " ,
strerror ( - err ) ) ;
goto fail ;
}
} else {
err = bpf_xdp_attach ( ifindex , prog_fd ,
XDP_FLAGS_UPDATE_IF_NOEXIST , NULL ) ;
if ( err < 0 ) {
fprintf ( stderr , " Error: bpf_set_link_xdp_fd: %s \n " ,
strerror ( - err ) ) ;
goto fail ;
}
2022-06-15 16:48:45 +03:00
}
err = 0 ;
out :
bpf_object__close ( obj ) ;
return err ;
2022-06-15 16:48:47 +03:00
fail :
signal ( SIGINT , SIG_DFL ) ;
signal ( SIGTERM , SIG_DFL ) ;
attached_prog_id = 0 ;
goto out ;
2022-06-15 16:48:45 +03:00
}
static int syncookie_open_bpf_maps ( __u32 prog_id , int * values_map_fd , int * ports_map_fd )
{
struct bpf_prog_info prog_info ;
__u32 map_ids [ 8 ] ;
__u32 info_len ;
int prog_fd ;
int err ;
int i ;
* values_map_fd = - 1 ;
* ports_map_fd = - 1 ;
prog_fd = bpf_prog_get_fd_by_id ( prog_id ) ;
if ( prog_fd < 0 ) {
fprintf ( stderr , " Error: bpf_prog_get_fd_by_id: %s \n " , strerror ( - prog_fd ) ) ;
return prog_fd ;
}
prog_info = ( struct bpf_prog_info ) {
. nr_map_ids = 8 ,
2022-11-11 11:08:36 +08:00
. map_ids = ( __u64 ) ( unsigned long ) map_ids ,
2022-06-15 16:48:45 +03:00
} ;
info_len = sizeof ( prog_info ) ;
2023-02-15 00:12:18 +01:00
err = bpf_prog_get_info_by_fd ( prog_fd , & prog_info , & info_len ) ;
2022-06-15 16:48:45 +03:00
if ( err ! = 0 ) {
2023-02-15 00:12:18 +01:00
fprintf ( stderr , " Error: bpf_prog_get_info_by_fd: %s \n " ,
strerror ( - err ) ) ;
2022-06-15 16:48:45 +03:00
goto out ;
}
if ( prog_info . nr_map_ids < 2 ) {
fprintf ( stderr , " Error: Found %u BPF maps, expected at least 2 \n " ,
prog_info . nr_map_ids ) ;
err = - ENOENT ;
goto out ;
}
for ( i = 0 ; i < prog_info . nr_map_ids ; i + + ) {
struct bpf_map_info map_info = { } ;
int map_fd ;
err = bpf_map_get_fd_by_id ( map_ids [ i ] ) ;
if ( err < 0 ) {
fprintf ( stderr , " Error: bpf_map_get_fd_by_id: %s \n " , strerror ( - err ) ) ;
goto err_close_map_fds ;
}
map_fd = err ;
info_len = sizeof ( map_info ) ;
2023-02-15 00:12:18 +01:00
err = bpf_map_get_info_by_fd ( map_fd , & map_info , & info_len ) ;
2022-06-15 16:48:45 +03:00
if ( err ! = 0 ) {
2023-02-15 00:12:18 +01:00
fprintf ( stderr , " Error: bpf_map_get_info_by_fd: %s \n " ,
strerror ( - err ) ) ;
2022-06-15 16:48:45 +03:00
close ( map_fd ) ;
goto err_close_map_fds ;
}
if ( strcmp ( map_info . name , " values " ) = = 0 ) {
* values_map_fd = map_fd ;
continue ;
}
if ( strcmp ( map_info . name , " allowed_ports " ) = = 0 ) {
* ports_map_fd = map_fd ;
continue ;
}
close ( map_fd ) ;
}
if ( * values_map_fd ! = - 1 & & * ports_map_fd ! = - 1 ) {
err = 0 ;
goto out ;
}
err = - ENOENT ;
err_close_map_fds :
if ( * values_map_fd ! = - 1 )
close ( * values_map_fd ) ;
if ( * ports_map_fd ! = - 1 )
close ( * ports_map_fd ) ;
* values_map_fd = - 1 ;
* ports_map_fd = - 1 ;
out :
close ( prog_fd ) ;
return err ;
}
int main ( int argc , char * argv [ ] )
{
int values_map_fd , ports_map_fd ;
__u64 tcpipopts ;
bool firstiter ;
__u64 prevcnt ;
__u32 prog_id ;
char * ports ;
bool single ;
int err = 0 ;
2022-06-15 16:48:47 +03:00
bool tc ;
2022-06-15 16:48:45 +03:00
2022-06-15 16:48:47 +03:00
parse_options ( argc , argv , & ifindex , & prog_id , & tcpipopts , & ports ,
& single , & tc ) ;
2022-06-15 16:48:45 +03:00
if ( prog_id = = 0 ) {
2022-06-15 16:48:47 +03:00
if ( ! tc ) {
err = bpf_xdp_query_id ( ifindex , 0 , & prog_id ) ;
if ( err < 0 ) {
fprintf ( stderr , " Error: bpf_get_link_xdp_id: %s \n " ,
strerror ( - err ) ) ;
goto out ;
}
2022-06-15 16:48:45 +03:00
}
if ( prog_id = = 0 ) {
2022-06-15 16:48:47 +03:00
err = syncookie_attach ( argv [ 0 ] , ifindex , tc ) ;
2022-06-15 16:48:45 +03:00
if ( err < 0 )
goto out ;
prog_id = attached_prog_id ;
}
}
err = syncookie_open_bpf_maps ( prog_id , & values_map_fd , & ports_map_fd ) ;
if ( err < 0 )
goto out ;
if ( ports ) {
__u16 port_last = 0 ;
__u32 port_idx = 0 ;
char * p = ports ;
fprintf ( stderr , " Replacing allowed ports \n " ) ;
while ( p & & * p ! = ' \0 ' ) {
char * token = strsep ( & p , " , " ) ;
__u16 port ;
port = parse_arg_ul ( argv [ 0 ] , token , UINT16_MAX ) ;
err = bpf_map_update_elem ( ports_map_fd , & port_idx , & port , BPF_ANY ) ;
if ( err ! = 0 ) {
fprintf ( stderr , " Error: bpf_map_update_elem: %s \n " , strerror ( - err ) ) ;
fprintf ( stderr , " Failed to add port %u (index %u) \n " ,
port , port_idx ) ;
goto out_close_maps ;
}
fprintf ( stderr , " Added port %u \n " , port ) ;
port_idx + + ;
}
err = bpf_map_update_elem ( ports_map_fd , & port_idx , & port_last , BPF_ANY ) ;
if ( err ! = 0 ) {
fprintf ( stderr , " Error: bpf_map_update_elem: %s \n " , strerror ( - err ) ) ;
fprintf ( stderr , " Failed to add the terminator value 0 (index %u) \n " ,
port_idx ) ;
goto out_close_maps ;
}
}
if ( tcpipopts ) {
__u32 key = 0 ;
fprintf ( stderr , " Replacing TCP/IP options \n " ) ;
err = bpf_map_update_elem ( values_map_fd , & key , & tcpipopts , BPF_ANY ) ;
if ( err ! = 0 ) {
fprintf ( stderr , " Error: bpf_map_update_elem: %s \n " , strerror ( - err ) ) ;
goto out_close_maps ;
}
}
if ( ( ports | | tcpipopts ) & & attached_prog_id = = 0 & & ! single )
goto out_close_maps ;
prevcnt = 0 ;
firstiter = true ;
while ( true ) {
__u32 key = 1 ;
__u64 value ;
err = bpf_map_lookup_elem ( values_map_fd , & key , & value ) ;
if ( err ! = 0 ) {
fprintf ( stderr , " Error: bpf_map_lookup_elem: %s \n " , strerror ( - err ) ) ;
goto out_close_maps ;
}
if ( firstiter ) {
prevcnt = value ;
firstiter = false ;
}
if ( single ) {
printf ( " Total SYNACKs generated: %llu \n " , value ) ;
break ;
}
printf ( " SYNACKs generated: %llu (total %llu) \n " , value - prevcnt , value ) ;
prevcnt = value ;
sleep ( 1 ) ;
}
out_close_maps :
close ( values_map_fd ) ;
close ( ports_map_fd ) ;
out :
return err = = 0 ? 0 : 1 ;
}