2018-12-13 06:59:26 +03:00
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2017-12-13 18:18:54 +03:00
// Copyright (C) 2017 Facebook
// Author: Roman Gushchin <guro@fb.com>
2018-07-07 00:28:14 +03:00
# define _XOPEN_SOURCE 500
# include <errno.h>
2017-12-13 18:18:54 +03:00
# include <fcntl.h>
2018-07-07 00:28:14 +03:00
# include <ftw.h>
# include <mntent.h>
# include <stdio.h>
2017-12-13 18:18:54 +03:00
# include <stdlib.h>
# include <string.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <unistd.h>
2020-01-20 16:06:46 +03:00
# include <bpf/bpf.h>
2017-12-13 18:18:54 +03:00
# include "main.h"
# define HELP_SPEC_ATTACH_FLAGS \
" ATTACH_FLAGS := { multi | override } "
2018-04-17 20:28:44 +03:00
# define HELP_SPEC_ATTACH_TYPES \
" ATTACH_TYPE := { ingress | egress | sock_create | \n " \
" sock_ops | device | bind4 | bind6 | \n " \
" post_bind4 | post_bind6 | connect4 | \n " \
2020-05-19 01:45:47 +03:00
" connect6 | getpeername4 | getpeername6 | \n " \
" getsockname4 | getsockname6 | sendmsg4 | \n " \
" sendmsg6 | recvmsg4 | recvmsg6 | \n " \
2021-05-25 04:41:39 +03:00
" sysctl | getsockopt | setsockopt | \n " \
" sock_release } "
2017-12-13 18:18:54 +03:00
2019-07-31 00:03:00 +03:00
static unsigned int query_flags ;
2017-12-13 18:18:54 +03:00
static enum bpf_attach_type parse_attach_type ( const char * str )
{
enum bpf_attach_type type ;
for ( type = 0 ; type < __MAX_BPF_ATTACH_TYPE ; type + + ) {
2020-04-29 03:16:11 +03:00
if ( attach_type_name [ type ] & &
is_prefix ( str , attach_type_name [ type ] ) )
2017-12-13 18:18:54 +03:00
return type ;
}
return __MAX_BPF_ATTACH_TYPE ;
}
2020-04-29 03:16:11 +03:00
static int show_bpf_prog ( int id , enum bpf_attach_type attach_type ,
2018-07-07 00:28:14 +03:00
const char * attach_flags_str ,
int level )
2017-12-13 18:18:54 +03:00
{
struct bpf_prog_info info = { } ;
__u32 info_len = sizeof ( info ) ;
int prog_fd ;
prog_fd = bpf_prog_get_fd_by_id ( id ) ;
if ( prog_fd < 0 )
return - 1 ;
if ( bpf_obj_get_info_by_fd ( prog_fd , & info , & info_len ) ) {
close ( prog_fd ) ;
return - 1 ;
}
if ( json_output ) {
jsonw_start_object ( json_wtr ) ;
jsonw_uint_field ( json_wtr , " id " , info . id ) ;
2020-04-29 03:16:11 +03:00
if ( attach_type < ARRAY_SIZE ( attach_type_name ) )
jsonw_string_field ( json_wtr , " attach_type " ,
attach_type_name [ attach_type ] ) ;
else
jsonw_uint_field ( json_wtr , " attach_type " , attach_type ) ;
2017-12-13 18:18:54 +03:00
jsonw_string_field ( json_wtr , " attach_flags " ,
attach_flags_str ) ;
jsonw_string_field ( json_wtr , " name " , info . name ) ;
jsonw_end_object ( json_wtr ) ;
} else {
2020-04-29 03:16:11 +03:00
printf ( " %s%-8u " , level ? " " : " " , info . id ) ;
if ( attach_type < ARRAY_SIZE ( attach_type_name ) )
printf ( " %-15s " , attach_type_name [ attach_type ] ) ;
else
printf ( " type %-10u " , attach_type ) ;
printf ( " %-15s %-15s \n " , attach_flags_str , info . name ) ;
2017-12-13 18:18:54 +03:00
}
close ( prog_fd ) ;
return 0 ;
}
2018-07-07 00:28:14 +03:00
static int count_attached_bpf_progs ( int cgroup_fd , enum bpf_attach_type type )
{
__u32 prog_cnt = 0 ;
int ret ;
2019-07-31 00:03:00 +03:00
ret = bpf_prog_query ( cgroup_fd , type , query_flags , NULL ,
NULL , & prog_cnt ) ;
2018-07-07 00:28:14 +03:00
if ( ret )
return - 1 ;
return prog_cnt ;
}
2019-12-24 04:17:42 +03:00
static int cgroup_has_attached_progs ( int cgroup_fd )
{
enum bpf_attach_type type ;
bool no_prog = true ;
for ( type = 0 ; type < __MAX_BPF_ATTACH_TYPE ; type + + ) {
int count = count_attached_bpf_progs ( cgroup_fd , type ) ;
if ( count < 0 & & errno ! = EINVAL )
return - 1 ;
if ( count > 0 ) {
no_prog = false ;
break ;
}
}
return no_prog ? 0 : 1 ;
}
2018-07-07 00:28:14 +03:00
static int show_attached_bpf_progs ( int cgroup_fd , enum bpf_attach_type type ,
int level )
2017-12-13 18:18:54 +03:00
{
2019-08-14 14:37:24 +03:00
const char * attach_flags_str ;
2017-12-13 18:18:54 +03:00
__u32 prog_ids [ 1024 ] = { 0 } ;
__u32 prog_cnt , iter ;
__u32 attach_flags ;
char buf [ 32 ] ;
int ret ;
prog_cnt = ARRAY_SIZE ( prog_ids ) ;
2019-07-31 00:03:00 +03:00
ret = bpf_prog_query ( cgroup_fd , type , query_flags , & attach_flags ,
prog_ids , & prog_cnt ) ;
2017-12-13 18:18:54 +03:00
if ( ret )
return ret ;
if ( prog_cnt = = 0 )
return 0 ;
switch ( attach_flags ) {
case BPF_F_ALLOW_MULTI :
attach_flags_str = " multi " ;
break ;
case BPF_F_ALLOW_OVERRIDE :
attach_flags_str = " override " ;
break ;
case 0 :
attach_flags_str = " " ;
break ;
default :
snprintf ( buf , sizeof ( buf ) , " unknown(%x) " , attach_flags ) ;
attach_flags_str = buf ;
}
for ( iter = 0 ; iter < prog_cnt ; iter + + )
2020-04-29 03:16:11 +03:00
show_bpf_prog ( prog_ids [ iter ] , type ,
2018-07-07 00:28:14 +03:00
attach_flags_str , level ) ;
2017-12-13 18:18:54 +03:00
return 0 ;
}
2018-01-03 01:48:35 +03:00
static int do_show ( int argc , char * * argv )
2017-12-13 18:18:54 +03:00
{
enum bpf_attach_type type ;
2019-12-24 04:17:42 +03:00
int has_attached_progs ;
2019-07-31 00:03:00 +03:00
const char * path ;
2017-12-13 18:18:54 +03:00
int cgroup_fd ;
int ret = - 1 ;
2019-07-31 00:03:00 +03:00
query_flags = 0 ;
if ( ! REQ_ARGS ( 1 ) )
return - 1 ;
path = GET_ARG ( ) ;
while ( argc ) {
if ( is_prefix ( * argv , " effective " ) ) {
if ( query_flags & BPF_F_QUERY_EFFECTIVE ) {
p_err ( " duplicated argument: %s " , * argv ) ;
return - 1 ;
}
query_flags | = BPF_F_QUERY_EFFECTIVE ;
NEXT_ARG ( ) ;
} else {
p_err ( " expected no more arguments, 'effective', got: '%s'? " ,
* argv ) ;
return - 1 ;
}
2017-12-13 18:18:54 +03:00
}
2019-07-31 00:03:00 +03:00
cgroup_fd = open ( path , O_RDONLY ) ;
2017-12-13 18:18:54 +03:00
if ( cgroup_fd < 0 ) {
2019-07-31 00:03:00 +03:00
p_err ( " can't open cgroup %s " , path ) ;
2017-12-13 18:18:54 +03:00
goto exit ;
}
2019-12-24 04:17:42 +03:00
has_attached_progs = cgroup_has_attached_progs ( cgroup_fd ) ;
if ( has_attached_progs < 0 ) {
p_err ( " can't query bpf programs attached to %s: %s " ,
path , strerror ( errno ) ) ;
goto exit_cgroup ;
} else if ( ! has_attached_progs ) {
ret = 0 ;
goto exit_cgroup ;
}
2017-12-13 18:18:54 +03:00
if ( json_output )
jsonw_start_array ( json_wtr ) ;
else
printf ( " %-8s %-15s %-15s %-15s \n " , " ID " , " AttachType " ,
" AttachFlags " , " Name " ) ;
for ( type = 0 ; type < __MAX_BPF_ATTACH_TYPE ; type + + ) {
/*
* Not all attach types may be supported , so it ' s expected ,
* that some requests will fail .
2018-01-03 01:48:35 +03:00
* If we were able to get the show for at least one
2017-12-13 18:18:54 +03:00
* attach type , let ' s return 0.
*/
2018-07-07 00:28:14 +03:00
if ( show_attached_bpf_progs ( cgroup_fd , type , 0 ) = = 0 )
2017-12-13 18:18:54 +03:00
ret = 0 ;
}
if ( json_output )
jsonw_end_array ( json_wtr ) ;
2019-12-24 04:17:42 +03:00
exit_cgroup :
2017-12-13 18:18:54 +03:00
close ( cgroup_fd ) ;
exit :
return ret ;
}
2018-07-07 00:28:14 +03:00
/*
* To distinguish nftw ( ) errors and do_show_tree_fn ( ) errors
* and avoid duplicating error messages , let ' s return - 2
* from do_show_tree_fn ( ) in case of error .
*/
# define NFTW_ERR -1
# define SHOW_TREE_FN_ERR -2
static int do_show_tree_fn ( const char * fpath , const struct stat * sb ,
int typeflag , struct FTW * ftw )
{
enum bpf_attach_type type ;
2019-12-24 04:17:42 +03:00
int has_attached_progs ;
2018-07-07 00:28:14 +03:00
int cgroup_fd ;
if ( typeflag ! = FTW_D )
return 0 ;
cgroup_fd = open ( fpath , O_RDONLY ) ;
if ( cgroup_fd < 0 ) {
p_err ( " can't open cgroup %s: %s " , fpath , strerror ( errno ) ) ;
return SHOW_TREE_FN_ERR ;
}
2019-12-24 04:17:42 +03:00
has_attached_progs = cgroup_has_attached_progs ( cgroup_fd ) ;
if ( has_attached_progs < 0 ) {
p_err ( " can't query bpf programs attached to %s: %s " ,
fpath , strerror ( errno ) ) ;
close ( cgroup_fd ) ;
return SHOW_TREE_FN_ERR ;
} else if ( ! has_attached_progs ) {
2018-07-07 00:28:14 +03:00
close ( cgroup_fd ) ;
return 0 ;
}
if ( json_output ) {
jsonw_start_object ( json_wtr ) ;
jsonw_string_field ( json_wtr , " cgroup " , fpath ) ;
jsonw_name ( json_wtr , " programs " ) ;
jsonw_start_array ( json_wtr ) ;
} else {
printf ( " %s \n " , fpath ) ;
}
for ( type = 0 ; type < __MAX_BPF_ATTACH_TYPE ; type + + )
show_attached_bpf_progs ( cgroup_fd , type , ftw - > level ) ;
2019-04-12 16:29:35 +03:00
if ( errno = = EINVAL )
/* Last attach type does not support query.
* Do not report an error for this , especially because batch
* mode would stop processing commands .
*/
errno = 0 ;
2018-07-07 00:28:14 +03:00
if ( json_output ) {
jsonw_end_array ( json_wtr ) ;
jsonw_end_object ( json_wtr ) ;
}
close ( cgroup_fd ) ;
return 0 ;
}
static char * find_cgroup_root ( void )
{
struct mntent * mnt ;
FILE * f ;
f = fopen ( " /proc/mounts " , " r " ) ;
if ( f = = NULL )
return NULL ;
while ( ( mnt = getmntent ( f ) ) ) {
if ( strcmp ( mnt - > mnt_type , " cgroup2 " ) = = 0 ) {
fclose ( f ) ;
return strdup ( mnt - > mnt_dir ) ;
}
}
fclose ( f ) ;
return NULL ;
}
static int do_show_tree ( int argc , char * * argv )
{
2019-07-31 00:03:00 +03:00
char * cgroup_root , * cgroup_alloced = NULL ;
2018-07-07 00:28:14 +03:00
int ret ;
2019-07-31 00:03:00 +03:00
query_flags = 0 ;
if ( ! argc ) {
cgroup_alloced = find_cgroup_root ( ) ;
if ( ! cgroup_alloced ) {
2018-07-07 00:28:14 +03:00
p_err ( " cgroup v2 isn't mounted " ) ;
return - 1 ;
}
2019-07-31 00:03:00 +03:00
cgroup_root = cgroup_alloced ;
} else {
cgroup_root = GET_ARG ( ) ;
while ( argc ) {
if ( is_prefix ( * argv , " effective " ) ) {
if ( query_flags & BPF_F_QUERY_EFFECTIVE ) {
p_err ( " duplicated argument: %s " , * argv ) ;
return - 1 ;
}
query_flags | = BPF_F_QUERY_EFFECTIVE ;
NEXT_ARG ( ) ;
} else {
p_err ( " expected no more arguments, 'effective', got: '%s'? " ,
* argv ) ;
return - 1 ;
}
}
2018-07-07 00:28:14 +03:00
}
if ( json_output )
jsonw_start_array ( json_wtr ) ;
else
printf ( " %s \n "
" %-8s %-15s %-15s %-15s \n " ,
" CgroupPath " ,
" ID " , " AttachType " , " AttachFlags " , " Name " ) ;
switch ( nftw ( cgroup_root , do_show_tree_fn , 1024 , FTW_MOUNT ) ) {
case NFTW_ERR :
p_err ( " can't iterate over %s: %s " , cgroup_root ,
strerror ( errno ) ) ;
ret = - 1 ;
break ;
case SHOW_TREE_FN_ERR :
ret = - 1 ;
break ;
default :
ret = 0 ;
}
if ( json_output )
jsonw_end_array ( json_wtr ) ;
2019-07-31 00:03:00 +03:00
free ( cgroup_alloced ) ;
2018-07-07 00:28:14 +03:00
return ret ;
}
2017-12-13 18:18:54 +03:00
static int do_attach ( int argc , char * * argv )
{
enum bpf_attach_type attach_type ;
int cgroup_fd , prog_fd ;
int attach_flags = 0 ;
int ret = - 1 ;
int i ;
if ( argc < 4 ) {
2018-01-03 01:48:37 +03:00
p_err ( " too few parameters for cgroup attach " ) ;
2017-12-13 18:18:54 +03:00
goto exit ;
}
cgroup_fd = open ( argv [ 0 ] , O_RDONLY ) ;
if ( cgroup_fd < 0 ) {
2019-06-25 19:56:31 +03:00
p_err ( " can't open cgroup %s " , argv [ 0 ] ) ;
2017-12-13 18:18:54 +03:00
goto exit ;
}
attach_type = parse_attach_type ( argv [ 1 ] ) ;
if ( attach_type = = __MAX_BPF_ATTACH_TYPE ) {
2018-01-03 01:48:37 +03:00
p_err ( " invalid attach type " ) ;
2017-12-13 18:18:54 +03:00
goto exit_cgroup ;
}
argc - = 2 ;
argv = & argv [ 2 ] ;
prog_fd = prog_parse_fd ( & argc , & argv ) ;
if ( prog_fd < 0 )
goto exit_cgroup ;
for ( i = 0 ; i < argc ; i + + ) {
if ( is_prefix ( argv [ i ] , " multi " ) ) {
attach_flags | = BPF_F_ALLOW_MULTI ;
} else if ( is_prefix ( argv [ i ] , " override " ) ) {
attach_flags | = BPF_F_ALLOW_OVERRIDE ;
} else {
2018-01-03 01:48:37 +03:00
p_err ( " unknown option: %s " , argv [ i ] ) ;
2017-12-13 18:18:54 +03:00
goto exit_cgroup ;
}
}
if ( bpf_prog_attach ( prog_fd , cgroup_fd , attach_type , attach_flags ) ) {
p_err ( " failed to attach program " ) ;
goto exit_prog ;
}
if ( json_output )
jsonw_null ( json_wtr ) ;
ret = 0 ;
exit_prog :
close ( prog_fd ) ;
exit_cgroup :
close ( cgroup_fd ) ;
exit :
return ret ;
}
static int do_detach ( int argc , char * * argv )
{
enum bpf_attach_type attach_type ;
int prog_fd , cgroup_fd ;
int ret = - 1 ;
if ( argc < 4 ) {
2018-01-03 01:48:37 +03:00
p_err ( " too few parameters for cgroup detach " ) ;
2017-12-13 18:18:54 +03:00
goto exit ;
}
cgroup_fd = open ( argv [ 0 ] , O_RDONLY ) ;
if ( cgroup_fd < 0 ) {
2019-06-25 19:56:31 +03:00
p_err ( " can't open cgroup %s " , argv [ 0 ] ) ;
2017-12-13 18:18:54 +03:00
goto exit ;
}
attach_type = parse_attach_type ( argv [ 1 ] ) ;
if ( attach_type = = __MAX_BPF_ATTACH_TYPE ) {
p_err ( " invalid attach type " ) ;
goto exit_cgroup ;
}
argc - = 2 ;
argv = & argv [ 2 ] ;
prog_fd = prog_parse_fd ( & argc , & argv ) ;
if ( prog_fd < 0 )
goto exit_cgroup ;
if ( bpf_prog_detach2 ( prog_fd , cgroup_fd , attach_type ) ) {
p_err ( " failed to detach program " ) ;
goto exit_prog ;
}
if ( json_output )
jsonw_null ( json_wtr ) ;
ret = 0 ;
exit_prog :
close ( prog_fd ) ;
exit_cgroup :
close ( cgroup_fd ) ;
exit :
return ret ;
}
static int do_help ( int argc , char * * argv )
{
if ( json_output ) {
jsonw_null ( json_wtr ) ;
return 0 ;
}
fprintf ( stderr ,
2020-05-23 04:07:51 +03:00
" Usage: %1$s %2$s { show | list } CGROUP [**effective**] \n "
" %1$s %2$s tree [CGROUP_ROOT] [**effective**] \n "
" %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS] \n "
" %1$s %2$s detach CGROUP ATTACH_TYPE PROG \n "
" %1$s %2$s help \n "
2017-12-13 18:18:54 +03:00
" \n "
2018-04-17 20:28:44 +03:00
HELP_SPEC_ATTACH_TYPES " \n "
2017-12-13 18:18:54 +03:00
" " HELP_SPEC_ATTACH_FLAGS " \n "
" " HELP_SPEC_PROGRAM " \n "
" " HELP_SPEC_OPTIONS " \n "
" " ,
2020-05-23 04:07:51 +03:00
bin_name , argv [ - 2 ] ) ;
2017-12-13 18:18:54 +03:00
return 0 ;
}
static const struct cmd cmds [ ] = {
2018-01-03 01:48:36 +03:00
{ " show " , do_show } ,
2018-01-03 01:48:35 +03:00
{ " list " , do_show } ,
2018-07-07 00:28:14 +03:00
{ " tree " , do_show_tree } ,
2017-12-13 18:18:54 +03:00
{ " attach " , do_attach } ,
{ " detach " , do_detach } ,
{ " help " , do_help } ,
{ 0 }
} ;
int do_cgroup ( int argc , char * * argv )
{
return cmd_select ( cmds , argc , argv , do_help ) ;
}