2020-05-08 10:46:08 -07:00
// SPDX-License-Identifier: GPL-2.0-only
# include <errno.h>
# include <stdbool.h>
# include <stdio.h>
# include <string.h>
# include <unistd.h>
2020-05-14 13:03:48 -07:00
2020-05-19 00:45:48 +02:00
# include <arpa/inet.h>
2020-05-08 10:46:08 -07:00
# include <linux/err.h>
# include <linux/in.h>
# include <linux/in6.h>
2020-05-14 13:03:48 -07:00
# include "bpf_util.h"
2020-05-08 10:46:08 -07:00
# include "network_helpers.h"
# define clean_errno() (errno == 0 ? "None" : strerror(errno))
2020-07-01 17:48:52 -07:00
# define log_err(MSG, ...) ({ \
int __save = errno ; \
fprintf ( stderr , " (%s:%d: errno: %s) " MSG " \n " , \
__FILE__ , __LINE__ , clean_errno ( ) , \
# #__VA_ARGS__); \
errno = __save ; \
} )
2020-05-08 10:46:08 -07:00
2020-05-08 10:46:09 -07:00
struct ipv4_packet pkt_v4 = {
. eth . h_proto = __bpf_constant_htons ( ETH_P_IP ) ,
. iph . ihl = 5 ,
. iph . protocol = IPPROTO_TCP ,
. iph . tot_len = __bpf_constant_htons ( MAGIC_BYTES ) ,
. tcp . urg_ptr = 123 ,
. tcp . doff = 5 ,
} ;
struct ipv6_packet pkt_v6 = {
. eth . h_proto = __bpf_constant_htons ( ETH_P_IPV6 ) ,
. iph . nexthdr = IPPROTO_TCP ,
. iph . payload_len = __bpf_constant_htons ( MAGIC_BYTES ) ,
. tcp . urg_ptr = 123 ,
. tcp . doff = 5 ,
} ;
2021-05-05 08:59:25 +00:00
int settimeo ( int fd , int timeout_ms )
2020-07-01 17:48:52 -07:00
{
struct timeval timeout = { . tv_sec = 3 } ;
if ( timeout_ms > 0 ) {
timeout . tv_sec = timeout_ms / 1000 ;
timeout . tv_usec = ( timeout_ms % 1000 ) * 1000 ;
}
if ( setsockopt ( fd , SOL_SOCKET , SO_RCVTIMEO , & timeout ,
sizeof ( timeout ) ) ) {
log_err ( " Failed to set SO_RCVTIMEO " ) ;
return - 1 ;
}
if ( setsockopt ( fd , SOL_SOCKET , SO_SNDTIMEO , & timeout ,
sizeof ( timeout ) ) ) {
log_err ( " Failed to set SO_SNDTIMEO " ) ;
return - 1 ;
}
return 0 ;
}
# define save_errno_close(fd) ({ int __save = errno; close(fd); errno = __save; })
2021-07-01 13:06:25 -07:00
static int __start_server ( int type , const struct sockaddr * addr ,
socklen_t addrlen , int timeout_ms , bool reuseport )
2020-05-08 10:46:08 -07:00
{
2021-07-01 13:06:25 -07:00
int on = 1 ;
2020-05-08 10:46:08 -07:00
int fd ;
2021-07-01 13:06:25 -07:00
fd = socket ( addr - > sa_family , type , 0 ) ;
2020-05-08 10:46:08 -07:00
if ( fd < 0 ) {
log_err ( " Failed to create server socket " ) ;
return - 1 ;
}
2020-07-01 17:48:52 -07:00
if ( settimeo ( fd , timeout_ms ) )
goto error_close ;
2021-07-01 13:06:25 -07:00
if ( reuseport & &
setsockopt ( fd , SOL_SOCKET , SO_REUSEPORT , & on , sizeof ( on ) ) ) {
log_err ( " Failed to set SO_REUSEPORT " ) ;
return - 1 ;
}
if ( bind ( fd , addr , addrlen ) < 0 ) {
2020-05-08 10:46:08 -07:00
log_err ( " Failed to bind socket " ) ;
2020-07-01 17:48:52 -07:00
goto error_close ;
2020-05-08 10:46:08 -07:00
}
if ( type = = SOCK_STREAM ) {
if ( listen ( fd , 1 ) < 0 ) {
log_err ( " Failed to listed on socket " ) ;
2020-07-01 17:48:52 -07:00
goto error_close ;
2020-05-08 10:46:08 -07:00
}
}
return fd ;
2020-07-01 17:48:52 -07:00
error_close :
save_errno_close ( fd ) ;
return - 1 ;
2020-05-19 00:45:48 +02:00
}
2021-07-01 13:06:25 -07:00
int start_server ( int family , int type , const char * addr_str , __u16 port ,
int timeout_ms )
{
struct sockaddr_storage addr ;
socklen_t addrlen ;
if ( make_sockaddr ( family , addr_str , port , & addr , & addrlen ) )
return - 1 ;
return __start_server ( type , ( struct sockaddr * ) & addr ,
addrlen , timeout_ms , false ) ;
}
int * start_reuseport_server ( int family , int type , const char * addr_str ,
__u16 port , int timeout_ms , unsigned int nr_listens )
{
struct sockaddr_storage addr ;
unsigned int nr_fds = 0 ;
socklen_t addrlen ;
int * fds ;
if ( ! nr_listens )
return NULL ;
if ( make_sockaddr ( family , addr_str , port , & addr , & addrlen ) )
return NULL ;
fds = malloc ( sizeof ( * fds ) * nr_listens ) ;
if ( ! fds )
return NULL ;
fds [ 0 ] = __start_server ( type , ( struct sockaddr * ) & addr , addrlen ,
timeout_ms , true ) ;
if ( fds [ 0 ] = = - 1 )
goto close_fds ;
nr_fds = 1 ;
if ( getsockname ( fds [ 0 ] , ( struct sockaddr * ) & addr , & addrlen ) )
goto close_fds ;
for ( ; nr_fds < nr_listens ; nr_fds + + ) {
fds [ nr_fds ] = __start_server ( type , ( struct sockaddr * ) & addr ,
addrlen , timeout_ms , true ) ;
if ( fds [ nr_fds ] = = - 1 )
goto close_fds ;
}
return fds ;
close_fds :
free_fds ( fds , nr_fds ) ;
return NULL ;
}
void free_fds ( int * fds , unsigned int nr_close_fds )
{
if ( fds ) {
while ( nr_close_fds )
close ( fds [ - - nr_close_fds ] ) ;
free ( fds ) ;
}
}
2020-08-20 12:01:11 -07:00
int fastopen_connect ( int server_fd , const char * data , unsigned int data_len ,
int timeout_ms )
{
struct sockaddr_storage addr ;
socklen_t addrlen = sizeof ( addr ) ;
struct sockaddr_in * addr_in ;
int fd , ret ;
if ( getsockname ( server_fd , ( struct sockaddr * ) & addr , & addrlen ) ) {
log_err ( " Failed to get server addr " ) ;
return - 1 ;
}
addr_in = ( struct sockaddr_in * ) & addr ;
fd = socket ( addr_in - > sin_family , SOCK_STREAM , 0 ) ;
if ( fd < 0 ) {
log_err ( " Failed to create client socket " ) ;
return - 1 ;
}
if ( settimeo ( fd , timeout_ms ) )
goto error_close ;
ret = sendto ( fd , data , data_len , MSG_FASTOPEN , ( struct sockaddr * ) & addr ,
addrlen ) ;
if ( ret ! = data_len ) {
log_err ( " sendto(data, %u) != %d \n " , data_len , ret ) ;
goto error_close ;
}
return fd ;
error_close :
save_errno_close ( fd ) ;
return - 1 ;
}
2020-07-01 17:48:52 -07:00
static int connect_fd_to_addr ( int fd ,
const struct sockaddr_storage * addr ,
2021-09-14 01:07:59 +02:00
socklen_t addrlen , const bool must_fail )
2020-05-08 10:46:08 -07:00
{
2021-09-14 01:07:59 +02:00
int ret ;
errno = 0 ;
ret = connect ( fd , ( const struct sockaddr * ) addr , addrlen ) ;
if ( must_fail ) {
if ( ! ret ) {
log_err ( " Unexpected success to connect to server " ) ;
return - 1 ;
}
if ( errno ! = EPERM ) {
log_err ( " Unexpected error from connect to server " ) ;
return - 1 ;
}
} else {
if ( ret ) {
log_err ( " Failed to connect to server " ) ;
return - 1 ;
}
2020-05-08 10:46:08 -07:00
}
2020-07-01 17:48:52 -07:00
return 0 ;
2020-05-14 13:03:48 -07:00
}
2021-08-24 10:30:19 -07:00
static const struct network_helper_opts default_opts ;
int connect_to_fd_opts ( int server_fd , const struct network_helper_opts * opts )
2020-05-14 13:03:48 -07:00
{
struct sockaddr_storage addr ;
2020-07-01 17:48:52 -07:00
struct sockaddr_in * addr_in ;
socklen_t addrlen , optlen ;
int fd , type ;
2020-05-14 13:03:48 -07:00
2021-08-24 10:30:19 -07:00
if ( ! opts )
opts = & default_opts ;
2020-07-01 17:48:52 -07:00
optlen = sizeof ( type ) ;
if ( getsockopt ( server_fd , SOL_SOCKET , SO_TYPE , & type , & optlen ) ) {
log_err ( " getsockopt(SOL_TYPE) " ) ;
2020-05-14 13:03:48 -07:00
return - 1 ;
2020-05-08 10:46:08 -07:00
}
2020-07-01 17:48:52 -07:00
addrlen = sizeof ( addr ) ;
if ( getsockname ( server_fd , ( struct sockaddr * ) & addr , & addrlen ) ) {
2020-05-08 10:46:08 -07:00
log_err ( " Failed to get server addr " ) ;
2020-05-14 13:03:48 -07:00
return - 1 ;
2020-05-08 10:46:08 -07:00
}
2020-07-01 17:48:52 -07:00
addr_in = ( struct sockaddr_in * ) & addr ;
fd = socket ( addr_in - > sin_family , type , 0 ) ;
if ( fd < 0 ) {
log_err ( " Failed to create client socket " ) ;
2020-05-14 13:03:48 -07:00
return - 1 ;
2020-05-08 10:46:08 -07:00
}
2021-08-24 10:30:19 -07:00
if ( settimeo ( fd , opts - > timeout_ms ) )
goto error_close ;
if ( opts - > cc & & opts - > cc [ 0 ] & &
setsockopt ( fd , SOL_TCP , TCP_CONGESTION , opts - > cc ,
strlen ( opts - > cc ) + 1 ) )
2020-07-01 17:48:52 -07:00
goto error_close ;
2021-09-14 01:07:59 +02:00
if ( connect_fd_to_addr ( fd , & addr , addrlen , opts - > must_fail ) )
2020-07-01 17:48:52 -07:00
goto error_close ;
return fd ;
error_close :
save_errno_close ( fd ) ;
return - 1 ;
2020-05-14 13:03:48 -07:00
}
2021-08-24 10:30:19 -07:00
int connect_to_fd ( int server_fd , int timeout_ms )
{
struct network_helper_opts opts = {
. timeout_ms = timeout_ms ,
} ;
return connect_to_fd_opts ( server_fd , & opts ) ;
}
2020-07-01 17:48:52 -07:00
int connect_fd_to_fd ( int client_fd , int server_fd , int timeout_ms )
2020-05-14 13:03:48 -07:00
{
2020-07-01 17:48:52 -07:00
struct sockaddr_storage addr ;
socklen_t len = sizeof ( addr ) ;
2020-05-14 13:03:48 -07:00
2020-07-01 17:48:52 -07:00
if ( settimeo ( client_fd , timeout_ms ) )
2020-05-14 13:03:48 -07:00
return - 1 ;
2020-07-01 17:48:52 -07:00
if ( getsockname ( server_fd , ( struct sockaddr * ) & addr , & len ) ) {
log_err ( " Failed to get server addr " ) ;
2020-05-14 13:03:48 -07:00
return - 1 ;
}
2021-09-14 01:07:59 +02:00
if ( connect_fd_to_addr ( client_fd , & addr , len , false ) )
2020-07-01 17:48:52 -07:00
return - 1 ;
2020-05-08 10:46:08 -07:00
2020-07-01 17:48:52 -07:00
return 0 ;
2020-05-08 10:46:08 -07:00
}
selftests/bpf: Tests for BPF_SK_LOOKUP attach point
Add tests to test_progs that exercise:
- attaching/detaching/querying programs to BPF_SK_LOOKUP hook,
- redirecting socket lookup to a socket selected by BPF program,
- failing a socket lookup on BPF program's request,
- error scenarios for selecting a socket from BPF program,
- accessing BPF program context,
- attaching and running multiple BPF programs.
Run log:
bash-5.0# ./test_progs -n 70
#70/1 query lookup prog:OK
#70/2 TCP IPv4 redir port:OK
#70/3 TCP IPv4 redir addr:OK
#70/4 TCP IPv4 redir with reuseport:OK
#70/5 TCP IPv4 redir skip reuseport:OK
#70/6 TCP IPv6 redir port:OK
#70/7 TCP IPv6 redir addr:OK
#70/8 TCP IPv4->IPv6 redir port:OK
#70/9 TCP IPv6 redir with reuseport:OK
#70/10 TCP IPv6 redir skip reuseport:OK
#70/11 UDP IPv4 redir port:OK
#70/12 UDP IPv4 redir addr:OK
#70/13 UDP IPv4 redir with reuseport:OK
#70/14 UDP IPv4 redir skip reuseport:OK
#70/15 UDP IPv6 redir port:OK
#70/16 UDP IPv6 redir addr:OK
#70/17 UDP IPv4->IPv6 redir port:OK
#70/18 UDP IPv6 redir and reuseport:OK
#70/19 UDP IPv6 redir skip reuseport:OK
#70/20 TCP IPv4 drop on lookup:OK
#70/21 TCP IPv6 drop on lookup:OK
#70/22 UDP IPv4 drop on lookup:OK
#70/23 UDP IPv6 drop on lookup:OK
#70/24 TCP IPv4 drop on reuseport:OK
#70/25 TCP IPv6 drop on reuseport:OK
#70/26 UDP IPv4 drop on reuseport:OK
#70/27 TCP IPv6 drop on reuseport:OK
#70/28 sk_assign returns EEXIST:OK
#70/29 sk_assign honors F_REPLACE:OK
#70/30 sk_assign accepts NULL socket:OK
#70/31 access ctx->sk:OK
#70/32 narrow access to ctx v4:OK
#70/33 narrow access to ctx v6:OK
#70/34 sk_assign rejects TCP established:OK
#70/35 sk_assign rejects UDP connected:OK
#70/36 multi prog - pass, pass:OK
#70/37 multi prog - drop, drop:OK
#70/38 multi prog - pass, drop:OK
#70/39 multi prog - drop, pass:OK
#70/40 multi prog - pass, redir:OK
#70/41 multi prog - redir, pass:OK
#70/42 multi prog - drop, redir:OK
#70/43 multi prog - redir, drop:OK
#70/44 multi prog - redir, redir:OK
#70 sk_lookup:OK
Summary: 1/44 PASSED, 0 SKIPPED, 0 FAILED
Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20200717103536.397595-16-jakub@cloudflare.com
2020-07-17 12:35:36 +02:00
int make_sockaddr ( int family , const char * addr_str , __u16 port ,
struct sockaddr_storage * addr , socklen_t * len )
{
if ( family = = AF_INET ) {
struct sockaddr_in * sin = ( void * ) addr ;
2021-07-01 13:06:25 -07:00
memset ( addr , 0 , sizeof ( * sin ) ) ;
selftests/bpf: Tests for BPF_SK_LOOKUP attach point
Add tests to test_progs that exercise:
- attaching/detaching/querying programs to BPF_SK_LOOKUP hook,
- redirecting socket lookup to a socket selected by BPF program,
- failing a socket lookup on BPF program's request,
- error scenarios for selecting a socket from BPF program,
- accessing BPF program context,
- attaching and running multiple BPF programs.
Run log:
bash-5.0# ./test_progs -n 70
#70/1 query lookup prog:OK
#70/2 TCP IPv4 redir port:OK
#70/3 TCP IPv4 redir addr:OK
#70/4 TCP IPv4 redir with reuseport:OK
#70/5 TCP IPv4 redir skip reuseport:OK
#70/6 TCP IPv6 redir port:OK
#70/7 TCP IPv6 redir addr:OK
#70/8 TCP IPv4->IPv6 redir port:OK
#70/9 TCP IPv6 redir with reuseport:OK
#70/10 TCP IPv6 redir skip reuseport:OK
#70/11 UDP IPv4 redir port:OK
#70/12 UDP IPv4 redir addr:OK
#70/13 UDP IPv4 redir with reuseport:OK
#70/14 UDP IPv4 redir skip reuseport:OK
#70/15 UDP IPv6 redir port:OK
#70/16 UDP IPv6 redir addr:OK
#70/17 UDP IPv4->IPv6 redir port:OK
#70/18 UDP IPv6 redir and reuseport:OK
#70/19 UDP IPv6 redir skip reuseport:OK
#70/20 TCP IPv4 drop on lookup:OK
#70/21 TCP IPv6 drop on lookup:OK
#70/22 UDP IPv4 drop on lookup:OK
#70/23 UDP IPv6 drop on lookup:OK
#70/24 TCP IPv4 drop on reuseport:OK
#70/25 TCP IPv6 drop on reuseport:OK
#70/26 UDP IPv4 drop on reuseport:OK
#70/27 TCP IPv6 drop on reuseport:OK
#70/28 sk_assign returns EEXIST:OK
#70/29 sk_assign honors F_REPLACE:OK
#70/30 sk_assign accepts NULL socket:OK
#70/31 access ctx->sk:OK
#70/32 narrow access to ctx v4:OK
#70/33 narrow access to ctx v6:OK
#70/34 sk_assign rejects TCP established:OK
#70/35 sk_assign rejects UDP connected:OK
#70/36 multi prog - pass, pass:OK
#70/37 multi prog - drop, drop:OK
#70/38 multi prog - pass, drop:OK
#70/39 multi prog - drop, pass:OK
#70/40 multi prog - pass, redir:OK
#70/41 multi prog - redir, pass:OK
#70/42 multi prog - drop, redir:OK
#70/43 multi prog - redir, drop:OK
#70/44 multi prog - redir, redir:OK
#70 sk_lookup:OK
Summary: 1/44 PASSED, 0 SKIPPED, 0 FAILED
Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20200717103536.397595-16-jakub@cloudflare.com
2020-07-17 12:35:36 +02:00
sin - > sin_family = AF_INET ;
sin - > sin_port = htons ( port ) ;
if ( addr_str & &
inet_pton ( AF_INET , addr_str , & sin - > sin_addr ) ! = 1 ) {
log_err ( " inet_pton(AF_INET, %s) " , addr_str ) ;
return - 1 ;
}
if ( len )
* len = sizeof ( * sin ) ;
return 0 ;
} else if ( family = = AF_INET6 ) {
struct sockaddr_in6 * sin6 = ( void * ) addr ;
2021-07-01 13:06:25 -07:00
memset ( addr , 0 , sizeof ( * sin6 ) ) ;
selftests/bpf: Tests for BPF_SK_LOOKUP attach point
Add tests to test_progs that exercise:
- attaching/detaching/querying programs to BPF_SK_LOOKUP hook,
- redirecting socket lookup to a socket selected by BPF program,
- failing a socket lookup on BPF program's request,
- error scenarios for selecting a socket from BPF program,
- accessing BPF program context,
- attaching and running multiple BPF programs.
Run log:
bash-5.0# ./test_progs -n 70
#70/1 query lookup prog:OK
#70/2 TCP IPv4 redir port:OK
#70/3 TCP IPv4 redir addr:OK
#70/4 TCP IPv4 redir with reuseport:OK
#70/5 TCP IPv4 redir skip reuseport:OK
#70/6 TCP IPv6 redir port:OK
#70/7 TCP IPv6 redir addr:OK
#70/8 TCP IPv4->IPv6 redir port:OK
#70/9 TCP IPv6 redir with reuseport:OK
#70/10 TCP IPv6 redir skip reuseport:OK
#70/11 UDP IPv4 redir port:OK
#70/12 UDP IPv4 redir addr:OK
#70/13 UDP IPv4 redir with reuseport:OK
#70/14 UDP IPv4 redir skip reuseport:OK
#70/15 UDP IPv6 redir port:OK
#70/16 UDP IPv6 redir addr:OK
#70/17 UDP IPv4->IPv6 redir port:OK
#70/18 UDP IPv6 redir and reuseport:OK
#70/19 UDP IPv6 redir skip reuseport:OK
#70/20 TCP IPv4 drop on lookup:OK
#70/21 TCP IPv6 drop on lookup:OK
#70/22 UDP IPv4 drop on lookup:OK
#70/23 UDP IPv6 drop on lookup:OK
#70/24 TCP IPv4 drop on reuseport:OK
#70/25 TCP IPv6 drop on reuseport:OK
#70/26 UDP IPv4 drop on reuseport:OK
#70/27 TCP IPv6 drop on reuseport:OK
#70/28 sk_assign returns EEXIST:OK
#70/29 sk_assign honors F_REPLACE:OK
#70/30 sk_assign accepts NULL socket:OK
#70/31 access ctx->sk:OK
#70/32 narrow access to ctx v4:OK
#70/33 narrow access to ctx v6:OK
#70/34 sk_assign rejects TCP established:OK
#70/35 sk_assign rejects UDP connected:OK
#70/36 multi prog - pass, pass:OK
#70/37 multi prog - drop, drop:OK
#70/38 multi prog - pass, drop:OK
#70/39 multi prog - drop, pass:OK
#70/40 multi prog - pass, redir:OK
#70/41 multi prog - redir, pass:OK
#70/42 multi prog - drop, redir:OK
#70/43 multi prog - redir, drop:OK
#70/44 multi prog - redir, redir:OK
#70 sk_lookup:OK
Summary: 1/44 PASSED, 0 SKIPPED, 0 FAILED
Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20200717103536.397595-16-jakub@cloudflare.com
2020-07-17 12:35:36 +02:00
sin6 - > sin6_family = AF_INET6 ;
sin6 - > sin6_port = htons ( port ) ;
if ( addr_str & &
inet_pton ( AF_INET6 , addr_str , & sin6 - > sin6_addr ) ! = 1 ) {
log_err ( " inet_pton(AF_INET6, %s) " , addr_str ) ;
return - 1 ;
}
if ( len )
* len = sizeof ( * sin6 ) ;
return 0 ;
}
return - 1 ;
}
2021-08-04 13:55:24 -07:00
char * ping_command ( int family )
{
if ( family = = AF_INET6 ) {
/* On some systems 'ping' doesn't support IPv6, so use ping6 if it is present. */
if ( ! system ( " which ping6 >/dev/null 2>&1 " ) )
return " ping6 " ;
else
return " ping -6 " ;
}
return " ping " ;
}