2019-03-22 09:54:06 +08:00
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018 Facebook
// Copyright (c) 2019 Cloudflare
2019-07-29 09:59:18 -07:00
# include <limits.h>
2019-03-22 09:54:06 +08:00
# include <string.h>
# include <stdlib.h>
# include <unistd.h>
# include <arpa/inet.h>
# include <netinet/in.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <bpf/bpf.h>
# include <bpf/libbpf.h>
# include "cgroup_helpers.h"
2022-04-06 15:41:13 +03:00
static int start_server ( const struct sockaddr * addr , socklen_t len , bool dual )
2019-03-22 09:54:06 +08:00
{
2022-04-06 15:41:13 +03:00
int mode = ! dual ;
2019-03-22 09:54:06 +08:00
int fd ;
fd = socket ( addr - > sa_family , SOCK_STREAM , 0 ) ;
if ( fd = = - 1 ) {
log_err ( " Failed to create server socket " ) ;
goto out ;
}
2022-04-06 15:41:13 +03:00
if ( addr - > sa_family = = AF_INET6 ) {
if ( setsockopt ( fd , IPPROTO_IPV6 , IPV6_V6ONLY , ( char * ) & mode ,
sizeof ( mode ) ) = = - 1 ) {
log_err ( " Failed to set the dual-stack mode " ) ;
goto close_out ;
}
}
2019-03-22 09:54:06 +08:00
if ( bind ( fd , addr , len ) = = - 1 ) {
log_err ( " Failed to bind server socket " ) ;
goto close_out ;
}
if ( listen ( fd , 128 ) = = - 1 ) {
log_err ( " Failed to listen on server socket " ) ;
goto close_out ;
}
goto out ;
close_out :
close ( fd ) ;
fd = - 1 ;
out :
return fd ;
}
2022-04-06 15:41:13 +03:00
static int connect_to_server ( const struct sockaddr * addr , socklen_t len )
2019-03-22 09:54:06 +08:00
{
int fd = - 1 ;
2022-04-06 15:41:13 +03:00
fd = socket ( addr - > sa_family , SOCK_STREAM , 0 ) ;
2019-03-22 09:54:06 +08:00
if ( fd = = - 1 ) {
log_err ( " Failed to create client socket " ) ;
goto out ;
}
2022-04-06 15:41:13 +03:00
if ( connect ( fd , ( const struct sockaddr * ) addr , len ) = = - 1 ) {
2019-03-22 09:54:06 +08:00
log_err ( " Fail to connect to server " ) ;
goto close_out ;
}
goto out ;
close_out :
close ( fd ) ;
fd = - 1 ;
out :
return fd ;
}
2019-07-29 09:59:18 -07:00
static int get_map_fd_by_prog_id ( int prog_id , bool * xdp )
2019-03-22 09:54:06 +08:00
{
struct bpf_prog_info info = { } ;
__u32 info_len = sizeof ( info ) ;
__u32 map_ids [ 1 ] ;
int prog_fd = - 1 ;
int map_fd = - 1 ;
prog_fd = bpf_prog_get_fd_by_id ( prog_id ) ;
if ( prog_fd < 0 ) {
log_err ( " Failed to get fd by prog id %d " , prog_id ) ;
goto err ;
}
info . nr_map_ids = 1 ;
info . map_ids = ( __u64 ) ( unsigned long ) map_ids ;
if ( bpf_obj_get_info_by_fd ( prog_fd , & info , & info_len ) ) {
log_err ( " Failed to get info by prog fd %d " , prog_fd ) ;
goto err ;
}
if ( ! info . nr_map_ids ) {
log_err ( " No maps found for prog fd %d " , prog_fd ) ;
goto err ;
}
2019-07-29 09:59:18 -07:00
* xdp = info . type = = BPF_PROG_TYPE_XDP ;
2019-03-22 09:54:06 +08:00
map_fd = bpf_map_get_fd_by_id ( map_ids [ 0 ] ) ;
if ( map_fd < 0 )
log_err ( " Failed to get fd by map id %d " , map_ids [ 0 ] ) ;
err :
if ( prog_fd > = 0 )
close ( prog_fd ) ;
return map_fd ;
}
2022-04-06 15:41:13 +03:00
static int run_test ( int server_fd , int results_fd , bool xdp ,
const struct sockaddr * addr , socklen_t len )
2019-03-22 09:54:06 +08:00
{
int client = - 1 , srv_client = - 1 ;
int ret = 0 ;
__u32 key = 0 ;
2019-07-29 09:59:18 -07:00
__u32 key_gen = 1 ;
__u32 key_mss = 2 ;
__u32 value = 0 ;
__u32 value_gen = 0 ;
__u32 value_mss = 0 ;
2019-03-22 09:54:06 +08:00
if ( bpf_map_update_elem ( results_fd , & key , & value , 0 ) < 0 ) {
log_err ( " Can't clear results " ) ;
goto err ;
}
2019-07-29 09:59:18 -07:00
if ( bpf_map_update_elem ( results_fd , & key_gen , & value_gen , 0 ) < 0 ) {
log_err ( " Can't clear results " ) ;
goto err ;
}
if ( bpf_map_update_elem ( results_fd , & key_mss , & value_mss , 0 ) < 0 ) {
log_err ( " Can't clear results " ) ;
goto err ;
}
2022-04-06 15:41:13 +03:00
client = connect_to_server ( addr , len ) ;
2019-03-22 09:54:06 +08:00
if ( client = = - 1 )
goto err ;
srv_client = accept ( server_fd , NULL , 0 ) ;
if ( srv_client = = - 1 ) {
log_err ( " Can't accept connection " ) ;
goto err ;
}
if ( bpf_map_lookup_elem ( results_fd , & key , & value ) < 0 ) {
log_err ( " Can't lookup result " ) ;
goto err ;
}
2019-07-29 09:59:18 -07:00
if ( value = = 0 ) {
log_err ( " Didn't match syncookie: %u " , value ) ;
goto err ;
}
if ( bpf_map_lookup_elem ( results_fd , & key_gen , & value_gen ) < 0 ) {
log_err ( " Can't lookup result " ) ;
goto err ;
}
if ( xdp & & value_gen = = 0 ) {
// SYN packets do not get passed through generic XDP, skip the
// rest of the test.
printf ( " Skipping XDP cookie check \n " ) ;
goto out ;
}
if ( bpf_map_lookup_elem ( results_fd , & key_mss , & value_mss ) < 0 ) {
log_err ( " Can't lookup result " ) ;
goto err ;
}
if ( value ! = value_gen ) {
log_err ( " BPF generated cookie does not match kernel one " ) ;
goto err ;
}
if ( value_mss < 536 | | value_mss > USHRT_MAX ) {
log_err ( " Unexpected MSS retrieved " ) ;
2019-03-22 09:54:06 +08:00
goto err ;
}
goto out ;
err :
ret = 1 ;
out :
close ( client ) ;
close ( srv_client ) ;
return ret ;
}
2022-04-06 15:41:13 +03:00
static bool get_port ( int server_fd , in_port_t * port )
{
struct sockaddr_in addr ;
socklen_t len = sizeof ( addr ) ;
if ( getsockname ( server_fd , ( struct sockaddr * ) & addr , & len ) ) {
log_err ( " Failed to get server addr " ) ;
return false ;
}
/* sin_port and sin6_port are located at the same offset. */
* port = addr . sin_port ;
return true ;
}
2019-03-22 09:54:06 +08:00
int main ( int argc , char * * argv )
{
struct sockaddr_in addr4 ;
struct sockaddr_in6 addr6 ;
2022-04-06 15:41:13 +03:00
struct sockaddr_in addr4dual ;
struct sockaddr_in6 addr6dual ;
2019-03-22 09:54:06 +08:00
int server = - 1 ;
int server_v6 = - 1 ;
2022-04-06 15:41:13 +03:00
int server_dual = - 1 ;
2019-03-22 09:54:06 +08:00
int results = - 1 ;
int err = 0 ;
2019-07-29 09:59:18 -07:00
bool xdp ;
2019-03-22 09:54:06 +08:00
if ( argc < 2 ) {
fprintf ( stderr , " Usage: %s prog_id \n " , argv [ 0 ] ) ;
exit ( 1 ) ;
}
2022-04-09 12:59:56 +00:00
/* Use libbpf 1.0 API mode */
libbpf_set_strict_mode ( LIBBPF_STRICT_ALL ) ;
2019-07-29 09:59:18 -07:00
results = get_map_fd_by_prog_id ( atoi ( argv [ 1 ] ) , & xdp ) ;
2019-03-22 09:54:06 +08:00
if ( results < 0 ) {
log_err ( " Can't get map " ) ;
goto err ;
}
memset ( & addr4 , 0 , sizeof ( addr4 ) ) ;
addr4 . sin_family = AF_INET ;
addr4 . sin_addr . s_addr = htonl ( INADDR_LOOPBACK ) ;
addr4 . sin_port = 0 ;
2022-04-06 15:41:13 +03:00
memcpy ( & addr4dual , & addr4 , sizeof ( addr4dual ) ) ;
2019-03-22 09:54:06 +08:00
memset ( & addr6 , 0 , sizeof ( addr6 ) ) ;
addr6 . sin6_family = AF_INET6 ;
addr6 . sin6_addr = in6addr_loopback ;
addr6 . sin6_port = 0 ;
2022-04-06 15:41:13 +03:00
memset ( & addr6dual , 0 , sizeof ( addr6dual ) ) ;
addr6dual . sin6_family = AF_INET6 ;
addr6dual . sin6_addr = in6addr_any ;
addr6dual . sin6_port = 0 ;
server = start_server ( ( const struct sockaddr * ) & addr4 , sizeof ( addr4 ) ,
false ) ;
if ( server = = - 1 | | ! get_port ( server , & addr4 . sin_port ) )
2019-03-22 09:54:06 +08:00
goto err ;
server_v6 = start_server ( ( const struct sockaddr * ) & addr6 ,
2022-04-06 15:41:13 +03:00
sizeof ( addr6 ) , false ) ;
if ( server_v6 = = - 1 | | ! get_port ( server_v6 , & addr6 . sin6_port ) )
goto err ;
server_dual = start_server ( ( const struct sockaddr * ) & addr6dual ,
sizeof ( addr6dual ) , true ) ;
if ( server_dual = = - 1 | | ! get_port ( server_dual , & addr4dual . sin_port ) )
goto err ;
if ( run_test ( server , results , xdp ,
( const struct sockaddr * ) & addr4 , sizeof ( addr4 ) ) )
2019-03-22 09:54:06 +08:00
goto err ;
2022-04-06 15:41:13 +03:00
if ( run_test ( server_v6 , results , xdp ,
( const struct sockaddr * ) & addr6 , sizeof ( addr6 ) ) )
2019-03-22 09:54:06 +08:00
goto err ;
2022-04-06 15:41:13 +03:00
if ( run_test ( server_dual , results , xdp ,
( const struct sockaddr * ) & addr4dual , sizeof ( addr4dual ) ) )
2019-03-22 09:54:06 +08:00
goto err ;
printf ( " ok \n " ) ;
goto out ;
err :
err = 1 ;
out :
close ( server ) ;
close ( server_v6 ) ;
2022-04-06 15:41:13 +03:00
close ( server_dual ) ;
2019-03-22 09:54:06 +08:00
close ( results ) ;
return err ;
}