mirror of https://gitee.com/openkylin/linux.git
Merge branch 'bpftool'
Jakub Kicinski says: ==================== tools: add bpftool This set adds bpftool to the tools/ directory. The first patch renames tools/net to tools/bpf, the second one adds the new code, while the third adds simple documentation. v4: - rename docs *.txt -> *.rst (Jesper). v3: - address Alexei's comments about output and docs. v2: - report names, map ids, load time, uid; - add docs/man pages; - general cleanups & fixes. ==================== Acked-by: David Ahern <dsahern@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
4b54db1375
|
@ -2725,7 +2725,7 @@ F: net/core/filter.c
|
||||||
F: net/sched/act_bpf.c
|
F: net/sched/act_bpf.c
|
||||||
F: net/sched/cls_bpf.c
|
F: net/sched/cls_bpf.c
|
||||||
F: samples/bpf/
|
F: samples/bpf/
|
||||||
F: tools/net/bpf*
|
F: tools/bpf/
|
||||||
F: tools/testing/selftests/bpf/
|
F: tools/testing/selftests/bpf/
|
||||||
|
|
||||||
BROADCOM B44 10/100 ETHERNET DRIVER
|
BROADCOM B44 10/100 ETHERNET DRIVER
|
||||||
|
@ -9416,7 +9416,6 @@ F: include/uapi/linux/in.h
|
||||||
F: include/uapi/linux/net.h
|
F: include/uapi/linux/net.h
|
||||||
F: include/uapi/linux/netdevice.h
|
F: include/uapi/linux/netdevice.h
|
||||||
F: include/uapi/linux/net_namespace.h
|
F: include/uapi/linux/net_namespace.h
|
||||||
F: tools/net/
|
|
||||||
F: tools/testing/selftests/net/
|
F: tools/testing/selftests/net/
|
||||||
F: lib/random32.c
|
F: lib/random32.c
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ help:
|
||||||
@echo ' kvm_stat - top-like utility for displaying kvm statistics'
|
@echo ' kvm_stat - top-like utility for displaying kvm statistics'
|
||||||
@echo ' leds - LEDs tools'
|
@echo ' leds - LEDs tools'
|
||||||
@echo ' liblockdep - user-space wrapper for kernel locking-validator'
|
@echo ' liblockdep - user-space wrapper for kernel locking-validator'
|
||||||
@echo ' net - misc networking tools'
|
@echo ' bpf - misc BPF tools'
|
||||||
@echo ' perf - Linux performance measurement and analysis tool'
|
@echo ' perf - Linux performance measurement and analysis tool'
|
||||||
@echo ' selftests - various kernel selftests'
|
@echo ' selftests - various kernel selftests'
|
||||||
@echo ' spi - spi tools'
|
@echo ' spi - spi tools'
|
||||||
|
@ -57,7 +57,7 @@ acpi: FORCE
|
||||||
cpupower: FORCE
|
cpupower: FORCE
|
||||||
$(call descend,power/$@)
|
$(call descend,power/$@)
|
||||||
|
|
||||||
cgroup firewire hv guest spi usb virtio vm net iio gpio objtool leds: FORCE
|
cgroup firewire hv guest spi usb virtio vm bpf iio gpio objtool leds: FORCE
|
||||||
$(call descend,$@)
|
$(call descend,$@)
|
||||||
|
|
||||||
liblockdep: FORCE
|
liblockdep: FORCE
|
||||||
|
@ -91,7 +91,7 @@ kvm_stat: FORCE
|
||||||
|
|
||||||
all: acpi cgroup cpupower gpio hv firewire liblockdep \
|
all: acpi cgroup cpupower gpio hv firewire liblockdep \
|
||||||
perf selftests spi turbostat usb \
|
perf selftests spi turbostat usb \
|
||||||
virtio vm net x86_energy_perf_policy \
|
virtio vm bpf x86_energy_perf_policy \
|
||||||
tmon freefall iio objtool kvm_stat
|
tmon freefall iio objtool kvm_stat
|
||||||
|
|
||||||
acpi_install:
|
acpi_install:
|
||||||
|
@ -100,7 +100,7 @@ acpi_install:
|
||||||
cpupower_install:
|
cpupower_install:
|
||||||
$(call descend,power/$(@:_install=),install)
|
$(call descend,power/$(@:_install=),install)
|
||||||
|
|
||||||
cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install net_install objtool_install:
|
cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install:
|
||||||
$(call descend,$(@:_install=),install)
|
$(call descend,$(@:_install=),install)
|
||||||
|
|
||||||
liblockdep_install:
|
liblockdep_install:
|
||||||
|
@ -124,7 +124,7 @@ kvm_stat_install:
|
||||||
install: acpi_install cgroup_install cpupower_install gpio_install \
|
install: acpi_install cgroup_install cpupower_install gpio_install \
|
||||||
hv_install firewire_install iio_install liblockdep_install \
|
hv_install firewire_install iio_install liblockdep_install \
|
||||||
perf_install selftests_install turbostat_install usb_install \
|
perf_install selftests_install turbostat_install usb_install \
|
||||||
virtio_install vm_install net_install x86_energy_perf_policy_install \
|
virtio_install vm_install bpf_install x86_energy_perf_policy_install \
|
||||||
tmon_install freefall_install objtool_install kvm_stat_install
|
tmon_install freefall_install objtool_install kvm_stat_install
|
||||||
|
|
||||||
acpi_clean:
|
acpi_clean:
|
||||||
|
@ -133,7 +133,7 @@ acpi_clean:
|
||||||
cpupower_clean:
|
cpupower_clean:
|
||||||
$(call descend,power/cpupower,clean)
|
$(call descend,power/cpupower,clean)
|
||||||
|
|
||||||
cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean objtool_clean leds_clean:
|
cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean:
|
||||||
$(call descend,$(@:_clean=),clean)
|
$(call descend,$(@:_clean=),clean)
|
||||||
|
|
||||||
liblockdep_clean:
|
liblockdep_clean:
|
||||||
|
@ -169,7 +169,7 @@ build_clean:
|
||||||
|
|
||||||
clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
|
clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
|
||||||
perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
|
perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
|
||||||
vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
|
vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
|
||||||
freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
|
freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
|
||||||
gpio_clean objtool_clean leds_clean
|
gpio_clean objtool_clean leds_clean
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ prefix = /usr
|
||||||
CC = gcc
|
CC = gcc
|
||||||
LEX = flex
|
LEX = flex
|
||||||
YACC = bison
|
YACC = bison
|
||||||
|
MAKE = make
|
||||||
|
|
||||||
CFLAGS += -Wall -O2
|
CFLAGS += -Wall -O2
|
||||||
CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
|
CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
|
||||||
|
@ -13,7 +14,7 @@ CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
|
||||||
%.lex.c: %.l
|
%.lex.c: %.l
|
||||||
$(LEX) -o $@ $<
|
$(LEX) -o $@ $<
|
||||||
|
|
||||||
all : bpf_jit_disasm bpf_dbg bpf_asm
|
all: bpf_jit_disasm bpf_dbg bpf_asm bpftool
|
||||||
|
|
||||||
bpf_jit_disasm : CFLAGS += -DPACKAGE='bpf_jit_disasm'
|
bpf_jit_disasm : CFLAGS += -DPACKAGE='bpf_jit_disasm'
|
||||||
bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl
|
bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl
|
||||||
|
@ -26,10 +27,21 @@ bpf_asm : LDLIBS =
|
||||||
bpf_asm : bpf_asm.o bpf_exp.yacc.o bpf_exp.lex.o
|
bpf_asm : bpf_asm.o bpf_exp.yacc.o bpf_exp.lex.o
|
||||||
bpf_exp.lex.o : bpf_exp.yacc.c
|
bpf_exp.lex.o : bpf_exp.yacc.c
|
||||||
|
|
||||||
clean :
|
clean: bpftool_clean
|
||||||
rm -rf *.o bpf_jit_disasm bpf_dbg bpf_asm bpf_exp.yacc.* bpf_exp.lex.*
|
rm -rf *.o bpf_jit_disasm bpf_dbg bpf_asm bpf_exp.yacc.* bpf_exp.lex.*
|
||||||
|
|
||||||
install :
|
install: bpftool_install
|
||||||
install bpf_jit_disasm $(prefix)/bin/bpf_jit_disasm
|
install bpf_jit_disasm $(prefix)/bin/bpf_jit_disasm
|
||||||
install bpf_dbg $(prefix)/bin/bpf_dbg
|
install bpf_dbg $(prefix)/bin/bpf_dbg
|
||||||
install bpf_asm $(prefix)/bin/bpf_asm
|
install bpf_asm $(prefix)/bin/bpf_asm
|
||||||
|
|
||||||
|
bpftool:
|
||||||
|
$(MAKE) -C bpftool
|
||||||
|
|
||||||
|
bpftool_install:
|
||||||
|
$(MAKE) -C bpftool install
|
||||||
|
|
||||||
|
bpftool_clean:
|
||||||
|
$(MAKE) -C bpftool clean
|
||||||
|
|
||||||
|
.PHONY: bpftool FORCE
|
|
@ -0,0 +1,34 @@
|
||||||
|
include ../../../scripts/Makefile.include
|
||||||
|
include ../../../scripts/utilities.mak
|
||||||
|
|
||||||
|
INSTALL ?= install
|
||||||
|
RM ?= rm -f
|
||||||
|
|
||||||
|
# Make the path relative to DESTDIR, not prefix
|
||||||
|
ifndef DESTDIR
|
||||||
|
prefix?=$(HOME)
|
||||||
|
endif
|
||||||
|
mandir ?= $(prefix)/share/man
|
||||||
|
man8dir = $(mandir)/man8
|
||||||
|
|
||||||
|
MAN8_RST = $(wildcard *.rst)
|
||||||
|
|
||||||
|
_DOC_MAN8 = $(patsubst %.rst,%.8,$(MAN8_RST))
|
||||||
|
DOC_MAN8 = $(addprefix $(OUTPUT),$(_DOC_MAN8))
|
||||||
|
|
||||||
|
man: man8
|
||||||
|
man8: $(DOC_MAN8)
|
||||||
|
|
||||||
|
$(OUTPUT)%.8: %.rst
|
||||||
|
rst2man $< > $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(call QUIET_CLEAN, Documentation) $(RM) $(DOC_MAN8)
|
||||||
|
|
||||||
|
install: man
|
||||||
|
$(call QUIET_INSTALL, Documentation-man) \
|
||||||
|
$(INSTALL) -d -m 755 $(DESTDIR)$(man8dir); \
|
||||||
|
$(INSTALL) -m 644 $(DOC_MAN8) $(DESTDIR)$(man8dir);
|
||||||
|
|
||||||
|
.PHONY: man man8 clean install
|
||||||
|
.DEFAULT_GOAL := man
|
|
@ -0,0 +1,110 @@
|
||||||
|
================
|
||||||
|
bpftool-map
|
||||||
|
================
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
tool for inspection and simple manipulation of eBPF maps
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
:Manual section: 8
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
========
|
||||||
|
|
||||||
|
**bpftool** **map** *COMMAND*
|
||||||
|
|
||||||
|
*COMMANDS* :=
|
||||||
|
{ show | dump | update | lookup | getnext | delete | pin | help }
|
||||||
|
|
||||||
|
MAP COMMANDS
|
||||||
|
=============
|
||||||
|
|
||||||
|
| **bpftool** map show [*MAP*]
|
||||||
|
| **bpftool** map dump *MAP*
|
||||||
|
| **bpftool** map update *MAP* key *BYTES* value *VALUE* [*UPDATE_FLAGS*]
|
||||||
|
| **bpftool** map lookup *MAP* key *BYTES*
|
||||||
|
| **bpftool** map getnext *MAP* [key *BYTES*]
|
||||||
|
| **bpftool** map delete *MAP* key *BYTES*
|
||||||
|
| **bpftool** map pin *MAP* *FILE*
|
||||||
|
| **bpftool** map help
|
||||||
|
|
|
||||||
|
| *MAP* := { id MAP_ID | pinned FILE }
|
||||||
|
| *VALUE* := { BYTES | MAP | PROGRAM }
|
||||||
|
| *UPDATE_FLAGS* := { any | exist | noexist }
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
===========
|
||||||
|
**bpftool map show** [*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.
|
||||||
|
|
||||||
|
Output will start with map ID followed by map type and
|
||||||
|
zero or more named attributes (depending on kernel version).
|
||||||
|
|
||||||
|
**bpftool map dump** *MAP*
|
||||||
|
Dump all entries in a given *MAP*.
|
||||||
|
|
||||||
|
**bpftool map update** *MAP* **key** *BYTES* **value** *VALUE* [*UPDATE_FLAGS*]
|
||||||
|
Update map entry for a given *KEY*.
|
||||||
|
|
||||||
|
*UPDATE_FLAGS* can be one of: **any** update existing entry
|
||||||
|
or add if doesn't exit; **exist** update only if entry already
|
||||||
|
exists; **noexist** update only if entry doesn't exist.
|
||||||
|
|
||||||
|
**bpftool map lookup** *MAP* **key** *BYTES*
|
||||||
|
Lookup **key** in the map.
|
||||||
|
|
||||||
|
**bpftool map getnext** *MAP* [**key** *BYTES*]
|
||||||
|
Get next key. If *key* is not specified, get first key.
|
||||||
|
|
||||||
|
**bpftool map delete** *MAP* **key** *BYTES*
|
||||||
|
Remove entry from the map.
|
||||||
|
|
||||||
|
**bpftool map pin** *MAP* *FILE*
|
||||||
|
Pin map *MAP* as *FILE*.
|
||||||
|
|
||||||
|
Note: *FILE* must be located in *bpffs* mount.
|
||||||
|
|
||||||
|
**bpftool map help**
|
||||||
|
Print short help message.
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
========
|
||||||
|
**# bpftool map show**
|
||||||
|
::
|
||||||
|
|
||||||
|
10: hash name some_map flags 0x0
|
||||||
|
key 4B value 8B max_entries 2048 memlock 167936B
|
||||||
|
|
||||||
|
**# bpftool map update id 10 key 13 00 07 00 value 02 00 00 00 01 02 03 04**
|
||||||
|
|
||||||
|
**# bpftool map lookup id 10 key 0 1 2 3**
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
key: 00 01 02 03 value: 00 01 02 03 04 05 06 07
|
||||||
|
|
||||||
|
|
||||||
|
**# bpftool map dump id 10**
|
||||||
|
::
|
||||||
|
|
||||||
|
key: 00 01 02 03 value: 00 01 02 03 04 05 06 07
|
||||||
|
key: 0d 00 07 00 value: 02 00 00 00 01 02 03 04
|
||||||
|
Found 2 elements
|
||||||
|
|
||||||
|
**# bpftool map getnext id 10 key 0 1 2 3**
|
||||||
|
::
|
||||||
|
|
||||||
|
key:
|
||||||
|
00 01 02 03
|
||||||
|
next key:
|
||||||
|
0d 00 07 00
|
||||||
|
|
||||||
|
|
|
||||||
|
| **# mount -t bpf none /sys/fs/bpf/**
|
||||||
|
| **# bpftool map pin id 10 /sys/fs/bpf/map**
|
||||||
|
| **# bpftool map del pinned /sys/fs/bpf/map key 13 00 07 00**
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
========
|
||||||
|
**bpftool**\ (8), **bpftool-prog**\ (8)
|
|
@ -0,0 +1,79 @@
|
||||||
|
================
|
||||||
|
bpftool-prog
|
||||||
|
================
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
tool for inspection and simple manipulation of eBPF progs
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
:Manual section: 8
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
========
|
||||||
|
|
||||||
|
| **bpftool** prog show [*PROG*]
|
||||||
|
| **bpftool** prog dump xlated *PROG* file *FILE*
|
||||||
|
| **bpftool** prog dump jited *PROG* [file *FILE*] [opcodes]
|
||||||
|
| **bpftool** prog pin *PROG* *FILE*
|
||||||
|
| **bpftool** prog help
|
||||||
|
|
|
||||||
|
| *PROG* := { id *PROG_ID* | pinned *FILE* | tag *PROG_TAG* }
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
===========
|
||||||
|
**bpftool prog show** [*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.
|
||||||
|
|
||||||
|
Output will start with program ID followed by program type and
|
||||||
|
zero or more named attributes (depending on kernel version).
|
||||||
|
|
||||||
|
**bpftool prog dump xlated** *PROG* **file** *FILE*
|
||||||
|
Dump eBPF instructions of the program from the kernel to a
|
||||||
|
file.
|
||||||
|
|
||||||
|
**bpftool prog dump jited** *PROG* [**file** *FILE*] [**opcodes**]
|
||||||
|
Dump jited image (host machine code) of the program.
|
||||||
|
If *FILE* is specified image will be written to a file,
|
||||||
|
otherwise it will be disassembled and printed to stdout.
|
||||||
|
|
||||||
|
**opcodes** controls if raw opcodes will be printed.
|
||||||
|
|
||||||
|
**bpftool prog pin** *PROG* *FILE*
|
||||||
|
Pin program *PROG* as *FILE*.
|
||||||
|
|
||||||
|
Note: *FILE* must be located in *bpffs* mount.
|
||||||
|
|
||||||
|
**bpftool prog help**
|
||||||
|
Print short help message.
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
========
|
||||||
|
**# bpftool prog show**
|
||||||
|
::
|
||||||
|
|
||||||
|
10: xdp name some_prog tag 00:5a:3d:21:23:62:0c:8b
|
||||||
|
loaded_at Sep 29/20:11 uid 0
|
||||||
|
xlated 528B jited 370B memlock 4096B map_ids 10
|
||||||
|
|
||||||
|
|
|
||||||
|
| **# bpftool prog dump xlated id 10 file /tmp/t**
|
||||||
|
| **# ls -l /tmp/t**
|
||||||
|
| -rw------- 1 root root 560 Jul 22 01:42 /tmp/t
|
||||||
|
|
||||||
|
|
|
||||||
|
| **# bpftool prog dum jited pinned /sys/fs/bpf/prog**
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
push %rbp
|
||||||
|
mov %rsp,%rbp
|
||||||
|
sub $0x228,%rsp
|
||||||
|
sub $0x28,%rbp
|
||||||
|
mov %rbx,0x0(%rbp)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
========
|
||||||
|
**bpftool**\ (8), **bpftool-map**\ (8)
|
|
@ -0,0 +1,34 @@
|
||||||
|
================
|
||||||
|
BPFTOOL
|
||||||
|
================
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
tool for inspection and simple manipulation of eBPF programs and maps
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
:Manual section: 8
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
========
|
||||||
|
|
||||||
|
**bpftool** *OBJECT* { *COMMAND* | help }
|
||||||
|
|
||||||
|
**bpftool** batch file *FILE*
|
||||||
|
|
||||||
|
*OBJECT* := { **map** | **program** }
|
||||||
|
|
||||||
|
*MAP-COMMANDS* :=
|
||||||
|
{ show | dump | update | lookup | getnext | delete | pin | help }
|
||||||
|
|
||||||
|
*PROG-COMMANDS* := { show | dump jited | dump xlated | pin | help }
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
===========
|
||||||
|
*bpftool* allows for inspection and simple modification of BPF objects
|
||||||
|
on the system.
|
||||||
|
|
||||||
|
Note that format of the output of all tools is not guaranteed to be
|
||||||
|
stable and should not be depended upon.
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
========
|
||||||
|
**bpftool-map**\ (8), **bpftool-prog**\ (8)
|
|
@ -0,0 +1,86 @@
|
||||||
|
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)
|
||||||
|
Q =
|
||||||
|
else
|
||||||
|
Q = @
|
||||||
|
endif
|
||||||
|
|
||||||
|
BPF_DIR = $(srctree)/tools/lib/bpf/
|
||||||
|
|
||||||
|
ifneq ($(OUTPUT),)
|
||||||
|
BPF_PATH=$(OUTPUT)
|
||||||
|
else
|
||||||
|
BPF_PATH=$(BPF_DIR)
|
||||||
|
endif
|
||||||
|
|
||||||
|
LIBBPF = $(BPF_PATH)libbpf.a
|
||||||
|
|
||||||
|
$(LIBBPF): FORCE
|
||||||
|
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) $(OUTPUT)libbpf.a FEATURES_DUMP=$(FEATURE_DUMP_EXPORT)
|
||||||
|
|
||||||
|
$(LIBBPF)-clean:
|
||||||
|
$(call QUIET_CLEAN, libbpf)
|
||||||
|
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) clean >/dev/null
|
||||||
|
|
||||||
|
prefix = /usr
|
||||||
|
|
||||||
|
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
|
||||||
|
LIBS = -lelf -lbfd -lopcodes $(LIBBPF)
|
||||||
|
|
||||||
|
include $(wildcard *.d)
|
||||||
|
|
||||||
|
all: $(OUTPUT)bpftool
|
||||||
|
|
||||||
|
SRCS=$(wildcard *.c)
|
||||||
|
OBJS=$(patsubst %.c,$(OUTPUT)%.o,$(SRCS))
|
||||||
|
|
||||||
|
$(OUTPUT)bpftool: $(OBJS) $(LIBBPF)
|
||||||
|
$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
|
||||||
|
|
||||||
|
$(OUTPUT)%.o: %.c
|
||||||
|
$(QUIET_CC)$(COMPILE.c) -MMD -o $@ $<
|
||||||
|
|
||||||
|
clean: $(LIBBPF)-clean
|
||||||
|
$(call QUIET_CLEAN, bpftool)
|
||||||
|
$(Q)rm -rf $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d
|
||||||
|
|
||||||
|
install:
|
||||||
|
install $(OUTPUT)bpftool $(prefix)/sbin/bpftool
|
||||||
|
|
||||||
|
doc:
|
||||||
|
$(Q)$(MAKE) -C Documentation/
|
||||||
|
|
||||||
|
doc-install:
|
||||||
|
$(Q)$(MAKE) -C Documentation/ install
|
||||||
|
|
||||||
|
FORCE:
|
||||||
|
|
||||||
|
.PHONY: all clean FORCE
|
||||||
|
.DEFAULT_GOAL := all
|
|
@ -0,0 +1,216 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Netronome Systems, Inc.
|
||||||
|
*
|
||||||
|
* This software is dual licensed under the GNU General License Version 2,
|
||||||
|
* June 1991 as shown in the file COPYING in the top-level directory of this
|
||||||
|
* source tree or the BSD 2-Clause License provided below. You have the
|
||||||
|
* option to license this software under the complete terms of either license.
|
||||||
|
*
|
||||||
|
* The BSD 2-Clause License:
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials
|
||||||
|
* provided with the distribution.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Author: Jakub Kicinski <kubakici@wp.pl> */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
#include <linux/magic.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/vfs.h>
|
||||||
|
|
||||||
|
#include <bpf.h>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
static bool is_bpffs(char *path)
|
||||||
|
{
|
||||||
|
struct statfs st_fs;
|
||||||
|
|
||||||
|
if (statfs(path, &st_fs) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (unsigned long)st_fs.f_type == BPF_FS_MAGIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
|
||||||
|
{
|
||||||
|
enum bpf_obj_type type;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = bpf_obj_get(path);
|
||||||
|
if (fd < 0) {
|
||||||
|
err("bpf obj get (%s): %s\n", path,
|
||||||
|
errno == EACCES && !is_bpffs(dirname(path)) ?
|
||||||
|
"directory not in bpf file system (bpffs)" :
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = get_fd_type(fd);
|
||||||
|
if (type < 0) {
|
||||||
|
close(fd);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
if (type != exp_type) {
|
||||||
|
err("incorrect object type: %s\n", get_fd_type_name(type));
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
|
||||||
|
{
|
||||||
|
unsigned int id;
|
||||||
|
char *endptr;
|
||||||
|
int err;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (!is_prefix(*argv, "id")) {
|
||||||
|
err("expected 'id' got %s\n", *argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
id = strtoul(*argv, &endptr, 0);
|
||||||
|
if (*endptr) {
|
||||||
|
err("can't parse %s as ID\n", *argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
if (argc != 1)
|
||||||
|
usage();
|
||||||
|
|
||||||
|
fd = get_fd_by_id(id);
|
||||||
|
if (fd < 0) {
|
||||||
|
err("can't get prog by id (%u): %s\n", id, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bpf_obj_pin(fd, *argv);
|
||||||
|
close(fd);
|
||||||
|
if (err) {
|
||||||
|
err("can't pin the object (%s): %s\n", *argv,
|
||||||
|
errno == EACCES && !is_bpffs(dirname(*argv)) ?
|
||||||
|
"directory not in bpf file system (bpffs)" :
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *get_fd_type_name(enum bpf_obj_type type)
|
||||||
|
{
|
||||||
|
static const char * const names[] = {
|
||||||
|
[BPF_OBJ_UNKNOWN] = "unknown",
|
||||||
|
[BPF_OBJ_PROG] = "prog",
|
||||||
|
[BPF_OBJ_MAP] = "map",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type < 0 || type >= ARRAY_SIZE(names) || !names[type])
|
||||||
|
return names[BPF_OBJ_UNKNOWN];
|
||||||
|
|
||||||
|
return names[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_fd_type(int fd)
|
||||||
|
{
|
||||||
|
char path[PATH_MAX];
|
||||||
|
char buf[512];
|
||||||
|
ssize_t n;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "/proc/%d/fd/%d", getpid(), fd);
|
||||||
|
|
||||||
|
n = readlink(path, buf, sizeof(buf));
|
||||||
|
if (n < 0) {
|
||||||
|
err("can't read link type: %s\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (n == sizeof(path)) {
|
||||||
|
err("can't read link type: path too long!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strstr(buf, "bpf-map"))
|
||||||
|
return BPF_OBJ_MAP;
|
||||||
|
else if (strstr(buf, "bpf-prog"))
|
||||||
|
return BPF_OBJ_PROG;
|
||||||
|
|
||||||
|
return BPF_OBJ_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *get_fdinfo(int fd, const char *key)
|
||||||
|
{
|
||||||
|
char path[PATH_MAX];
|
||||||
|
char *line = NULL;
|
||||||
|
size_t line_n = 0;
|
||||||
|
ssize_t n;
|
||||||
|
FILE *fdi;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "/proc/%d/fdinfo/%d", getpid(), fd);
|
||||||
|
|
||||||
|
fdi = fopen(path, "r");
|
||||||
|
if (!fdi) {
|
||||||
|
err("can't open fdinfo: %s\n", strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((n = getline(&line, &line_n, fdi))) {
|
||||||
|
char *value;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (!strstr(line, key))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fclose(fdi);
|
||||||
|
|
||||||
|
value = strchr(line, '\t');
|
||||||
|
if (!value || !value[1]) {
|
||||||
|
err("malformed fdinfo!?\n");
|
||||||
|
free(line);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
value++;
|
||||||
|
|
||||||
|
len = strlen(value);
|
||||||
|
memmove(line, value, len);
|
||||||
|
line[len - 1] = '\0';
|
||||||
|
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
err("key '%s' not found in fdinfo\n", key);
|
||||||
|
free(line);
|
||||||
|
fclose(fdi);
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Based on:
|
||||||
|
*
|
||||||
|
* Minimal BPF JIT image disassembler
|
||||||
|
*
|
||||||
|
* Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
|
||||||
|
* debugging or verification purposes.
|
||||||
|
*
|
||||||
|
* Copyright 2013 Daniel Borkmann <daniel@iogearbox.net>
|
||||||
|
* Licensed under the GNU General Public License, version 2.0 (GPLv2)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <bfd.h>
|
||||||
|
#include <dis-asm.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
static void get_exec_path(char *tpath, size_t size)
|
||||||
|
{
|
||||||
|
ssize_t len;
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
snprintf(tpath, size, "/proc/%d/exe", (int) getpid());
|
||||||
|
tpath[size - 1] = 0;
|
||||||
|
|
||||||
|
path = strdup(tpath);
|
||||||
|
assert(path);
|
||||||
|
|
||||||
|
len = readlink(path, tpath, size - 1);
|
||||||
|
assert(len > 0);
|
||||||
|
tpath[len] = 0;
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
|
||||||
|
{
|
||||||
|
disassembler_ftype disassemble;
|
||||||
|
struct disassemble_info info;
|
||||||
|
int count, i, pc = 0;
|
||||||
|
char tpath[256];
|
||||||
|
bfd *bfdf;
|
||||||
|
|
||||||
|
if (!len)
|
||||||
|
return;
|
||||||
|
|
||||||
|
memset(tpath, 0, sizeof(tpath));
|
||||||
|
get_exec_path(tpath, sizeof(tpath));
|
||||||
|
|
||||||
|
bfdf = bfd_openr(tpath, NULL);
|
||||||
|
assert(bfdf);
|
||||||
|
assert(bfd_check_format(bfdf, bfd_object));
|
||||||
|
|
||||||
|
init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf);
|
||||||
|
info.arch = bfd_get_arch(bfdf);
|
||||||
|
info.mach = bfd_get_mach(bfdf);
|
||||||
|
info.buffer = image;
|
||||||
|
info.buffer_length = len;
|
||||||
|
|
||||||
|
disassemble_init_for_target(&info);
|
||||||
|
|
||||||
|
disassemble = disassembler(bfdf);
|
||||||
|
assert(disassemble);
|
||||||
|
|
||||||
|
do {
|
||||||
|
printf("%4x:\t", pc);
|
||||||
|
|
||||||
|
count = disassemble(pc, &info);
|
||||||
|
|
||||||
|
if (opcodes) {
|
||||||
|
printf("\n\t");
|
||||||
|
for (i = 0; i < count; ++i)
|
||||||
|
printf("%02x ", (uint8_t) image[pc + i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
pc += count;
|
||||||
|
} while (count > 0 && pc < len);
|
||||||
|
|
||||||
|
bfd_close(bfdf);
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Netronome Systems, Inc.
|
||||||
|
*
|
||||||
|
* This software is dual licensed under the GNU General License Version 2,
|
||||||
|
* June 1991 as shown in the file COPYING in the top-level directory of this
|
||||||
|
* source tree or the BSD 2-Clause License provided below. You have the
|
||||||
|
* option to license this software under the complete terms of either license.
|
||||||
|
*
|
||||||
|
* The BSD 2-Clause License:
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials
|
||||||
|
* provided with the distribution.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Author: Jakub Kicinski <kubakici@wp.pl> */
|
||||||
|
|
||||||
|
#include <bfd.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <linux/bpf.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <bpf.h>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
const char *bin_name;
|
||||||
|
static int last_argc;
|
||||||
|
static char **last_argv;
|
||||||
|
static int (*last_do_help)(int argc, char **argv);
|
||||||
|
|
||||||
|
void usage(void)
|
||||||
|
{
|
||||||
|
last_do_help(last_argc - 1, last_argv + 1);
|
||||||
|
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_help(int argc, char **argv)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: %s OBJECT { COMMAND | help }\n"
|
||||||
|
" %s batch file FILE\n"
|
||||||
|
"\n"
|
||||||
|
" OBJECT := { prog | map }\n",
|
||||||
|
bin_name, bin_name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmd_select(const struct cmd *cmds, int argc, char **argv,
|
||||||
|
int (*help)(int argc, char **argv))
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
last_argc = argc;
|
||||||
|
last_argv = argv;
|
||||||
|
last_do_help = help;
|
||||||
|
|
||||||
|
if (argc < 1 && cmds[0].func)
|
||||||
|
return cmds[0].func(argc, argv);
|
||||||
|
|
||||||
|
for (i = 0; cmds[i].func; i++)
|
||||||
|
if (is_prefix(*argv, cmds[i].cmd))
|
||||||
|
return cmds[i].func(argc - 1, argv + 1);
|
||||||
|
|
||||||
|
help(argc - 1, argv + 1);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_prefix(const char *pfx, const char *str)
|
||||||
|
{
|
||||||
|
if (!pfx)
|
||||||
|
return false;
|
||||||
|
if (strlen(str) < strlen(pfx))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !memcmp(str, pfx, strlen(pfx));
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_hex(void *arg, unsigned int n, const char *sep)
|
||||||
|
{
|
||||||
|
unsigned char *data = arg;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
const char *pfx = "";
|
||||||
|
|
||||||
|
if (!i)
|
||||||
|
/* nothing */;
|
||||||
|
else if (!(i % 16))
|
||||||
|
printf("\n");
|
||||||
|
else if (!(i % 8))
|
||||||
|
printf(" ");
|
||||||
|
else
|
||||||
|
pfx = sep;
|
||||||
|
|
||||||
|
printf("%s%02hhx", i ? pfx : "", data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_batch(int argc, char **argv);
|
||||||
|
|
||||||
|
static const struct cmd cmds[] = {
|
||||||
|
{ "help", do_help },
|
||||||
|
{ "batch", do_batch },
|
||||||
|
{ "prog", do_prog },
|
||||||
|
{ "map", do_map },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static int do_batch(int argc, char **argv)
|
||||||
|
{
|
||||||
|
unsigned int lines = 0;
|
||||||
|
char *n_argv[4096];
|
||||||
|
char buf[65536];
|
||||||
|
int n_argc;
|
||||||
|
FILE *fp;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (argc < 2) {
|
||||||
|
err("too few parameters for batch\n");
|
||||||
|
return -1;
|
||||||
|
} else if (!is_prefix(*argv, "file")) {
|
||||||
|
err("expected 'file', got: %s\n", *argv);
|
||||||
|
return -1;
|
||||||
|
} else if (argc > 2) {
|
||||||
|
err("too many parameters for batch\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
fp = fopen(*argv, "r");
|
||||||
|
if (!fp) {
|
||||||
|
err("Can't open file (%s): %s\n", *argv, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
|
if (strlen(buf) == sizeof(buf) - 1) {
|
||||||
|
errno = E2BIG;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
n_argc = 0;
|
||||||
|
n_argv[n_argc] = strtok(buf, " \t\n");
|
||||||
|
|
||||||
|
while (n_argv[n_argc]) {
|
||||||
|
n_argc++;
|
||||||
|
if (n_argc == ARRAY_SIZE(n_argv)) {
|
||||||
|
err("line %d has too many arguments, skip\n",
|
||||||
|
lines);
|
||||||
|
n_argc = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
n_argv[n_argc] = strtok(NULL, " \t\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!n_argc)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
err = cmd_select(cmds, n_argc, n_argv, do_help);
|
||||||
|
if (err)
|
||||||
|
goto err_close;
|
||||||
|
|
||||||
|
lines++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errno && errno != ENOENT) {
|
||||||
|
perror("reading batch file failed");
|
||||||
|
err = -1;
|
||||||
|
} else {
|
||||||
|
info("processed %d lines\n", lines);
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
err_close:
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
bin_name = argv[0];
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
bfd_init();
|
||||||
|
|
||||||
|
return cmd_select(cmds, argc, argv, do_help);
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Netronome Systems, Inc.
|
||||||
|
*
|
||||||
|
* This software is dual licensed under the GNU General License Version 2,
|
||||||
|
* June 1991 as shown in the file COPYING in the top-level directory of this
|
||||||
|
* source tree or the BSD 2-Clause License provided below. You have the
|
||||||
|
* option to license this software under the complete terms of either license.
|
||||||
|
*
|
||||||
|
* The BSD 2-Clause License:
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials
|
||||||
|
* provided with the distribution.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Author: Jakub Kicinski <kubakici@wp.pl> */
|
||||||
|
|
||||||
|
#ifndef __BPF_TOOL_H
|
||||||
|
#define __BPF_TOOL_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <linux/bpf.h>
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||||
|
|
||||||
|
#define err(msg...) fprintf(stderr, "Error: " msg)
|
||||||
|
#define warn(msg...) fprintf(stderr, "Warning: " msg)
|
||||||
|
#define info(msg...) fprintf(stderr, msg)
|
||||||
|
|
||||||
|
#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
|
||||||
|
|
||||||
|
#define min(a, b) \
|
||||||
|
({ typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _b : _a; })
|
||||||
|
#define max(a, b) \
|
||||||
|
({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _b : _a; })
|
||||||
|
|
||||||
|
#define NEXT_ARG() ({ argc--; argv++; if (argc < 0) usage(); })
|
||||||
|
#define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); })
|
||||||
|
#define BAD_ARG() ({ err("what is '%s'?\n", *argv); -1; })
|
||||||
|
|
||||||
|
#define BPF_TAG_FMT "%02hhx:%02hhx:%02hhx:%02hhx:" \
|
||||||
|
"%02hhx:%02hhx:%02hhx:%02hhx"
|
||||||
|
|
||||||
|
#define HELP_SPEC_PROGRAM \
|
||||||
|
"PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }"
|
||||||
|
|
||||||
|
enum bpf_obj_type {
|
||||||
|
BPF_OBJ_UNKNOWN,
|
||||||
|
BPF_OBJ_PROG,
|
||||||
|
BPF_OBJ_MAP,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const char *bin_name;
|
||||||
|
|
||||||
|
bool is_prefix(const char *pfx, const char *str);
|
||||||
|
void print_hex(void *arg, unsigned int n, const char *sep);
|
||||||
|
void usage(void) __attribute__((noreturn));
|
||||||
|
|
||||||
|
struct cmd {
|
||||||
|
const char *cmd;
|
||||||
|
int (*func)(int argc, char **argv);
|
||||||
|
};
|
||||||
|
|
||||||
|
int cmd_select(const struct cmd *cmds, int argc, char **argv,
|
||||||
|
int (*help)(int argc, char **argv));
|
||||||
|
|
||||||
|
int get_fd_type(int fd);
|
||||||
|
const char *get_fd_type_name(enum bpf_obj_type type);
|
||||||
|
char *get_fdinfo(int fd, const char *key);
|
||||||
|
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_prog(int argc, char **arg);
|
||||||
|
int do_map(int argc, char **arg);
|
||||||
|
|
||||||
|
int prog_parse_fd(int *argc, char ***argv);
|
||||||
|
|
||||||
|
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,744 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Netronome Systems, Inc.
|
||||||
|
*
|
||||||
|
* This software is dual licensed under the GNU General License Version 2,
|
||||||
|
* June 1991 as shown in the file COPYING in the top-level directory of this
|
||||||
|
* source tree or the BSD 2-Clause License provided below. You have the
|
||||||
|
* option to license this software under the complete terms of either license.
|
||||||
|
*
|
||||||
|
* The BSD 2-Clause License:
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials
|
||||||
|
* provided with the distribution.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Author: Jakub Kicinski <kubakici@wp.pl> */
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <bpf.h>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
static const char * const map_type_name[] = {
|
||||||
|
[BPF_MAP_TYPE_UNSPEC] = "unspec",
|
||||||
|
[BPF_MAP_TYPE_HASH] = "hash",
|
||||||
|
[BPF_MAP_TYPE_ARRAY] = "array",
|
||||||
|
[BPF_MAP_TYPE_PROG_ARRAY] = "prog_array",
|
||||||
|
[BPF_MAP_TYPE_PERF_EVENT_ARRAY] = "perf_event_array",
|
||||||
|
[BPF_MAP_TYPE_PERCPU_HASH] = "percpu_hash",
|
||||||
|
[BPF_MAP_TYPE_PERCPU_ARRAY] = "percpu_array",
|
||||||
|
[BPF_MAP_TYPE_STACK_TRACE] = "stack_trace",
|
||||||
|
[BPF_MAP_TYPE_CGROUP_ARRAY] = "cgroup_array",
|
||||||
|
[BPF_MAP_TYPE_LRU_HASH] = "lru_hash",
|
||||||
|
[BPF_MAP_TYPE_LRU_PERCPU_HASH] = "lru_percpu_hash",
|
||||||
|
[BPF_MAP_TYPE_LPM_TRIE] = "lpm_trie",
|
||||||
|
[BPF_MAP_TYPE_ARRAY_OF_MAPS] = "array_of_maps",
|
||||||
|
[BPF_MAP_TYPE_HASH_OF_MAPS] = "hash_of_maps",
|
||||||
|
[BPF_MAP_TYPE_DEVMAP] = "devmap",
|
||||||
|
[BPF_MAP_TYPE_SOCKMAP] = "sockmap",
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned int get_possible_cpus(void)
|
||||||
|
{
|
||||||
|
static unsigned int result;
|
||||||
|
char buf[128];
|
||||||
|
long int n;
|
||||||
|
char *ptr;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
fd = open("/sys/devices/system/cpu/possible", O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
err("can't open sysfs possible cpus\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
n = read(fd, buf, sizeof(buf));
|
||||||
|
if (n < 2) {
|
||||||
|
err("can't read sysfs possible cpus\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
if (n == sizeof(buf)) {
|
||||||
|
err("read sysfs possible cpus overflow\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = buf;
|
||||||
|
n = 0;
|
||||||
|
while (*ptr && *ptr != '\n') {
|
||||||
|
unsigned int a, b;
|
||||||
|
|
||||||
|
if (sscanf(ptr, "%u-%u", &a, &b) == 2) {
|
||||||
|
n += b - a + 1;
|
||||||
|
|
||||||
|
ptr = strchr(ptr, '-') + 1;
|
||||||
|
} else if (sscanf(ptr, "%u", &a) == 1) {
|
||||||
|
n++;
|
||||||
|
} else {
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (isdigit(*ptr))
|
||||||
|
ptr++;
|
||||||
|
if (*ptr == ',')
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = n;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool map_is_per_cpu(__u32 type)
|
||||||
|
{
|
||||||
|
return type == BPF_MAP_TYPE_PERCPU_HASH ||
|
||||||
|
type == BPF_MAP_TYPE_PERCPU_ARRAY ||
|
||||||
|
type == BPF_MAP_TYPE_LRU_PERCPU_HASH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool map_is_map_of_maps(__u32 type)
|
||||||
|
{
|
||||||
|
return type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
|
||||||
|
type == BPF_MAP_TYPE_HASH_OF_MAPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool map_is_map_of_progs(__u32 type)
|
||||||
|
{
|
||||||
|
return type == BPF_MAP_TYPE_PROG_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *alloc_value(struct bpf_map_info *info)
|
||||||
|
{
|
||||||
|
if (map_is_per_cpu(info->type))
|
||||||
|
return malloc(info->value_size * get_possible_cpus());
|
||||||
|
else
|
||||||
|
return malloc(info->value_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int map_parse_fd(int *argc, char ***argv)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (is_prefix(**argv, "id")) {
|
||||||
|
unsigned int id;
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
NEXT_ARGP();
|
||||||
|
|
||||||
|
id = strtoul(**argv, &endptr, 0);
|
||||||
|
if (*endptr) {
|
||||||
|
err("can't parse %s as ID\n", **argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
NEXT_ARGP();
|
||||||
|
|
||||||
|
fd = bpf_map_get_fd_by_id(id);
|
||||||
|
if (fd < 0)
|
||||||
|
err("get map by id (%u): %s\n", id, strerror(errno));
|
||||||
|
return fd;
|
||||||
|
} else if (is_prefix(**argv, "pinned")) {
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
NEXT_ARGP();
|
||||||
|
|
||||||
|
path = **argv;
|
||||||
|
NEXT_ARGP();
|
||||||
|
|
||||||
|
return open_obj_pinned_any(path, BPF_OBJ_MAP);
|
||||||
|
}
|
||||||
|
|
||||||
|
err("expected 'id' or 'pinned', got: '%s'?\n", **argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = map_parse_fd(argc, argv);
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
err = bpf_obj_get_info_by_fd(fd, info, info_len);
|
||||||
|
if (err) {
|
||||||
|
err("can't get map info: %s\n", strerror(errno));
|
||||||
|
close(fd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_entry(struct bpf_map_info *info, unsigned char *key,
|
||||||
|
unsigned char *value)
|
||||||
|
{
|
||||||
|
if (!map_is_per_cpu(info->type)) {
|
||||||
|
bool single_line, break_names;
|
||||||
|
|
||||||
|
break_names = info->key_size > 16 || info->value_size > 16;
|
||||||
|
single_line = info->key_size + info->value_size <= 24 &&
|
||||||
|
!break_names;
|
||||||
|
|
||||||
|
printf("key:%c", break_names ? '\n' : ' ');
|
||||||
|
print_hex(key, info->key_size, " ");
|
||||||
|
|
||||||
|
printf(single_line ? " " : "\n");
|
||||||
|
|
||||||
|
printf("value:%c", break_names ? '\n' : ' ');
|
||||||
|
print_hex(value, info->value_size, " ");
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
} else {
|
||||||
|
unsigned int i, n;
|
||||||
|
|
||||||
|
n = get_possible_cpus();
|
||||||
|
|
||||||
|
printf("key:\n");
|
||||||
|
print_hex(key, info->key_size, " ");
|
||||||
|
printf("\n");
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
printf("value (CPU %02d):%c",
|
||||||
|
i, info->value_size > 16 ? '\n' : ' ');
|
||||||
|
print_hex(value + i * info->value_size,
|
||||||
|
info->value_size, " ");
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char **parse_bytes(char **argv, const char *name, unsigned char *val,
|
||||||
|
unsigned int n)
|
||||||
|
{
|
||||||
|
unsigned int i = 0;
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
while (i < n && argv[i]) {
|
||||||
|
val[i] = strtoul(argv[i], &endptr, 0);
|
||||||
|
if (*endptr) {
|
||||||
|
err("error parsing byte: %s\n", argv[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i != n) {
|
||||||
|
err("%s expected %d bytes got %d\n", name, n, i);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return argv + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_elem(char **argv, struct bpf_map_info *info,
|
||||||
|
void *key, void *value, __u32 key_size, __u32 value_size,
|
||||||
|
__u32 *flags, __u32 **value_fd)
|
||||||
|
{
|
||||||
|
if (!*argv) {
|
||||||
|
if (!key && !value)
|
||||||
|
return 0;
|
||||||
|
err("did not find %s\n", key ? "key" : "value");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_prefix(*argv, "key")) {
|
||||||
|
if (!key) {
|
||||||
|
if (key_size)
|
||||||
|
err("duplicate key\n");
|
||||||
|
else
|
||||||
|
err("unnecessary key\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
argv = parse_bytes(argv + 1, "key", key, key_size);
|
||||||
|
if (!argv)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return parse_elem(argv, info, NULL, value, key_size, value_size,
|
||||||
|
flags, value_fd);
|
||||||
|
} else if (is_prefix(*argv, "value")) {
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
if (value_size)
|
||||||
|
err("duplicate value\n");
|
||||||
|
else
|
||||||
|
err("unnecessary value\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
argv++;
|
||||||
|
|
||||||
|
if (map_is_map_of_maps(info->type)) {
|
||||||
|
int argc = 2;
|
||||||
|
|
||||||
|
if (value_size != 4) {
|
||||||
|
err("value smaller than 4B for map in map?\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!argv[0] || !argv[1]) {
|
||||||
|
err("not enough value arguments for map in map\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = map_parse_fd(&argc, &argv);
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*value_fd = value;
|
||||||
|
**value_fd = fd;
|
||||||
|
} else if (map_is_map_of_progs(info->type)) {
|
||||||
|
int argc = 2;
|
||||||
|
|
||||||
|
if (value_size != 4) {
|
||||||
|
err("value smaller than 4B for map of progs?\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!argv[0] || !argv[1]) {
|
||||||
|
err("not enough value arguments for map of progs\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = prog_parse_fd(&argc, &argv);
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*value_fd = value;
|
||||||
|
**value_fd = fd;
|
||||||
|
} else {
|
||||||
|
argv = parse_bytes(argv, "value", value, value_size);
|
||||||
|
if (!argv)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse_elem(argv, info, key, NULL, key_size, value_size,
|
||||||
|
flags, NULL);
|
||||||
|
} else if (is_prefix(*argv, "any") || is_prefix(*argv, "noexist") ||
|
||||||
|
is_prefix(*argv, "exist")) {
|
||||||
|
if (!flags) {
|
||||||
|
err("flags specified multiple times: %s\n", *argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_prefix(*argv, "any"))
|
||||||
|
*flags = BPF_ANY;
|
||||||
|
else if (is_prefix(*argv, "noexist"))
|
||||||
|
*flags = BPF_NOEXIST;
|
||||||
|
else if (is_prefix(*argv, "exist"))
|
||||||
|
*flags = BPF_EXIST;
|
||||||
|
|
||||||
|
return parse_elem(argv + 1, info, key, value, key_size,
|
||||||
|
value_size, NULL, value_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
err("expected key or value, got: %s\n", *argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int show_map_close(int fd, struct bpf_map_info *info)
|
||||||
|
{
|
||||||
|
char *memlock;
|
||||||
|
|
||||||
|
memlock = get_fdinfo(fd, "memlock");
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
printf("%u: ", info->id);
|
||||||
|
if (info->type < ARRAY_SIZE(map_type_name))
|
||||||
|
printf("%s ", map_type_name[info->type]);
|
||||||
|
else
|
||||||
|
printf("type %u ", info->type);
|
||||||
|
|
||||||
|
if (*info->name)
|
||||||
|
printf("name %s ", info->name);
|
||||||
|
|
||||||
|
printf("flags 0x%x\n", info->map_flags);
|
||||||
|
printf("\tkey %uB value %uB max_entries %u",
|
||||||
|
info->key_size, info->value_size, info->max_entries);
|
||||||
|
|
||||||
|
if (memlock)
|
||||||
|
printf(" memlock %sB", memlock);
|
||||||
|
free(memlock);
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_show(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct bpf_map_info info = {};
|
||||||
|
__u32 len = sizeof(info);
|
||||||
|
__u32 id = 0;
|
||||||
|
int err;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (argc == 2) {
|
||||||
|
fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return show_map_close(fd, &info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc)
|
||||||
|
return BAD_ARG();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
err = bpf_map_get_next_id(id, &id);
|
||||||
|
if (err) {
|
||||||
|
if (errno == ENOENT)
|
||||||
|
break;
|
||||||
|
err("can't get next map: %s\n", strerror(errno));
|
||||||
|
if (errno == EINVAL)
|
||||||
|
err("kernel too old?\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = bpf_map_get_fd_by_id(id);
|
||||||
|
if (fd < 0) {
|
||||||
|
err("can't get map by id (%u): %s\n",
|
||||||
|
id, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||||
|
if (err) {
|
||||||
|
err("can't get map info: %s\n", strerror(errno));
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
show_map_close(fd, &info);
|
||||||
|
}
|
||||||
|
|
||||||
|
return errno == ENOENT ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_dump(int argc, char **argv)
|
||||||
|
{
|
||||||
|
void *key, *value, *prev_key;
|
||||||
|
unsigned int num_elems = 0;
|
||||||
|
struct bpf_map_info info = {};
|
||||||
|
__u32 len = sizeof(info);
|
||||||
|
int err;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (argc != 2)
|
||||||
|
usage();
|
||||||
|
|
||||||
|
fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (map_is_map_of_maps(info.type) || map_is_map_of_progs(info.type)) {
|
||||||
|
err("Dumping maps of maps and program maps not supported\n");
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
key = malloc(info.key_size);
|
||||||
|
value = alloc_value(&info);
|
||||||
|
if (!key || !value) {
|
||||||
|
err("mem alloc failed\n");
|
||||||
|
err = -1;
|
||||||
|
goto exit_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_key = NULL;
|
||||||
|
while (true) {
|
||||||
|
err = bpf_map_get_next_key(fd, prev_key, key);
|
||||||
|
if (err) {
|
||||||
|
if (errno == ENOENT)
|
||||||
|
err = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bpf_map_lookup_elem(fd, key, value)) {
|
||||||
|
print_entry(&info, key, value);
|
||||||
|
} else {
|
||||||
|
info("can't lookup element with key: ");
|
||||||
|
print_hex(key, info.key_size, " ");
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_key = key;
|
||||||
|
num_elems++;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Found %u element%s\n", num_elems, num_elems != 1 ? "s" : "");
|
||||||
|
|
||||||
|
exit_free:
|
||||||
|
free(key);
|
||||||
|
free(value);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_update(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct bpf_map_info info = {};
|
||||||
|
__u32 len = sizeof(info);
|
||||||
|
__u32 *value_fd = NULL;
|
||||||
|
__u32 flags = BPF_ANY;
|
||||||
|
void *key, *value;
|
||||||
|
int fd, err;
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
usage();
|
||||||
|
|
||||||
|
fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
key = malloc(info.key_size);
|
||||||
|
value = alloc_value(&info);
|
||||||
|
if (!key || !value) {
|
||||||
|
err("mem alloc failed");
|
||||||
|
err = -1;
|
||||||
|
goto exit_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = parse_elem(argv, &info, key, value, info.key_size,
|
||||||
|
info.value_size, &flags, &value_fd);
|
||||||
|
if (err)
|
||||||
|
goto exit_free;
|
||||||
|
|
||||||
|
err = bpf_map_update_elem(fd, key, value, flags);
|
||||||
|
if (err) {
|
||||||
|
err("update failed: %s\n", strerror(errno));
|
||||||
|
goto exit_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit_free:
|
||||||
|
if (value_fd)
|
||||||
|
close(*value_fd);
|
||||||
|
free(key);
|
||||||
|
free(value);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_lookup(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct bpf_map_info info = {};
|
||||||
|
__u32 len = sizeof(info);
|
||||||
|
void *key, *value;
|
||||||
|
int err;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
usage();
|
||||||
|
|
||||||
|
fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
key = malloc(info.key_size);
|
||||||
|
value = alloc_value(&info);
|
||||||
|
if (!key || !value) {
|
||||||
|
err("mem alloc failed");
|
||||||
|
err = -1;
|
||||||
|
goto exit_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL);
|
||||||
|
if (err)
|
||||||
|
goto exit_free;
|
||||||
|
|
||||||
|
err = bpf_map_lookup_elem(fd, key, value);
|
||||||
|
if (!err) {
|
||||||
|
print_entry(&info, key, value);
|
||||||
|
} else if (errno == ENOENT) {
|
||||||
|
printf("key:\n");
|
||||||
|
print_hex(key, info.key_size, " ");
|
||||||
|
printf("\n\nNot found\n");
|
||||||
|
} else {
|
||||||
|
err("lookup failed: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
exit_free:
|
||||||
|
free(key);
|
||||||
|
free(value);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_getnext(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct bpf_map_info info = {};
|
||||||
|
__u32 len = sizeof(info);
|
||||||
|
void *key, *nextkey;
|
||||||
|
int err;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
usage();
|
||||||
|
|
||||||
|
fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
key = malloc(info.key_size);
|
||||||
|
nextkey = malloc(info.key_size);
|
||||||
|
if (!key || !nextkey) {
|
||||||
|
err("mem alloc failed");
|
||||||
|
err = -1;
|
||||||
|
goto exit_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc) {
|
||||||
|
err = parse_elem(argv, &info, key, NULL, info.key_size, 0,
|
||||||
|
NULL, NULL);
|
||||||
|
if (err)
|
||||||
|
goto exit_free;
|
||||||
|
} else {
|
||||||
|
free(key);
|
||||||
|
key = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bpf_map_get_next_key(fd, key, nextkey);
|
||||||
|
if (err) {
|
||||||
|
err("can't get next key: %s\n", strerror(errno));
|
||||||
|
goto exit_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key) {
|
||||||
|
printf("key:\n");
|
||||||
|
print_hex(key, info.key_size, " ");
|
||||||
|
printf("\n");
|
||||||
|
} else {
|
||||||
|
printf("key: None\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("next key:\n");
|
||||||
|
print_hex(nextkey, info.key_size, " ");
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
exit_free:
|
||||||
|
free(nextkey);
|
||||||
|
free(key);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_delete(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct bpf_map_info info = {};
|
||||||
|
__u32 len = sizeof(info);
|
||||||
|
void *key;
|
||||||
|
int err;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
usage();
|
||||||
|
|
||||||
|
fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
key = malloc(info.key_size);
|
||||||
|
if (!key) {
|
||||||
|
err("mem alloc failed");
|
||||||
|
err = -1;
|
||||||
|
goto exit_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL);
|
||||||
|
if (err)
|
||||||
|
goto exit_free;
|
||||||
|
|
||||||
|
err = bpf_map_delete_elem(fd, key);
|
||||||
|
if (err)
|
||||||
|
err("delete failed: %s\n", strerror(errno));
|
||||||
|
|
||||||
|
exit_free:
|
||||||
|
free(key);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_pin(int argc, char **argv)
|
||||||
|
{
|
||||||
|
return do_pin_any(argc, argv, bpf_map_get_fd_by_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_help(int argc, char **argv)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: %s %s show [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"
|
||||||
|
" %s %s getnext MAP [key BYTES]\n"
|
||||||
|
" %s %s delete MAP key BYTES\n"
|
||||||
|
" %s %s pin MAP FILE\n"
|
||||||
|
" %s %s help\n"
|
||||||
|
"\n"
|
||||||
|
" MAP := { id MAP_ID | pinned FILE }\n"
|
||||||
|
" " HELP_SPEC_PROGRAM "\n"
|
||||||
|
" VALUE := { BYTES | MAP | PROG }\n"
|
||||||
|
" UPDATE_FLAGS := { any | exist | noexist }\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 },
|
||||||
|
{ "help", do_help },
|
||||||
|
{ "dump", do_dump },
|
||||||
|
{ "update", do_update },
|
||||||
|
{ "lookup", do_lookup },
|
||||||
|
{ "getnext", do_getnext },
|
||||||
|
{ "delete", do_delete },
|
||||||
|
{ "pin", do_pin },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
int do_map(int argc, char **argv)
|
||||||
|
{
|
||||||
|
return cmd_select(cmds, argc, argv, do_help);
|
||||||
|
}
|
|
@ -0,0 +1,456 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Netronome Systems, Inc.
|
||||||
|
*
|
||||||
|
* This software is dual licensed under the GNU General License Version 2,
|
||||||
|
* June 1991 as shown in the file COPYING in the top-level directory of this
|
||||||
|
* source tree or the BSD 2-Clause License provided below. You have the
|
||||||
|
* option to license this software under the complete terms of either license.
|
||||||
|
*
|
||||||
|
* The BSD 2-Clause License:
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials
|
||||||
|
* provided with the distribution.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Author: Jakub Kicinski <kubakici@wp.pl> */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <bpf.h>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
static const char * const prog_type_name[] = {
|
||||||
|
[BPF_PROG_TYPE_UNSPEC] = "unspec",
|
||||||
|
[BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter",
|
||||||
|
[BPF_PROG_TYPE_KPROBE] = "kprobe",
|
||||||
|
[BPF_PROG_TYPE_SCHED_CLS] = "sched_cls",
|
||||||
|
[BPF_PROG_TYPE_SCHED_ACT] = "sched_act",
|
||||||
|
[BPF_PROG_TYPE_TRACEPOINT] = "tracepoint",
|
||||||
|
[BPF_PROG_TYPE_XDP] = "xdp",
|
||||||
|
[BPF_PROG_TYPE_PERF_EVENT] = "perf_event",
|
||||||
|
[BPF_PROG_TYPE_CGROUP_SKB] = "cgroup_skb",
|
||||||
|
[BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup_sock",
|
||||||
|
[BPF_PROG_TYPE_LWT_IN] = "lwt_in",
|
||||||
|
[BPF_PROG_TYPE_LWT_OUT] = "lwt_out",
|
||||||
|
[BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit",
|
||||||
|
[BPF_PROG_TYPE_SOCK_OPS] = "sock_ops",
|
||||||
|
[BPF_PROG_TYPE_SK_SKB] = "sk_skb",
|
||||||
|
};
|
||||||
|
|
||||||
|
static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
|
||||||
|
{
|
||||||
|
struct timespec real_time_ts, boot_time_ts;
|
||||||
|
time_t wallclock_secs;
|
||||||
|
struct tm load_tm;
|
||||||
|
|
||||||
|
buf[--size] = '\0';
|
||||||
|
|
||||||
|
if (clock_gettime(CLOCK_REALTIME, &real_time_ts) ||
|
||||||
|
clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) {
|
||||||
|
perror("Can't read clocks");
|
||||||
|
snprintf(buf, size, "%llu", nsecs / 1000000000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) +
|
||||||
|
nsecs / 1000000000;
|
||||||
|
|
||||||
|
if (!localtime_r(&wallclock_secs, &load_tm)) {
|
||||||
|
snprintf(buf, size, "%llu", nsecs / 1000000000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
strftime(buf, size, "%b %d/%H:%M", &load_tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int prog_fd_by_tag(unsigned char *tag)
|
||||||
|
{
|
||||||
|
struct bpf_prog_info info = {};
|
||||||
|
__u32 len = sizeof(info);
|
||||||
|
unsigned int id = 0;
|
||||||
|
int err;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
err = bpf_prog_get_next_id(id, &id);
|
||||||
|
if (err) {
|
||||||
|
err("%s\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = bpf_prog_get_fd_by_id(id);
|
||||||
|
if (fd < 0) {
|
||||||
|
err("can't get prog by id (%u): %s\n",
|
||||||
|
id, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||||
|
if (err) {
|
||||||
|
err("can't get prog info (%u): %s\n",
|
||||||
|
id, strerror(errno));
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!memcmp(tag, info.tag, BPF_TAG_SIZE))
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int prog_parse_fd(int *argc, char ***argv)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (is_prefix(**argv, "id")) {
|
||||||
|
unsigned int id;
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
NEXT_ARGP();
|
||||||
|
|
||||||
|
id = strtoul(**argv, &endptr, 0);
|
||||||
|
if (*endptr) {
|
||||||
|
err("can't parse %s as ID\n", **argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
NEXT_ARGP();
|
||||||
|
|
||||||
|
fd = bpf_prog_get_fd_by_id(id);
|
||||||
|
if (fd < 0)
|
||||||
|
err("get by id (%u): %s\n", id, strerror(errno));
|
||||||
|
return fd;
|
||||||
|
} else if (is_prefix(**argv, "tag")) {
|
||||||
|
unsigned char tag[BPF_TAG_SIZE];
|
||||||
|
|
||||||
|
NEXT_ARGP();
|
||||||
|
|
||||||
|
if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2,
|
||||||
|
tag + 3, tag + 4, tag + 5, tag + 6, tag + 7)
|
||||||
|
!= BPF_TAG_SIZE) {
|
||||||
|
err("can't parse tag\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
NEXT_ARGP();
|
||||||
|
|
||||||
|
return prog_fd_by_tag(tag);
|
||||||
|
} else if (is_prefix(**argv, "pinned")) {
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
NEXT_ARGP();
|
||||||
|
|
||||||
|
path = **argv;
|
||||||
|
NEXT_ARGP();
|
||||||
|
|
||||||
|
return open_obj_pinned_any(path, BPF_OBJ_PROG);
|
||||||
|
}
|
||||||
|
|
||||||
|
err("expected 'id', 'tag' or 'pinned', got: '%s'?\n", **argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_prog_maps(int fd, u32 num_maps)
|
||||||
|
{
|
||||||
|
struct bpf_prog_info info = {};
|
||||||
|
__u32 len = sizeof(info);
|
||||||
|
__u32 map_ids[num_maps];
|
||||||
|
unsigned int i;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
info.nr_map_ids = num_maps;
|
||||||
|
info.map_ids = ptr_to_u64(map_ids);
|
||||||
|
|
||||||
|
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||||
|
if (err || !info.nr_map_ids)
|
||||||
|
return;
|
||||||
|
|
||||||
|
printf(" map_ids ");
|
||||||
|
for (i = 0; i < info.nr_map_ids; i++)
|
||||||
|
printf("%u%s", map_ids[i],
|
||||||
|
i == info.nr_map_ids - 1 ? "" : ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int show_prog(int fd)
|
||||||
|
{
|
||||||
|
struct bpf_prog_info info = {};
|
||||||
|
__u32 len = sizeof(info);
|
||||||
|
char *memlock;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||||
|
if (err) {
|
||||||
|
err("can't get prog info: %s\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%u: ", info.id);
|
||||||
|
if (info.type < ARRAY_SIZE(prog_type_name))
|
||||||
|
printf("%s ", prog_type_name[info.type]);
|
||||||
|
else
|
||||||
|
printf("type %u ", info.type);
|
||||||
|
|
||||||
|
if (*info.name)
|
||||||
|
printf("name %s ", info.name);
|
||||||
|
|
||||||
|
printf("tag ");
|
||||||
|
print_hex(info.tag, BPF_TAG_SIZE, ":");
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
if (info.load_time) {
|
||||||
|
char buf[32];
|
||||||
|
|
||||||
|
print_boot_time(info.load_time, buf, sizeof(buf));
|
||||||
|
|
||||||
|
/* Piggy back on load_time, since 0 uid is a valid one */
|
||||||
|
printf("\tloaded_at %s uid %u\n", buf, info.created_by_uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\txlated %uB", info.xlated_prog_len);
|
||||||
|
|
||||||
|
if (info.jited_prog_len)
|
||||||
|
printf(" jited %uB", info.jited_prog_len);
|
||||||
|
else
|
||||||
|
printf(" not jited");
|
||||||
|
|
||||||
|
memlock = get_fdinfo(fd, "memlock");
|
||||||
|
if (memlock)
|
||||||
|
printf(" memlock %sB", memlock);
|
||||||
|
free(memlock);
|
||||||
|
|
||||||
|
if (info.nr_map_ids)
|
||||||
|
show_prog_maps(fd, info.nr_map_ids);
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_show(int argc, char **argv)
|
||||||
|
{ __u32 id = 0;
|
||||||
|
int err;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (argc == 2) {
|
||||||
|
fd = prog_parse_fd(&argc, &argv);
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return show_prog(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc)
|
||||||
|
return BAD_ARG();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
err = bpf_prog_get_next_id(id, &id);
|
||||||
|
if (err) {
|
||||||
|
if (errno == ENOENT)
|
||||||
|
break;
|
||||||
|
err("can't get next program: %s\n", strerror(errno));
|
||||||
|
if (errno == EINVAL)
|
||||||
|
err("kernel too old?\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = bpf_prog_get_fd_by_id(id);
|
||||||
|
if (fd < 0) {
|
||||||
|
err("can't get prog by id (%u): %s\n",
|
||||||
|
id, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = show_prog(fd);
|
||||||
|
close(fd);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_dump(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct bpf_prog_info info = {};
|
||||||
|
__u32 len = sizeof(info);
|
||||||
|
bool can_disasm = false;
|
||||||
|
unsigned int buf_size;
|
||||||
|
char *filepath = NULL;
|
||||||
|
bool opcodes = false;
|
||||||
|
unsigned char *buf;
|
||||||
|
__u32 *member_len;
|
||||||
|
__u64 *member_ptr;
|
||||||
|
ssize_t n;
|
||||||
|
int err;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (is_prefix(*argv, "jited")) {
|
||||||
|
member_len = &info.jited_prog_len;
|
||||||
|
member_ptr = &info.jited_prog_insns;
|
||||||
|
can_disasm = true;
|
||||||
|
} else if (is_prefix(*argv, "xlated")) {
|
||||||
|
member_len = &info.xlated_prog_len;
|
||||||
|
member_ptr = &info.xlated_prog_insns;
|
||||||
|
} else {
|
||||||
|
err("expected 'xlated' or 'jited', got: %s\n", *argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
usage();
|
||||||
|
|
||||||
|
fd = prog_parse_fd(&argc, &argv);
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (is_prefix(*argv, "file")) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (!argc) {
|
||||||
|
err("expected file path\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
filepath = *argv;
|
||||||
|
NEXT_ARG();
|
||||||
|
} else if (is_prefix(*argv, "opcodes")) {
|
||||||
|
opcodes = true;
|
||||||
|
NEXT_ARG();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filepath && !can_disasm) {
|
||||||
|
err("expected 'file' got %s\n", *argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (argc) {
|
||||||
|
usage();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||||
|
if (err) {
|
||||||
|
err("can't get prog info: %s\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*member_len) {
|
||||||
|
info("no instructions returned\n");
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_size = *member_len;
|
||||||
|
|
||||||
|
buf = malloc(buf_size);
|
||||||
|
if (!buf) {
|
||||||
|
err("mem alloc failed\n");
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
|
||||||
|
*member_ptr = ptr_to_u64(buf);
|
||||||
|
*member_len = buf_size;
|
||||||
|
|
||||||
|
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||||
|
close(fd);
|
||||||
|
if (err) {
|
||||||
|
err("can't get prog info: %s\n", strerror(errno));
|
||||||
|
goto err_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*member_len > buf_size) {
|
||||||
|
info("too many instructions returned\n");
|
||||||
|
goto err_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filepath) {
|
||||||
|
fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||||
|
if (fd < 0) {
|
||||||
|
err("can't open file %s: %s\n", filepath,
|
||||||
|
strerror(errno));
|
||||||
|
goto err_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = write(fd, buf, *member_len);
|
||||||
|
close(fd);
|
||||||
|
if (n != *member_len) {
|
||||||
|
err("error writing output file: %s\n",
|
||||||
|
n < 0 ? strerror(errno) : "short write");
|
||||||
|
goto err_free;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
disasm_print_insn(buf, *member_len, opcodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_free:
|
||||||
|
free(buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_pin(int argc, char **argv)
|
||||||
|
{
|
||||||
|
return do_pin_any(argc, argv, bpf_prog_get_fd_by_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_help(int argc, char **argv)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: %s %s show [PROG]\n"
|
||||||
|
" %s %s dump xlated PROG file FILE\n"
|
||||||
|
" %s %s dump jited PROG [file FILE] [opcodes]\n"
|
||||||
|
" %s %s pin PROG FILE\n"
|
||||||
|
" %s %s help\n"
|
||||||
|
"\n"
|
||||||
|
" " HELP_SPEC_PROGRAM "\n"
|
||||||
|
"",
|
||||||
|
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 },
|
||||||
|
{ "dump", do_dump },
|
||||||
|
{ "pin", do_pin },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
int do_prog(int argc, char **argv)
|
||||||
|
{
|
||||||
|
return cmd_select(cmds, argc, argv, do_help);
|
||||||
|
}
|
Loading…
Reference in New Issue