diff options
Diffstat (limited to 'tools/bpf/bpftool')
| -rw-r--r-- | tools/bpf/bpftool/Documentation/Makefile | 35 | ||||
| -rw-r--r-- | tools/bpf/bpftool/Documentation/bpftool-cgroup.rst | 118 | ||||
| -rw-r--r-- | tools/bpf/bpftool/Documentation/bpftool-map.rst | 11 | ||||
| -rw-r--r-- | tools/bpf/bpftool/Documentation/bpftool-prog.rst | 16 | ||||
| -rw-r--r-- | tools/bpf/bpftool/Documentation/bpftool.rst | 12 | ||||
| -rw-r--r-- | tools/bpf/bpftool/Makefile | 90 | ||||
| -rw-r--r-- | tools/bpf/bpftool/bash-completion/bpftool | 78 | ||||
| -rw-r--r-- | tools/bpf/bpftool/cgroup.c | 308 | ||||
| -rw-r--r-- | tools/bpf/bpftool/common.c | 195 | ||||
| -rw-r--r-- | tools/bpf/bpftool/jit_disasm.c | 23 | ||||
| -rw-r--r-- | tools/bpf/bpftool/main.c | 18 | ||||
| -rw-r--r-- | tools/bpf/bpftool/main.h | 9 | ||||
| -rw-r--r-- | tools/bpf/bpftool/map.c | 11 | ||||
| -rw-r--r-- | tools/bpf/bpftool/prog.c | 232 | 
14 files changed, 1032 insertions, 124 deletions
| diff --git a/tools/bpf/bpftool/Documentation/Makefile b/tools/bpf/bpftool/Documentation/Makefile index 37292bb5ce60..a9d47c1558bb 100644 --- a/tools/bpf/bpftool/Documentation/Makefile +++ b/tools/bpf/bpftool/Documentation/Makefile @@ -3,12 +3,16 @@ include ../../../scripts/utilities.mak  INSTALL ?= install  RM ?= rm -f +RMDIR ?= rmdir --ignore-fail-on-non-empty -# Make the path relative to DESTDIR, not prefix -ifndef DESTDIR -prefix ?= /usr/local +ifeq ($(V),1) +  Q = +else +  Q = @  endif -mandir ?= $(prefix)/share/man + +prefix ?= /usr/local +mandir ?= $(prefix)/man  man8dir = $(mandir)/man8  MAN8_RST = $(wildcard *.rst) @@ -19,16 +23,27 @@ DOC_MAN8 = $(addprefix $(OUTPUT),$(_DOC_MAN8))  man: man8  man8: $(DOC_MAN8) +RST2MAN_DEP := $(shell command -v rst2man 2>/dev/null) +  $(OUTPUT)%.8: %.rst -	rst2man $< > $@ +ifndef RST2MAN_DEP +	$(error "rst2man not found, but required to generate man pages") +endif +	$(QUIET_GEN)rst2man $< > $@  clean: -	$(call QUIET_CLEAN, Documentation) $(RM) $(DOC_MAN8) +	$(call QUIET_CLEAN, Documentation) +	$(Q)$(RM) $(DOC_MAN8)  install: man -	$(call QUIET_INSTALL, Documentation-man) \ -		$(INSTALL) -d -m 755 $(DESTDIR)$(man8dir); \ -		$(INSTALL) -m 644 $(DOC_MAN8) $(DESTDIR)$(man8dir); +	$(call QUIET_INSTALL, Documentation-man) +	$(Q)$(INSTALL) -d -m 755 $(DESTDIR)$(man8dir) +	$(Q)$(INSTALL) -m 644 $(DOC_MAN8) $(DESTDIR)$(man8dir) + +uninstall: +	$(call QUIET_UNINST, Documentation-man) +	$(Q)$(RM) $(addprefix $(DESTDIR)$(man8dir)/,$(_DOC_MAN8)) +	$(Q)$(RMDIR) $(DESTDIR)$(man8dir) -.PHONY: man man8 clean install +.PHONY: man man8 clean install uninstall  .DEFAULT_GOAL := man diff --git a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst new file mode 100644 index 000000000000..0e4e923235b6 --- /dev/null +++ b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst @@ -0,0 +1,118 @@ +================ +bpftool-cgroup +================ +------------------------------------------------------------------------------- +tool for inspection and simple manipulation of eBPF progs +------------------------------------------------------------------------------- + +:Manual section: 8 + +SYNOPSIS +======== + +	**bpftool** [*OPTIONS*] **cgroup** *COMMAND* + +	*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } } + +	*COMMANDS* := +	{ **show** | **list** | **attach** | **detach** | **help** } + +MAP COMMANDS +============= + +|	**bpftool** **cgroup { show | list }** *CGROUP* +|	**bpftool** **cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*] +|	**bpftool** **cgroup detach** *CGROUP* *ATTACH_TYPE* *PROG* +|	**bpftool** **cgroup help** +| +|	*PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* } +|	*ATTACH_TYPE* := { **ingress** | **egress** | **sock_create** | **sock_ops** | **device** } +|	*ATTACH_FLAGS* := { **multi** | **override** } + +DESCRIPTION +=========== +	**bpftool cgroup { show | list }** *CGROUP* +		  List all programs attached to the cgroup *CGROUP*. + +		  Output will start with program ID followed by attach type, +		  attach flags and program name. + +	**bpftool cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*] +		  Attach program *PROG* to the cgroup *CGROUP* with attach type +		  *ATTACH_TYPE* and optional *ATTACH_FLAGS*. + +		  *ATTACH_FLAGS* can be one of: **override** if a sub-cgroup installs +		  some bpf program, the program in this cgroup yields to sub-cgroup +		  program; **multi** if a sub-cgroup installs some bpf program, +		  that cgroup program gets run in addition to the program in this +		  cgroup. + +		  Only one program is allowed to be attached to a cgroup with +		  no attach flags or the **override** flag. Attaching another +		  program will release old program and attach the new one. + +		  Multiple programs are allowed to be attached to a cgroup with +		  **multi**. They are executed in FIFO order (those that were +		  attached first, run first). + +		  Non-default *ATTACH_FLAGS* are supported by kernel version 4.14 +		  and later. + +		  *ATTACH_TYPE* can be on of: +		  **ingress** ingress path of the inet socket (since 4.10); +		  **egress** egress path of the inet socket (since 4.10); +		  **sock_create** opening of an inet socket (since 4.10); +		  **sock_ops** various socket operations (since 4.12); +		  **device** device access (since 4.15). + +	**bpftool cgroup detach** *CGROUP* *ATTACH_TYPE* *PROG* +		  Detach *PROG* from the cgroup *CGROUP* and attach type +		  *ATTACH_TYPE*. + +	**bpftool prog help** +		  Print short help message. + +OPTIONS +======= +	-h, --help +		  Print short generic help message (similar to **bpftool help**). + +	-v, --version +		  Print version number (similar to **bpftool version**). + +	-j, --json +		  Generate JSON output. For commands that cannot produce JSON, this +		  option has no effect. + +	-p, --pretty +		  Generate human-readable JSON output. Implies **-j**. + +	-f, --bpffs +		  Show file names of pinned programs. + +EXAMPLES +======== +| +| **# mount -t bpf none /sys/fs/bpf/** +| **# mkdir /sys/fs/cgroup/test.slice** +| **# bpftool prog load ./device_cgroup.o /sys/fs/bpf/prog** +| **# bpftool cgroup attach /sys/fs/cgroup/test.slice/ device id 1 allow_multi** + +**# bpftool cgroup list /sys/fs/cgroup/test.slice/** + +:: + +    ID       AttachType      AttachFlags     Name +    1        device          allow_multi     bpf_prog1 + +| +| **# bpftool cgroup detach /sys/fs/cgroup/test.slice/ device id 1** +| **# bpftool cgroup list /sys/fs/cgroup/test.slice/** + +:: + +    ID       AttachType      AttachFlags     Name + +SEE ALSO +======== +	**bpftool**\ (8), **bpftool-prog**\ (8), **bpftool-map**\ (8) diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst b/tools/bpf/bpftool/Documentation/bpftool-map.rst index 9f51a268eb06..457e868bd32f 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-map.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst @@ -15,13 +15,13 @@ SYNOPSIS  	*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } }  	*COMMANDS* := -	{ **show** | **dump** | **update** | **lookup** | **getnext** | **delete** +	{ **show** | **list** | **dump** | **update** | **lookup** | **getnext** | **delete**  	| **pin** | **help** }  MAP COMMANDS  ============= -|	**bpftool** **map show**   [*MAP*] +|	**bpftool** **map { show | list }**   [*MAP*]  |	**bpftool** **map dump**    *MAP*  |	**bpftool** **map update**  *MAP*  **key** *BYTES*   **value** *VALUE* [*UPDATE_FLAGS*]  |	**bpftool** **map lookup**  *MAP*  **key** *BYTES* @@ -31,12 +31,13 @@ MAP COMMANDS  |	**bpftool** **map help**  |  |	*MAP* := { **id** *MAP_ID* | **pinned** *FILE* } -|	*VALUE* := { *BYTES* | *MAP* | *PROGRAM* } +|	*PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* } +|	*VALUE* := { *BYTES* | *MAP* | *PROG* }  |	*UPDATE_FLAGS* := { **any** | **exist** | **noexist** }  DESCRIPTION  =========== -	**bpftool map show**   [*MAP*] +	**bpftool map { show | list }**   [*MAP*]  		  Show information about loaded maps.  If *MAP* is specified  		  show information only about given map, otherwise list all  		  maps currently loaded on the system. @@ -128,4 +129,4 @@ EXAMPLES  SEE ALSO  ======== -	**bpftool**\ (8), **bpftool-prog**\ (8) +	**bpftool**\ (8), **bpftool-prog**\ (8), **bpftool-cgroup**\ (8) diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index 36e8d1c3c40d..e4ceee7f2dff 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -15,22 +15,23 @@ SYNOPSIS  	*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } }  	*COMMANDS* := -	{ **show** | **dump xlated** | **dump jited** | **pin** | **help** } +	{ **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load** | **help** }  MAP COMMANDS  ============= -|	**bpftool** **prog show** [*PROG*] +|	**bpftool** **prog { show | list }** [*PROG*]  |	**bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes**}]  |	**bpftool** **prog dump jited**  *PROG* [{**file** *FILE* | **opcodes**}]  |	**bpftool** **prog pin** *PROG* *FILE* +|	**bpftool** **prog load** *OBJ* *FILE*  |	**bpftool** **prog help**  |  |	*PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }  DESCRIPTION  =========== -	**bpftool prog show** [*PROG*] +	**bpftool prog { show | list }** [*PROG*]  		  Show information about loaded programs.  If *PROG* is  		  specified show information only about given program, otherwise  		  list all programs currently loaded on the system. @@ -57,6 +58,11 @@ DESCRIPTION  		  Note: *FILE* must be located in *bpffs* mount. +	**bpftool prog load** *OBJ* *FILE* +		  Load bpf program from binary *OBJ* and pin as *FILE*. + +		  Note: *FILE* must be located in *bpffs* mount. +  	**bpftool prog help**  		  Print short help message. @@ -126,8 +132,10 @@ EXAMPLES  |  | **# mount -t bpf none /sys/fs/bpf/**  | **# bpftool prog pin id 10 /sys/fs/bpf/prog** +| **# bpftool prog load ./my_prog.o /sys/fs/bpf/prog2**  | **# ls -l /sys/fs/bpf/**  |   -rw------- 1 root root 0 Jul 22 01:43 prog +|   -rw------- 1 root root 0 Jul 22 01:44 prog2  **# bpftool prog dum jited pinned /sys/fs/bpf/prog opcodes** @@ -147,4 +155,4 @@ EXAMPLES  SEE ALSO  ======== -	**bpftool**\ (8), **bpftool-map**\ (8) +	**bpftool**\ (8), **bpftool-map**\ (8), **bpftool-cgroup**\ (8) diff --git a/tools/bpf/bpftool/Documentation/bpftool.rst b/tools/bpf/bpftool/Documentation/bpftool.rst index 926c03d5a8da..20689a321ffe 100644 --- a/tools/bpf/bpftool/Documentation/bpftool.rst +++ b/tools/bpf/bpftool/Documentation/bpftool.rst @@ -16,17 +16,19 @@ SYNOPSIS  	**bpftool** **version** -	*OBJECT* := { **map** | **program** } +	*OBJECT* := { **map** | **program** | **cgroup** }  	*OPTIONS* := { { **-V** | **--version** } | { **-h** | **--help** }  	| { **-j** | **--json** } [{ **-p** | **--pretty** }] }  	*MAP-COMMANDS* := -	{ **show** | **dump** | **update** | **lookup** | **getnext** | **delete** +	{ **show** | **list** | **dump** | **update** | **lookup** | **getnext** | **delete**  	| **pin** | **help** } -	*PROG-COMMANDS* := { **show** | **dump jited** | **dump xlated** | **pin** -	| **help** } +	*PROG-COMMANDS* := { **show** | **list** | **dump jited** | **dump xlated** | **pin** +	| **load** | **help** } + +	*CGROUP-COMMANDS* := { **show** | **list** | **attach** | **detach** | **help** }  DESCRIPTION  =========== @@ -53,4 +55,4 @@ OPTIONS  SEE ALSO  ======== -	**bpftool-map**\ (8), **bpftool-prog**\ (8) +	**bpftool-map**\ (8), **bpftool-prog**\ (8), **bpftool-cgroup**\ (8) diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index ec3052c0b004..26901ec87361 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -1,25 +1,10 @@  include ../../scripts/Makefile.include -  include ../../scripts/utilities.mak  ifeq ($(srctree),)  srctree := $(patsubst %/,%,$(dir $(CURDIR)))  srctree := $(patsubst %/,%,$(dir $(srctree)))  srctree := $(patsubst %/,%,$(dir $(srctree))) -#$(info Determined 'srctree' to be $(srctree)) -endif - -ifneq ($(objtree),) -#$(info Determined 'objtree' to be $(objtree)) -endif - -ifneq ($(OUTPUT),) -#$(info Determined 'OUTPUT' to be $(OUTPUT)) -# Adding $(OUTPUT) as a directory to look for source files, -# because use generated output files as sources dependency -# for flex/bison parsers. -VPATH += $(OUTPUT) -export VPATH  endif  ifeq ($(V),1) @@ -28,16 +13,18 @@ else    Q = @  endif -BPF_DIR	= $(srctree)/tools/lib/bpf/ +BPF_DIR = $(srctree)/tools/lib/bpf/  ifneq ($(OUTPUT),) -  BPF_PATH=$(OUTPUT) +  BPF_PATH = $(OUTPUT)  else -  BPF_PATH=$(BPF_DIR) +  BPF_PATH = $(BPF_DIR)  endif  LIBBPF = $(BPF_PATH)libbpf.a +BPFTOOL_VERSION=$(shell make --no-print-directory -sC ../../.. kernelversion) +  $(LIBBPF): FORCE  	$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) $(OUTPUT)libbpf.a FEATURES_DUMP=$(FEATURE_DUMP_EXPORT) @@ -45,22 +32,50 @@ $(LIBBPF)-clean:  	$(call QUIET_CLEAN, libbpf)  	$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) clean >/dev/null -prefix = /usr/local +prefix ?= /usr/local  bash_compdir ?= /usr/share/bash-completion/completions  CC = gcc  CFLAGS += -O2  CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow -CFLAGS += -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi -I$(srctree)/tools/include -I$(srctree)/tools/lib/bpf -I$(srctree)/kernel/bpf/ +CFLAGS += -DPACKAGE='"bpftool"' -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi -I$(srctree)/tools/include -I$(srctree)/tools/lib/bpf -I$(srctree)/kernel/bpf/ +CFLAGS += -DBPFTOOL_VERSION='"$(BPFTOOL_VERSION)"'  LIBS = -lelf -lbfd -lopcodes $(LIBBPF) +INSTALL ?= install +RM ?= rm -f + +FEATURE_USER = .bpftool +FEATURE_TESTS = libbfd disassembler-four-args +FEATURE_DISPLAY = libbfd disassembler-four-args + +check_feat := 1 +NON_CHECK_FEAT_TARGETS := clean uninstall doc doc-clean doc-install doc-uninstall +ifdef MAKECMDGOALS +ifeq ($(filter-out $(NON_CHECK_FEAT_TARGETS),$(MAKECMDGOALS)),) +  check_feat := 0 +endif +endif + +ifeq ($(check_feat),1) +ifeq ($(FEATURES_DUMP),) +include $(srctree)/tools/build/Makefile.feature +else +include $(FEATURES_DUMP) +endif +endif + +ifeq ($(feature-disassembler-four-args), 1) +CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE +endif +  include $(wildcard *.d)  all: $(OUTPUT)bpftool -SRCS=$(wildcard *.c) -OBJS=$(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o +SRCS = $(wildcard *.c) +OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o  $(OUTPUT)disasm.o: $(srctree)/kernel/bpf/disasm.c  	$(QUIET_CC)$(COMPILE.c) -MMD -o $@ $< @@ -73,21 +88,34 @@ $(OUTPUT)%.o: %.c  clean: $(LIBBPF)-clean  	$(call QUIET_CLEAN, bpftool) -	$(Q)rm -rf $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d +	$(Q)$(RM) $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d + +install: $(OUTPUT)bpftool +	$(call QUIET_INSTALL, bpftool) +	$(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(prefix)/sbin +	$(Q)$(INSTALL) $(OUTPUT)bpftool $(DESTDIR)$(prefix)/sbin/bpftool +	$(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(bash_compdir) +	$(Q)$(INSTALL) -m 0644 bash-completion/bpftool $(DESTDIR)$(bash_compdir) -install: -	install -m 0755 -d $(prefix)/sbin -	install $(OUTPUT)bpftool $(prefix)/sbin/bpftool -	install -m 0755 -d $(bash_compdir) -	install -m 0644 bash-completion/bpftool $(bash_compdir) +uninstall: +	$(call QUIET_UNINST, bpftool) +	$(Q)$(RM) $(DESTDIR)$(prefix)/sbin/bpftool +	$(Q)$(RM) $(DESTDIR)$(bash_compdir)/bpftool  doc: -	$(Q)$(MAKE) -C Documentation/ +	$(call descend,Documentation) + +doc-clean: +	$(call descend,Documentation,clean)  doc-install: -	$(Q)$(MAKE) -C Documentation/ install +	$(call descend,Documentation,install) + +doc-uninstall: +	$(call descend,Documentation,uninstall)  FORCE: -.PHONY: all clean FORCE install doc doc-install +.PHONY: all FORCE clean install uninstall +.PHONY: doc doc-clean doc-install doc-uninstall  .DEFAULT_GOAL := all diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 7febee05c8e7..08719c54a614 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -52,16 +52,24 @@ _bpftool_once_attr()      done  } -# Takes a list of words in argument; adds them all to COMPREPLY if none of them -# is already present on the command line. Returns no value. -_bpftool_one_of_list() +# Takes a list of words as argument; if any of those words is present on the +# command line, return 0. Otherwise, return 1. +_bpftool_search_list()  {      local w idx      for w in $*; do          for (( idx=3; idx < ${#words[@]}-1; idx++ )); do -            [[ $w == ${words[idx]} ]] && return 1 +            [[ $w == ${words[idx]} ]] && return 0          done      done +    return 1 +} + +# Takes a list of words in argument; adds them all to COMPREPLY if none of them +# is already present on the command line. Returns no value. +_bpftool_one_of_list() +{ +    _bpftool_search_list $* && return 1      COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )  } @@ -197,7 +205,7 @@ _bpftool()              local PROG_TYPE='id pinned tag'              case $command in -                show) +                show|list)                      [[ $prev != "$command" ]] && return 0                      COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )                      return 0 @@ -230,17 +238,21 @@ _bpftool()                      fi                      return 0                      ;; +                load) +                    _filedir +                    return 0 +                    ;;                  *)                      [[ $prev == $object ]] && \ -                        COMPREPLY=( $( compgen -W 'dump help pin show' -- \ -                            "$cur" ) ) +                        COMPREPLY=( $( compgen -W 'dump help pin load \ +                            show list' -- "$cur" ) )                      ;;              esac              ;;          map)              local MAP_TYPE='id pinned'              case $command in -                show|dump) +                show|list|dump)                      case $prev in                          $command)                              COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) ) @@ -343,7 +355,55 @@ _bpftool()                  *)                      [[ $prev == $object ]] && \                          COMPREPLY=( $( compgen -W 'delete dump getnext help \ -                            lookup pin show update' -- "$cur" ) ) +                            lookup pin show list update' -- "$cur" ) ) +                    ;; +            esac +            ;; +        cgroup) +            case $command in +                show|list) +                    _filedir +                    return 0 +                    ;; +                attach|detach) +                    local ATTACH_TYPES='ingress egress sock_create sock_ops \ +                        device' +                    local ATTACH_FLAGS='multi override' +                    local PROG_TYPE='id pinned tag' +                    case $prev in +                        $command) +                            _filedir +                            return 0 +                            ;; +                        ingress|egress|sock_create|sock_ops|device) +                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \ +                                "$cur" ) ) +                            return 0 +                            ;; +                        id) +                            _bpftool_get_prog_ids +                            return 0 +                            ;; +                        *) +                            if ! _bpftool_search_list "$ATTACH_TYPES"; then +                                COMPREPLY=( $( compgen -W "$ATTACH_TYPES" -- \ +                                    "$cur" ) ) +                            elif [[ "$command" == "attach" ]]; then +                                # We have an attach type on the command line, +                                # but it is not the previous word, or +                                # "id|pinned|tag" (we already checked for +                                # that). This should only leave the case when +                                # we need attach flags for "attach" commamnd. +                                _bpftool_one_of_list "$ATTACH_FLAGS" +                            fi +                            return 0 +                            ;; +                    esac +                    ;; +                *) +                    [[ $prev == $object ]] && \ +                        COMPREPLY=( $( compgen -W 'help attach detach \ +                            show list' -- "$cur" ) )                      ;;              esac              ;; diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c new file mode 100644 index 000000000000..cae32a61cb18 --- /dev/null +++ b/tools/bpf/bpftool/cgroup.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (C) 2017 Facebook +// Author: Roman Gushchin <[email protected]> + +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <bpf.h> + +#include "main.h" + +#define HELP_SPEC_ATTACH_FLAGS						\ +	"ATTACH_FLAGS := { multi | override }" + +#define HELP_SPEC_ATTACH_TYPES						\ +	"ATTACH_TYPE := { ingress | egress | sock_create | sock_ops | device }" + +static const char * const attach_type_strings[] = { +	[BPF_CGROUP_INET_INGRESS] = "ingress", +	[BPF_CGROUP_INET_EGRESS] = "egress", +	[BPF_CGROUP_INET_SOCK_CREATE] = "sock_create", +	[BPF_CGROUP_SOCK_OPS] = "sock_ops", +	[BPF_CGROUP_DEVICE] = "device", +	[__MAX_BPF_ATTACH_TYPE] = NULL, +}; + +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++) { +		if (attach_type_strings[type] && +		    is_prefix(str, attach_type_strings[type])) +			return type; +	} + +	return __MAX_BPF_ATTACH_TYPE; +} + +static int show_bpf_prog(int id, const char *attach_type_str, +			 const char *attach_flags_str) +{ +	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); +		jsonw_string_field(json_wtr, "attach_type", +				   attach_type_str); +		jsonw_string_field(json_wtr, "attach_flags", +				   attach_flags_str); +		jsonw_string_field(json_wtr, "name", info.name); +		jsonw_end_object(json_wtr); +	} else { +		printf("%-8u %-15s %-15s %-15s\n", info.id, +		       attach_type_str, +		       attach_flags_str, +		       info.name); +	} + +	close(prog_fd); +	return 0; +} + +static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type) +{ +	__u32 prog_ids[1024] = {0}; +	char *attach_flags_str; +	__u32 prog_cnt, iter; +	__u32 attach_flags; +	char buf[32]; +	int ret; + +	prog_cnt = ARRAY_SIZE(prog_ids); +	ret = bpf_prog_query(cgroup_fd, type, 0, &attach_flags, prog_ids, +			     &prog_cnt); +	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++) +		show_bpf_prog(prog_ids[iter], attach_type_strings[type], +			      attach_flags_str); + +	return 0; +} + +static int do_show(int argc, char **argv) +{ +	enum bpf_attach_type type; +	int cgroup_fd; +	int ret = -1; + +	if (argc < 1) { +		p_err("too few parameters for cgroup show"); +		goto exit; +	} else if (argc > 1) { +		p_err("too many parameters for cgroup show"); +		goto exit; +	} + +	cgroup_fd = open(argv[0], O_RDONLY); +	if (cgroup_fd < 0) { +		p_err("can't open cgroup %s", argv[1]); +		goto exit; +	} + +	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. +		 * If we were able to get the show for at least one +		 * attach type, let's return 0. +		 */ +		if (show_attached_bpf_progs(cgroup_fd, type) == 0) +			ret = 0; +	} + +	if (json_output) +		jsonw_end_array(json_wtr); + +	close(cgroup_fd); +exit: +	return ret; +} + +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) { +		p_err("too few parameters for cgroup attach"); +		goto exit; +	} + +	cgroup_fd = open(argv[0], O_RDONLY); +	if (cgroup_fd < 0) { +		p_err("can't open cgroup %s", argv[1]); +		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; + +	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 { +			p_err("unknown option: %s", argv[i]); +			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) { +		p_err("too few parameters for cgroup detach"); +		goto exit; +	} + +	cgroup_fd = open(argv[0], O_RDONLY); +	if (cgroup_fd < 0) { +		p_err("can't open cgroup %s", argv[1]); +		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, +		"Usage: %s %s { show | list } CGROUP\n" +		"       %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n" +		"       %s %s detach CGROUP ATTACH_TYPE PROG\n" +		"       %s %s help\n" +		"\n" +		"       " HELP_SPEC_ATTACH_TYPES "\n" +		"       " HELP_SPEC_ATTACH_FLAGS "\n" +		"       " HELP_SPEC_PROGRAM "\n" +		"       " HELP_SPEC_OPTIONS "\n" +		"", +		bin_name, argv[-2], bin_name, argv[-2], +		bin_name, argv[-2], bin_name, argv[-2]); + +	return 0; +} + +static const struct cmd cmds[] = { +	{ "show",	do_show }, +	{ "list",	do_show }, +	{ "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); +} diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index 2bd3b280e6dd..0b482c0070e0 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -34,6 +34,7 @@  /* Author: Jakub Kicinski <[email protected]> */  #include <errno.h> +#include <fcntl.h>  #include <fts.h>  #include <libgen.h>  #include <mntent.h> @@ -44,7 +45,9 @@  #include <unistd.h>  #include <linux/limits.h>  #include <linux/magic.h> +#include <net/if.h>  #include <sys/mount.h> +#include <sys/stat.h>  #include <sys/types.h>  #include <sys/vfs.h> @@ -163,13 +166,49 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)  	return fd;  } -int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)) +int do_pin_fd(int fd, const char *name)  {  	char err_str[ERR_MAX_LEN]; -	unsigned int id; -	char *endptr;  	char *file;  	char *dir; +	int err = 0; + +	err = bpf_obj_pin(fd, name); +	if (!err) +		goto out; + +	file = malloc(strlen(name) + 1); +	strcpy(file, name); +	dir = dirname(file); + +	if (errno != EPERM || is_bpffs(dir)) { +		p_err("can't pin the object (%s): %s", name, strerror(errno)); +		goto out_free; +	} + +	/* Attempt to mount bpffs, then retry pinning. */ +	err = mnt_bpffs(dir, err_str, ERR_MAX_LEN); +	if (!err) { +		err = bpf_obj_pin(fd, name); +		if (err) +			p_err("can't pin the object (%s): %s", name, +			      strerror(errno)); +	} else { +		err_str[ERR_MAX_LEN - 1] = '\0'; +		p_err("can't mount BPF file system to pin the object (%s): %s", +		      name, err_str); +	} + +out_free: +	free(file); +out: +	return err; +} + +int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)) +{ +	unsigned int id; +	char *endptr;  	int err;  	int fd; @@ -195,35 +234,8 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))  		return -1;  	} -	err = bpf_obj_pin(fd, *argv); -	if (!err) -		goto out_close; - -	file = malloc(strlen(*argv) + 1); -	strcpy(file, *argv); -	dir = dirname(file); - -	if (errno != EPERM || is_bpffs(dir)) { -		p_err("can't pin the object (%s): %s", *argv, strerror(errno)); -		goto out_free; -	} - -	/* Attempt to mount bpffs, then retry pinning. */ -	err = mnt_bpffs(dir, err_str, ERR_MAX_LEN); -	if (!err) { -		err = bpf_obj_pin(fd, *argv); -		if (err) -			p_err("can't pin the object (%s): %s", *argv, -			      strerror(errno)); -	} else { -		err_str[ERR_MAX_LEN - 1] = '\0'; -		p_err("can't mount BPF file system to pin the object (%s): %s", -		      *argv, err_str); -	} +	err = do_pin_fd(fd, *argv); -out_free: -	free(file); -out_close:  	close(fd);  	return err;  } @@ -403,3 +415,124 @@ void delete_pinned_obj_table(struct pinned_obj_table *tab)  		free(obj);  	}  } + +static char * +ifindex_to_name_ns(__u32 ifindex, __u32 ns_dev, __u32 ns_ino, char *buf) +{ +	struct stat st; +	int err; + +	err = stat("/proc/self/ns/net", &st); +	if (err) { +		p_err("Can't stat /proc/self: %s", strerror(errno)); +		return NULL; +	} + +	if (st.st_dev != ns_dev || st.st_ino != ns_ino) +		return NULL; + +	return if_indextoname(ifindex, buf); +} + +static int read_sysfs_hex_int(char *path) +{ +	char vendor_id_buf[8]; +	int len; +	int fd; + +	fd = open(path, O_RDONLY); +	if (fd < 0) { +		p_err("Can't open %s: %s", path, strerror(errno)); +		return -1; +	} + +	len = read(fd, vendor_id_buf, sizeof(vendor_id_buf)); +	close(fd); +	if (len < 0) { +		p_err("Can't read %s: %s", path, strerror(errno)); +		return -1; +	} +	if (len >= (int)sizeof(vendor_id_buf)) { +		p_err("Value in %s too long", path); +		return -1; +	} + +	vendor_id_buf[len] = 0; + +	return strtol(vendor_id_buf, NULL, 0); +} + +static int read_sysfs_netdev_hex_int(char *devname, const char *entry_name) +{ +	char full_path[64]; + +	snprintf(full_path, sizeof(full_path), "/sys/class/net/%s/device/%s", +		 devname, entry_name); + +	return read_sysfs_hex_int(full_path); +} + +const char *ifindex_to_bfd_name_ns(__u32 ifindex, __u64 ns_dev, __u64 ns_ino) +{ +	char devname[IF_NAMESIZE]; +	int vendor_id; +	int device_id; + +	if (!ifindex_to_name_ns(ifindex, ns_dev, ns_ino, devname)) { +		p_err("Can't get net device name for ifindex %d: %s", ifindex, +		      strerror(errno)); +		return NULL; +	} + +	vendor_id = read_sysfs_netdev_hex_int(devname, "vendor"); +	if (vendor_id < 0) { +		p_err("Can't get device vendor id for %s", devname); +		return NULL; +	} + +	switch (vendor_id) { +	case 0x19ee: +		device_id = read_sysfs_netdev_hex_int(devname, "device"); +		if (device_id != 0x4000 && +		    device_id != 0x6000 && +		    device_id != 0x6003) +			p_info("Unknown NFP device ID, assuming it is NFP-6xxx arch"); +		return "NFP-6xxx"; +	default: +		p_err("Can't get bfd arch name for device vendor id 0x%04x", +		      vendor_id); +		return NULL; +	} +} + +void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode) +{ +	char name[IF_NAMESIZE]; + +	if (!ifindex) +		return; + +	printf(" dev "); +	if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name)) +		printf("%s", name); +	else +		printf("ifindex %u ns_dev %llu ns_ino %llu", +		       ifindex, ns_dev, ns_inode); +} + +void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode) +{ +	char name[IF_NAMESIZE]; + +	if (!ifindex) +		return; + +	jsonw_name(json_wtr, "dev"); +	jsonw_start_object(json_wtr); +	jsonw_uint_field(json_wtr, "ifindex", ifindex); +	jsonw_uint_field(json_wtr, "ns_dev", ns_dev); +	jsonw_uint_field(json_wtr, "ns_inode", ns_inode); +	if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name)) +		jsonw_string_field(json_wtr, "ifname", name); +	jsonw_end_object(json_wtr); +} diff --git a/tools/bpf/bpftool/jit_disasm.c b/tools/bpf/bpftool/jit_disasm.c index 1551d3918d4c..87439320ef70 100644 --- a/tools/bpf/bpftool/jit_disasm.c +++ b/tools/bpf/bpftool/jit_disasm.c @@ -76,7 +76,8 @@ static int fprintf_json(void *out, const char *fmt, ...)  	return 0;  } -void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes) +void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, +		       const char *arch)  {  	disassembler_ftype disassemble;  	struct disassemble_info info; @@ -100,6 +101,19 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)  	else  		init_disassemble_info(&info, stdout,  				      (fprintf_ftype) fprintf); + +	/* Update architecture info for offload. */ +	if (arch) { +		const bfd_arch_info_type *inf = bfd_scan_arch(arch); + +		if (inf) { +			bfdf->arch_info = inf; +		} else { +			p_err("No libfd support for %s", arch); +			return; +		} +	} +  	info.arch = bfd_get_arch(bfdf);  	info.mach = bfd_get_mach(bfdf);  	info.buffer = image; @@ -107,7 +121,14 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)  	disassemble_init_for_target(&info); +#ifdef DISASM_FOUR_ARGS_SIGNATURE +	disassemble = disassembler(info.arch, +				   bfd_big_endian(bfdf), +				   info.mach, +				   bfdf); +#else  	disassemble = disassembler(bfdf); +#endif  	assert(disassemble);  	if (json_output) diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index d294bc8168be..185acfa229b5 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -38,7 +38,6 @@  #include <errno.h>  #include <getopt.h>  #include <linux/bpf.h> -#include <linux/version.h>  #include <stdio.h>  #include <stdlib.h>  #include <string.h> @@ -85,7 +84,7 @@ static int do_help(int argc, char **argv)  		"       %s batch file FILE\n"  		"       %s version\n"  		"\n" -		"       OBJECT := { prog | map }\n" +		"       OBJECT := { prog | map | cgroup }\n"  		"       " HELP_SPEC_OPTIONS "\n"  		"",  		bin_name, bin_name, bin_name); @@ -95,21 +94,13 @@ static int do_help(int argc, char **argv)  static int do_version(int argc, char **argv)  { -	unsigned int version[3]; - -	version[0] = LINUX_VERSION_CODE >> 16; -	version[1] = LINUX_VERSION_CODE >> 8 & 0xf; -	version[2] = LINUX_VERSION_CODE & 0xf; -  	if (json_output) {  		jsonw_start_object(json_wtr);  		jsonw_name(json_wtr, "version"); -		jsonw_printf(json_wtr, "\"%u.%u.%u\"", -			     version[0], version[1], version[2]); +		jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION);  		jsonw_end_object(json_wtr);  	} else { -		printf("%s v%u.%u.%u\n", bin_name, -		       version[0], version[1], version[2]); +		printf("%s v%s\n", bin_name, BPFTOOL_VERSION);  	}  	return 0;  } @@ -173,6 +164,7 @@ static const struct cmd cmds[] = {  	{ "batch",	do_batch },  	{ "prog",	do_prog },  	{ "map",	do_map }, +	{ "cgroup",	do_cgroup },  	{ "version",	do_version },  	{ 0 }  }; @@ -252,7 +244,7 @@ static int do_batch(int argc, char **argv)  	}  	if (errno && errno != ENOENT) { -		perror("reading batch file failed"); +		p_err("reading batch file failed: %s", strerror(errno));  		err = -1;  	} else {  		p_info("processed %d lines", lines); diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index bff330b49791..b8e9584d6246 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -96,6 +96,8 @@ struct pinned_obj {  int build_pinned_obj_table(struct pinned_obj_table *table,  			   enum bpf_obj_type type);  void delete_pinned_obj_table(struct pinned_obj_table *tab); +void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode); +void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode);  struct cmd {  	const char *cmd; @@ -111,13 +113,18 @@ char *get_fdinfo(int fd, const char *key);  int open_obj_pinned(char *path);  int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type);  int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)); +int do_pin_fd(int fd, const char *name);  int do_prog(int argc, char **arg);  int do_map(int argc, char **arg); +int do_cgroup(int argc, char **arg);  int prog_parse_fd(int *argc, char ***argv); -void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes); +void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, +		       const char *arch);  void print_hex_data_json(uint8_t *data, size_t len); +const char *ifindex_to_bfd_name_ns(__u32 ifindex, __u64 ns_dev, __u64 ns_ino); +  #endif diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index a8c3a33dd185..f95fa67bb498 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -66,6 +66,7 @@ static const char * const map_type_name[] = {  	[BPF_MAP_TYPE_HASH_OF_MAPS]	= "hash_of_maps",  	[BPF_MAP_TYPE_DEVMAP]		= "devmap",  	[BPF_MAP_TYPE_SOCKMAP]		= "sockmap", +	[BPF_MAP_TYPE_CPUMAP]		= "cpumap",  };  static unsigned int get_possible_cpus(void) @@ -428,6 +429,9 @@ static int show_map_close_json(int fd, struct bpf_map_info *info)  	jsonw_name(json_wtr, "flags");  	jsonw_printf(json_wtr, "%#x", info->map_flags); + +	print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); +  	jsonw_uint_field(json_wtr, "bytes_key", info->key_size);  	jsonw_uint_field(json_wtr, "bytes_value", info->value_size);  	jsonw_uint_field(json_wtr, "max_entries", info->max_entries); @@ -469,7 +473,9 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info)  	if (*info->name)  		printf("name %s  ", info->name); -	printf("flags 0x%x\n", info->map_flags); +	printf("flags 0x%x", info->map_flags); +	print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino); +	printf("\n");  	printf("\tkey %uB  value %uB  max_entries %u",  	       info->key_size, info->value_size, info->max_entries); @@ -861,7 +867,7 @@ static int do_help(int argc, char **argv)  	}  	fprintf(stderr, -		"Usage: %s %s show   [MAP]\n" +		"Usage: %s %s { show | list }   [MAP]\n"  		"       %s %s dump    MAP\n"  		"       %s %s update  MAP  key BYTES value VALUE [UPDATE_FLAGS]\n"  		"       %s %s lookup  MAP  key BYTES\n" @@ -885,6 +891,7 @@ static int do_help(int argc, char **argv)  static const struct cmd cmds[] = {  	{ "show",	do_show }, +	{ "list",	do_show },  	{ "help",	do_help },  	{ "dump",	do_dump },  	{ "update",	do_update }, diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index dded77345bfb..e549e329be82 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -45,6 +45,7 @@  #include <sys/stat.h>  #include <bpf.h> +#include <libbpf.h>  #include "main.h"  #include "disasm.h" @@ -65,6 +66,7 @@ static const char * const prog_type_name[] = {  	[BPF_PROG_TYPE_LWT_XMIT]	= "lwt_xmit",  	[BPF_PROG_TYPE_SOCK_OPS]	= "sock_ops",  	[BPF_PROG_TYPE_SK_SKB]		= "sk_skb", +	[BPF_PROG_TYPE_CGROUP_DEVICE]	= "cgroup_device",  };  static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) @@ -229,6 +231,8 @@ static void print_prog_json(struct bpf_prog_info *info, int fd)  		     info->tag[0], info->tag[1], info->tag[2], info->tag[3],  		     info->tag[4], info->tag[5], info->tag[6], info->tag[7]); +	print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); +  	if (info->load_time) {  		char buf[32]; @@ -286,6 +290,7 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd)  	printf("tag ");  	fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); +	print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino);  	printf("\n");  	if (info->load_time) { @@ -402,6 +407,88 @@ static int do_show(int argc, char **argv)  	return err;  } +#define SYM_MAX_NAME	256 + +struct kernel_sym { +	unsigned long address; +	char name[SYM_MAX_NAME]; +}; + +struct dump_data { +	unsigned long address_call_base; +	struct kernel_sym *sym_mapping; +	__u32 sym_count; +	char scratch_buff[SYM_MAX_NAME]; +}; + +static int kernel_syms_cmp(const void *sym_a, const void *sym_b) +{ +	return ((struct kernel_sym *)sym_a)->address - +	       ((struct kernel_sym *)sym_b)->address; +} + +static void kernel_syms_load(struct dump_data *dd) +{ +	struct kernel_sym *sym; +	char buff[256]; +	void *tmp, *address; +	FILE *fp; + +	fp = fopen("/proc/kallsyms", "r"); +	if (!fp) +		return; + +	while (!feof(fp)) { +		if (!fgets(buff, sizeof(buff), fp)) +			break; +		tmp = realloc(dd->sym_mapping, +			      (dd->sym_count + 1) * +			      sizeof(*dd->sym_mapping)); +		if (!tmp) { +out: +			free(dd->sym_mapping); +			dd->sym_mapping = NULL; +			fclose(fp); +			return; +		} +		dd->sym_mapping = tmp; +		sym = &dd->sym_mapping[dd->sym_count]; +		if (sscanf(buff, "%p %*c %s", &address, sym->name) != 2) +			continue; +		sym->address = (unsigned long)address; +		if (!strcmp(sym->name, "__bpf_call_base")) { +			dd->address_call_base = sym->address; +			/* sysctl kernel.kptr_restrict was set */ +			if (!sym->address) +				goto out; +		} +		if (sym->address) +			dd->sym_count++; +	} + +	fclose(fp); + +	qsort(dd->sym_mapping, dd->sym_count, +	      sizeof(*dd->sym_mapping), kernel_syms_cmp); +} + +static void kernel_syms_destroy(struct dump_data *dd) +{ +	free(dd->sym_mapping); +} + +static struct kernel_sym *kernel_syms_search(struct dump_data *dd, +					     unsigned long key) +{ +	struct kernel_sym sym = { +		.address = key, +	}; + +	return dd->sym_mapping ? +	       bsearch(&sym, dd->sym_mapping, dd->sym_count, +		       sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL; +} +  static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...)  {  	va_list args; @@ -411,8 +498,71 @@ static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...)  	va_end(args);  } -static void dump_xlated_plain(void *buf, unsigned int len, bool opcodes) +static const char *print_call_pcrel(struct dump_data *dd, +				    struct kernel_sym *sym, +				    unsigned long address, +				    const struct bpf_insn *insn)  { +	if (sym) +		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), +			 "%+d#%s", insn->off, sym->name); +	else +		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), +			 "%+d#0x%lx", insn->off, address); +	return dd->scratch_buff; +} + +static const char *print_call_helper(struct dump_data *dd, +				     struct kernel_sym *sym, +				     unsigned long address) +{ +	if (sym) +		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), +			 "%s", sym->name); +	else +		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), +			 "0x%lx", address); +	return dd->scratch_buff; +} + +static const char *print_call(void *private_data, +			      const struct bpf_insn *insn) +{ +	struct dump_data *dd = private_data; +	unsigned long address = dd->address_call_base + insn->imm; +	struct kernel_sym *sym; + +	sym = kernel_syms_search(dd, address); +	if (insn->src_reg == BPF_PSEUDO_CALL) +		return print_call_pcrel(dd, sym, address, insn); +	else +		return print_call_helper(dd, sym, address); +} + +static const char *print_imm(void *private_data, +			     const struct bpf_insn *insn, +			     __u64 full_imm) +{ +	struct dump_data *dd = private_data; + +	if (insn->src_reg == BPF_PSEUDO_MAP_FD) +		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), +			 "map[id:%u]", insn->imm); +	else +		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), +			 "0x%llx", (unsigned long long)full_imm); +	return dd->scratch_buff; +} + +static void dump_xlated_plain(struct dump_data *dd, void *buf, +			      unsigned int len, bool opcodes) +{ +	const struct bpf_insn_cbs cbs = { +		.cb_print	= print_insn, +		.cb_call	= print_call, +		.cb_imm		= print_imm, +		.private_data	= dd, +	};  	struct bpf_insn *insn = buf;  	bool double_insn = false;  	unsigned int i; @@ -426,7 +576,7 @@ static void dump_xlated_plain(void *buf, unsigned int len, bool opcodes)  		double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);  		printf("% 4d: ", i); -		print_bpf_insn(print_insn, NULL, insn + i, true); +		print_bpf_insn(&cbs, NULL, insn + i, true);  		if (opcodes) {  			printf("       "); @@ -455,8 +605,15 @@ static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...)  	va_end(args);  } -static void dump_xlated_json(void *buf, unsigned int len, bool opcodes) +static void dump_xlated_json(struct dump_data *dd, void *buf, +			     unsigned int len, bool opcodes)  { +	const struct bpf_insn_cbs cbs = { +		.cb_print	= print_insn_json, +		.cb_call	= print_call, +		.cb_imm		= print_imm, +		.private_data	= dd, +	};  	struct bpf_insn *insn = buf;  	bool double_insn = false;  	unsigned int i; @@ -471,7 +628,7 @@ static void dump_xlated_json(void *buf, unsigned int len, bool opcodes)  		jsonw_start_object(json_wtr);  		jsonw_name(json_wtr, "disasm"); -		print_bpf_insn(print_insn_json, NULL, insn + i, true); +		print_bpf_insn(&cbs, NULL, insn + i, true);  		if (opcodes) {  			jsonw_name(json_wtr, "opcodes"); @@ -506,6 +663,7 @@ static void dump_xlated_json(void *buf, unsigned int len, bool opcodes)  static int do_dump(int argc, char **argv)  {  	struct bpf_prog_info info = {}; +	struct dump_data dd = {};  	__u32 len = sizeof(info);  	unsigned int buf_size;  	char *filepath = NULL; @@ -593,6 +751,14 @@ static int do_dump(int argc, char **argv)  		goto err_free;  	} +	if ((member_len == &info.jited_prog_len && +	     info.jited_prog_insns == 0) || +	    (member_len == &info.xlated_prog_len && +	     info.xlated_prog_insns == 0)) { +		p_err("error retrieving insn dump: kernel.kptr_restrict set?"); +		goto err_free; +	} +  	if (filepath) {  		fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);  		if (fd < 0) { @@ -608,18 +774,33 @@ static int do_dump(int argc, char **argv)  			      n < 0 ? strerror(errno) : "short write");  			goto err_free;  		} + +		if (json_output) +			jsonw_null(json_wtr);  	} else { -		if (member_len == &info.jited_prog_len) -			disasm_print_insn(buf, *member_len, opcodes); -		else +		if (member_len == &info.jited_prog_len) { +			const char *name = NULL; + +			if (info.ifindex) { +				name = ifindex_to_bfd_name_ns(info.ifindex, +							      info.netns_dev, +							      info.netns_ino); +				if (!name) +					goto err_free; +			} + +			disasm_print_insn(buf, *member_len, opcodes, name); +		} else { +			kernel_syms_load(&dd);  			if (json_output) -				dump_xlated_json(buf, *member_len, opcodes); +				dump_xlated_json(&dd, buf, *member_len, opcodes);  			else -				dump_xlated_plain(buf, *member_len, opcodes); +				dump_xlated_plain(&dd, buf, *member_len, opcodes); +			kernel_syms_destroy(&dd); +		}  	}  	free(buf); -  	return 0;  err_free: @@ -637,6 +818,30 @@ static int do_pin(int argc, char **argv)  	return err;  } +static int do_load(int argc, char **argv) +{ +	struct bpf_object *obj; +	int prog_fd; + +	if (argc != 2) +		usage(); + +	if (bpf_prog_load(argv[0], BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) { +		p_err("failed to load program"); +		return -1; +	} + +	if (do_pin_fd(prog_fd, argv[1])) { +		p_err("failed to pin program"); +		return -1; +	} + +	if (json_output) +		jsonw_null(json_wtr); + +	return 0; +} +  static int do_help(int argc, char **argv)  {  	if (json_output) { @@ -645,26 +850,29 @@ static int do_help(int argc, char **argv)  	}  	fprintf(stderr, -		"Usage: %s %s show [PROG]\n" +		"Usage: %s %s { show | list } [PROG]\n"  		"       %s %s dump xlated PROG [{ file FILE | opcodes }]\n"  		"       %s %s dump jited  PROG [{ file FILE | opcodes }]\n"  		"       %s %s pin   PROG FILE\n" +		"       %s %s load  OBJ  FILE\n"  		"       %s %s help\n"  		"\n"  		"       " HELP_SPEC_PROGRAM "\n"  		"       " HELP_SPEC_OPTIONS "\n"  		"",  		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], -		bin_name, argv[-2], bin_name, argv[-2]); +		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);  	return 0;  }  static const struct cmd cmds[] = {  	{ "show",	do_show }, +	{ "list",	do_show },  	{ "help",	do_help },  	{ "dump",	do_dump },  	{ "pin",	do_pin }, +	{ "load",	do_load },  	{ 0 }  }; |