2018-03-30 15:08:08 -07:00
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018 Facebook
# include <stdio.h>
# include <unistd.h>
# include <arpa/inet.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <linux/filter.h>
# include <bpf/bpf.h>
# include "cgroup_helpers.h"
2019-08-14 12:41:09 +02:00
# include "bpf_endian.h"
2018-04-18 10:49:12 -07:00
# include "bpf_rlimit.h"
2018-08-08 01:01:27 -07:00
# include "bpf_util.h"
2018-03-30 15:08:08 -07:00
# define CG_PATH " / foo"
# define MAX_INSNS 512
char bpf_log_buf [ BPF_LOG_BUF_SIZE ] ;
tools/bpf: add log_level to bpf_load_program_attr
The kernel verifier has three levels of logs:
0: no logs
1: logs mostly useful
> 1: verbose
Current libbpf API functions bpf_load_program_xattr() and
bpf_load_program() cannot specify log_level.
The bcc, however, provides an interface for user to
specify log_level 2 for verbose output.
This patch added log_level into structure
bpf_load_program_attr, so users, including bcc, can use
bpf_load_program_xattr() to change log_level. The
supported log_level is 0, 1, and 2.
The bpf selftest test_sock.c is modified to enable log_level = 2.
If the "verbose" in test_sock.c is changed to true,
the test will output logs like below:
$ ./test_sock
func#0 @0
0: R1=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
0: (bf) r6 = r1
1: R1=ctx(id=0,off=0,imm=0) R6_w=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
1: (61) r7 = *(u32 *)(r6 +28)
invalid bpf_context access off=28 size=4
Test case: bind4 load with invalid access: src_ip6 .. [PASS]
...
Test case: bind6 allow all .. [PASS]
Summary: 16 PASSED, 0 FAILED
Some test_sock tests are negative tests and verbose verifier
log will be printed out as shown in the above.
Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2019-02-07 09:34:51 -08:00
static bool verbose = false ;
2018-03-30 15:08:08 -07:00
struct sock_test {
const char * descr ;
/* BPF prog properties */
struct bpf_insn insns [ MAX_INSNS ] ;
enum bpf_attach_type expected_attach_type ;
enum bpf_attach_type attach_type ;
/* Socket properties */
int domain ;
int type ;
/* Endpoint to bind() to */
const char * ip ;
unsigned short port ;
/* Expected test result */
enum {
LOAD_REJECT ,
ATTACH_REJECT ,
BIND_REJECT ,
SUCCESS ,
} result ;
} ;
static struct sock_test tests [ ] = {
{
" bind4 load with invalid access: src_ip6 " ,
. insns = {
BPF_MOV64_REG ( BPF_REG_6 , BPF_REG_1 ) ,
BPF_LDX_MEM ( BPF_W , BPF_REG_7 , BPF_REG_6 ,
offsetof ( struct bpf_sock , src_ip6 [ 0 ] ) ) ,
BPF_MOV64_IMM ( BPF_REG_0 , 1 ) ,
BPF_EXIT_INSN ( ) ,
} ,
BPF_CGROUP_INET4_POST_BIND ,
BPF_CGROUP_INET4_POST_BIND ,
0 ,
0 ,
NULL ,
0 ,
LOAD_REJECT ,
} ,
{
" bind4 load with invalid access: mark " ,
. insns = {
BPF_MOV64_REG ( BPF_REG_6 , BPF_REG_1 ) ,
BPF_LDX_MEM ( BPF_W , BPF_REG_7 , BPF_REG_6 ,
offsetof ( struct bpf_sock , mark ) ) ,
BPF_MOV64_IMM ( BPF_REG_0 , 1 ) ,
BPF_EXIT_INSN ( ) ,
} ,
BPF_CGROUP_INET4_POST_BIND ,
BPF_CGROUP_INET4_POST_BIND ,
0 ,
0 ,
NULL ,
0 ,
LOAD_REJECT ,
} ,
{
" bind6 load with invalid access: src_ip4 " ,
. insns = {
BPF_MOV64_REG ( BPF_REG_6 , BPF_REG_1 ) ,
BPF_LDX_MEM ( BPF_W , BPF_REG_7 , BPF_REG_6 ,
offsetof ( struct bpf_sock , src_ip4 ) ) ,
BPF_MOV64_IMM ( BPF_REG_0 , 1 ) ,
BPF_EXIT_INSN ( ) ,
} ,
BPF_CGROUP_INET6_POST_BIND ,
BPF_CGROUP_INET6_POST_BIND ,
0 ,
0 ,
NULL ,
0 ,
LOAD_REJECT ,
} ,
{
" sock_create load with invalid access: src_port " ,
. insns = {
BPF_MOV64_REG ( BPF_REG_6 , BPF_REG_1 ) ,
BPF_LDX_MEM ( BPF_W , BPF_REG_7 , BPF_REG_6 ,
offsetof ( struct bpf_sock , src_port ) ) ,
BPF_MOV64_IMM ( BPF_REG_0 , 1 ) ,
BPF_EXIT_INSN ( ) ,
} ,
BPF_CGROUP_INET_SOCK_CREATE ,
BPF_CGROUP_INET_SOCK_CREATE ,
0 ,
0 ,
NULL ,
0 ,
LOAD_REJECT ,
} ,
{
" sock_create load w/o expected_attach_type (compat mode) " ,
. insns = {
BPF_MOV64_IMM ( BPF_REG_0 , 1 ) ,
BPF_EXIT_INSN ( ) ,
} ,
0 ,
BPF_CGROUP_INET_SOCK_CREATE ,
AF_INET ,
SOCK_STREAM ,
" 127.0.0.1 " ,
8097 ,
SUCCESS ,
} ,
{
" sock_create load w/ expected_attach_type " ,
. insns = {
BPF_MOV64_IMM ( BPF_REG_0 , 1 ) ,
BPF_EXIT_INSN ( ) ,
} ,
BPF_CGROUP_INET_SOCK_CREATE ,
BPF_CGROUP_INET_SOCK_CREATE ,
AF_INET ,
SOCK_STREAM ,
" 127.0.0.1 " ,
8097 ,
SUCCESS ,
} ,
{
" attach type mismatch bind4 vs bind6 " ,
. insns = {
BPF_MOV64_IMM ( BPF_REG_0 , 1 ) ,
BPF_EXIT_INSN ( ) ,
} ,
BPF_CGROUP_INET4_POST_BIND ,
BPF_CGROUP_INET6_POST_BIND ,
0 ,
0 ,
NULL ,
0 ,
ATTACH_REJECT ,
} ,
{
" attach type mismatch bind6 vs bind4 " ,
. insns = {
BPF_MOV64_IMM ( BPF_REG_0 , 1 ) ,
BPF_EXIT_INSN ( ) ,
} ,
BPF_CGROUP_INET6_POST_BIND ,
BPF_CGROUP_INET4_POST_BIND ,
0 ,
0 ,
NULL ,
0 ,
ATTACH_REJECT ,
} ,
{
" attach type mismatch default vs bind4 " ,
. insns = {
BPF_MOV64_IMM ( BPF_REG_0 , 1 ) ,
BPF_EXIT_INSN ( ) ,
} ,
0 ,
BPF_CGROUP_INET4_POST_BIND ,
0 ,
0 ,
NULL ,
0 ,
ATTACH_REJECT ,
} ,
{
" attach type mismatch bind6 vs sock_create " ,
. insns = {
BPF_MOV64_IMM ( BPF_REG_0 , 1 ) ,
BPF_EXIT_INSN ( ) ,
} ,
BPF_CGROUP_INET6_POST_BIND ,
BPF_CGROUP_INET_SOCK_CREATE ,
0 ,
0 ,
NULL ,
0 ,
ATTACH_REJECT ,
} ,
{
" bind4 reject all " ,
. insns = {
BPF_MOV64_IMM ( BPF_REG_0 , 0 ) ,
BPF_EXIT_INSN ( ) ,
} ,
BPF_CGROUP_INET4_POST_BIND ,
BPF_CGROUP_INET4_POST_BIND ,
AF_INET ,
SOCK_STREAM ,
" 0.0.0.0 " ,
0 ,
BIND_REJECT ,
} ,
{
" bind6 reject all " ,
. insns = {
BPF_MOV64_IMM ( BPF_REG_0 , 0 ) ,
BPF_EXIT_INSN ( ) ,
} ,
BPF_CGROUP_INET6_POST_BIND ,
BPF_CGROUP_INET6_POST_BIND ,
AF_INET6 ,
SOCK_STREAM ,
" :: " ,
0 ,
BIND_REJECT ,
} ,
{
" bind6 deny specific IP & port " ,
. insns = {
BPF_MOV64_REG ( BPF_REG_6 , BPF_REG_1 ) ,
/* if (ip == expected && port == expected) */
BPF_LDX_MEM ( BPF_W , BPF_REG_7 , BPF_REG_6 ,
offsetof ( struct bpf_sock , src_ip6 [ 3 ] ) ) ,
2019-08-14 12:41:09 +02:00
BPF_JMP_IMM ( BPF_JNE , BPF_REG_7 ,
__bpf_constant_ntohl ( 0x00000001 ) , 4 ) ,
2018-03-30 15:08:08 -07:00
BPF_LDX_MEM ( BPF_W , BPF_REG_7 , BPF_REG_6 ,
offsetof ( struct bpf_sock , src_port ) ) ,
BPF_JMP_IMM ( BPF_JNE , BPF_REG_7 , 0x2001 , 2 ) ,
/* return DENY; */
BPF_MOV64_IMM ( BPF_REG_0 , 0 ) ,
BPF_JMP_A ( 1 ) ,
/* else return ALLOW; */
BPF_MOV64_IMM ( BPF_REG_0 , 1 ) ,
BPF_EXIT_INSN ( ) ,
} ,
BPF_CGROUP_INET6_POST_BIND ,
BPF_CGROUP_INET6_POST_BIND ,
AF_INET6 ,
SOCK_STREAM ,
" ::1 " ,
8193 ,
BIND_REJECT ,
} ,
{
" bind4 allow specific IP & port " ,
. insns = {
BPF_MOV64_REG ( BPF_REG_6 , BPF_REG_1 ) ,
/* if (ip == expected && port == expected) */
BPF_LDX_MEM ( BPF_W , BPF_REG_7 , BPF_REG_6 ,
offsetof ( struct bpf_sock , src_ip4 ) ) ,
2019-08-14 12:41:09 +02:00
BPF_JMP_IMM ( BPF_JNE , BPF_REG_7 ,
__bpf_constant_ntohl ( 0x7F000001 ) , 4 ) ,
2018-03-30 15:08:08 -07:00
BPF_LDX_MEM ( BPF_W , BPF_REG_7 , BPF_REG_6 ,
offsetof ( struct bpf_sock , src_port ) ) ,
BPF_JMP_IMM ( BPF_JNE , BPF_REG_7 , 0x1002 , 2 ) ,
/* return ALLOW; */
BPF_MOV64_IMM ( BPF_REG_0 , 1 ) ,
BPF_JMP_A ( 1 ) ,
/* else return DENY; */
BPF_MOV64_IMM ( BPF_REG_0 , 0 ) ,
BPF_EXIT_INSN ( ) ,
} ,
BPF_CGROUP_INET4_POST_BIND ,
BPF_CGROUP_INET4_POST_BIND ,
AF_INET ,
SOCK_STREAM ,
" 127.0.0.1 " ,
4098 ,
SUCCESS ,
} ,
{
" bind4 allow all " ,
. insns = {
BPF_MOV64_IMM ( BPF_REG_0 , 1 ) ,
BPF_EXIT_INSN ( ) ,
} ,
BPF_CGROUP_INET4_POST_BIND ,
BPF_CGROUP_INET4_POST_BIND ,
AF_INET ,
SOCK_STREAM ,
" 0.0.0.0 " ,
0 ,
SUCCESS ,
} ,
{
" bind6 allow all " ,
. insns = {
BPF_MOV64_IMM ( BPF_REG_0 , 1 ) ,
BPF_EXIT_INSN ( ) ,
} ,
BPF_CGROUP_INET6_POST_BIND ,
BPF_CGROUP_INET6_POST_BIND ,
AF_INET6 ,
SOCK_STREAM ,
" :: " ,
0 ,
SUCCESS ,
} ,
} ;
static size_t probe_prog_length ( const struct bpf_insn * fp )
{
size_t len ;
for ( len = MAX_INSNS - 1 ; len > 0 ; - - len )
if ( fp [ len ] . code ! = 0 | | fp [ len ] . imm ! = 0 )
break ;
return len + 1 ;
}
static int load_sock_prog ( const struct bpf_insn * prog ,
enum bpf_attach_type attach_type )
{
struct bpf_load_program_attr attr ;
tools/bpf: add log_level to bpf_load_program_attr
The kernel verifier has three levels of logs:
0: no logs
1: logs mostly useful
> 1: verbose
Current libbpf API functions bpf_load_program_xattr() and
bpf_load_program() cannot specify log_level.
The bcc, however, provides an interface for user to
specify log_level 2 for verbose output.
This patch added log_level into structure
bpf_load_program_attr, so users, including bcc, can use
bpf_load_program_xattr() to change log_level. The
supported log_level is 0, 1, and 2.
The bpf selftest test_sock.c is modified to enable log_level = 2.
If the "verbose" in test_sock.c is changed to true,
the test will output logs like below:
$ ./test_sock
func#0 @0
0: R1=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
0: (bf) r6 = r1
1: R1=ctx(id=0,off=0,imm=0) R6_w=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
1: (61) r7 = *(u32 *)(r6 +28)
invalid bpf_context access off=28 size=4
Test case: bind4 load with invalid access: src_ip6 .. [PASS]
...
Test case: bind6 allow all .. [PASS]
Summary: 16 PASSED, 0 FAILED
Some test_sock tests are negative tests and verbose verifier
log will be printed out as shown in the above.
Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2019-02-07 09:34:51 -08:00
int ret ;
2018-03-30 15:08:08 -07:00
memset ( & attr , 0 , sizeof ( struct bpf_load_program_attr ) ) ;
attr . prog_type = BPF_PROG_TYPE_CGROUP_SOCK ;
attr . expected_attach_type = attach_type ;
attr . insns = prog ;
attr . insns_cnt = probe_prog_length ( attr . insns ) ;
attr . license = " GPL " ;
tools/bpf: add log_level to bpf_load_program_attr
The kernel verifier has three levels of logs:
0: no logs
1: logs mostly useful
> 1: verbose
Current libbpf API functions bpf_load_program_xattr() and
bpf_load_program() cannot specify log_level.
The bcc, however, provides an interface for user to
specify log_level 2 for verbose output.
This patch added log_level into structure
bpf_load_program_attr, so users, including bcc, can use
bpf_load_program_xattr() to change log_level. The
supported log_level is 0, 1, and 2.
The bpf selftest test_sock.c is modified to enable log_level = 2.
If the "verbose" in test_sock.c is changed to true,
the test will output logs like below:
$ ./test_sock
func#0 @0
0: R1=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
0: (bf) r6 = r1
1: R1=ctx(id=0,off=0,imm=0) R6_w=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
1: (61) r7 = *(u32 *)(r6 +28)
invalid bpf_context access off=28 size=4
Test case: bind4 load with invalid access: src_ip6 .. [PASS]
...
Test case: bind6 allow all .. [PASS]
Summary: 16 PASSED, 0 FAILED
Some test_sock tests are negative tests and verbose verifier
log will be printed out as shown in the above.
Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2019-02-07 09:34:51 -08:00
attr . log_level = 2 ;
2018-03-30 15:08:08 -07:00
tools/bpf: add log_level to bpf_load_program_attr
The kernel verifier has three levels of logs:
0: no logs
1: logs mostly useful
> 1: verbose
Current libbpf API functions bpf_load_program_xattr() and
bpf_load_program() cannot specify log_level.
The bcc, however, provides an interface for user to
specify log_level 2 for verbose output.
This patch added log_level into structure
bpf_load_program_attr, so users, including bcc, can use
bpf_load_program_xattr() to change log_level. The
supported log_level is 0, 1, and 2.
The bpf selftest test_sock.c is modified to enable log_level = 2.
If the "verbose" in test_sock.c is changed to true,
the test will output logs like below:
$ ./test_sock
func#0 @0
0: R1=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
0: (bf) r6 = r1
1: R1=ctx(id=0,off=0,imm=0) R6_w=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
1: (61) r7 = *(u32 *)(r6 +28)
invalid bpf_context access off=28 size=4
Test case: bind4 load with invalid access: src_ip6 .. [PASS]
...
Test case: bind6 allow all .. [PASS]
Summary: 16 PASSED, 0 FAILED
Some test_sock tests are negative tests and verbose verifier
log will be printed out as shown in the above.
Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2019-02-07 09:34:51 -08:00
ret = bpf_load_program_xattr ( & attr , bpf_log_buf , BPF_LOG_BUF_SIZE ) ;
if ( verbose & & ret < 0 )
fprintf ( stderr , " %s \n " , bpf_log_buf ) ;
return ret ;
2018-03-30 15:08:08 -07:00
}
static int attach_sock_prog ( int cgfd , int progfd ,
enum bpf_attach_type attach_type )
{
return bpf_prog_attach ( progfd , cgfd , attach_type , BPF_F_ALLOW_OVERRIDE ) ;
}
static int bind_sock ( int domain , int type , const char * ip , unsigned short port )
{
struct sockaddr_storage addr ;
struct sockaddr_in6 * addr6 ;
struct sockaddr_in * addr4 ;
int sockfd = - 1 ;
socklen_t len ;
int err = 0 ;
sockfd = socket ( domain , type , 0 ) ;
if ( sockfd < 0 )
goto err ;
memset ( & addr , 0 , sizeof ( addr ) ) ;
if ( domain = = AF_INET ) {
len = sizeof ( struct sockaddr_in ) ;
addr4 = ( struct sockaddr_in * ) & addr ;
addr4 - > sin_family = domain ;
addr4 - > sin_port = htons ( port ) ;
if ( inet_pton ( domain , ip , ( void * ) & addr4 - > sin_addr ) ! = 1 )
goto err ;
} else if ( domain = = AF_INET6 ) {
len = sizeof ( struct sockaddr_in6 ) ;
addr6 = ( struct sockaddr_in6 * ) & addr ;
addr6 - > sin6_family = domain ;
addr6 - > sin6_port = htons ( port ) ;
if ( inet_pton ( domain , ip , ( void * ) & addr6 - > sin6_addr ) ! = 1 )
goto err ;
} else {
goto err ;
}
if ( bind ( sockfd , ( const struct sockaddr * ) & addr , len ) = = - 1 )
goto err ;
goto out ;
err :
err = - 1 ;
out :
close ( sockfd ) ;
return err ;
}
static int run_test_case ( int cgfd , const struct sock_test * test )
{
int progfd = - 1 ;
int err = 0 ;
printf ( " Test case: %s .. " , test - > descr ) ;
progfd = load_sock_prog ( test - > insns , test - > expected_attach_type ) ;
if ( progfd < 0 ) {
if ( test - > result = = LOAD_REJECT )
goto out ;
else
goto err ;
}
if ( attach_sock_prog ( cgfd , progfd , test - > attach_type ) = = - 1 ) {
if ( test - > result = = ATTACH_REJECT )
goto out ;
else
goto err ;
}
if ( bind_sock ( test - > domain , test - > type , test - > ip , test - > port ) = = - 1 ) {
/* sys_bind() may fail for different reasons, errno has to be
* checked to confirm that BPF program rejected it .
*/
if ( test - > result = = BIND_REJECT & & errno = = EPERM )
goto out ;
else
goto err ;
}
if ( test - > result ! = SUCCESS )
goto err ;
goto out ;
err :
err = - 1 ;
out :
/* Detaching w/o checking return code: best effort attempt. */
if ( progfd ! = - 1 )
bpf_prog_detach ( cgfd , test - > attach_type ) ;
close ( progfd ) ;
printf ( " [%s] \n " , err ? " FAIL " : " PASS " ) ;
return err ;
}
static int run_tests ( int cgfd )
{
int passes = 0 ;
int fails = 0 ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( tests ) ; + + i ) {
if ( run_test_case ( cgfd , & tests [ i ] ) )
+ + fails ;
else
+ + passes ;
}
printf ( " Summary: %d PASSED, %d FAILED \n " , passes , fails ) ;
return fails ? - 1 : 0 ;
}
int main ( int argc , char * * argv )
{
int cgfd = - 1 ;
int err = 0 ;
if ( setup_cgroup_environment ( ) )
goto err ;
cgfd = create_and_get_cgroup ( CG_PATH ) ;
2019-01-07 09:46:46 -08:00
if ( cgfd < 0 )
2018-03-30 15:08:08 -07:00
goto err ;
if ( join_cgroup ( CG_PATH ) )
goto err ;
if ( run_tests ( cgfd ) )
goto err ;
goto out ;
err :
err = - 1 ;
out :
close ( cgfd ) ;
cleanup_cgroup_environment ( ) ;
return err ;
}