diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-11 10:55:49 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-11 10:55:49 -0700 |
commit | 237f83dfbe668443b5e31c3c7576125871cca674 (patch) | |
tree | 11848a8d0aa414a1d3ce2024e181071b1d9dea08 /tools/bpf/bpftool | |
parent | 8f6ccf6159aed1f04c6d179f61f6fb2691261e84 (diff) | |
parent | 1ff2f0fa450ea4e4f87793d9ed513098ec6e12be (diff) | |
download | linux-237f83dfbe668443b5e31c3c7576125871cca674.tar.gz linux-237f83dfbe668443b5e31c3c7576125871cca674.tar.bz2 linux-237f83dfbe668443b5e31c3c7576125871cca674.zip |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller:
"Some highlights from this development cycle:
1) Big refactoring of ipv6 route and neigh handling to support
nexthop objects configurable as units from userspace. From David
Ahern.
2) Convert explored_states in BPF verifier into a hash table,
significantly decreased state held for programs with bpf2bpf
calls, from Alexei Starovoitov.
3) Implement bpf_send_signal() helper, from Yonghong Song.
4) Various classifier enhancements to mvpp2 driver, from Maxime
Chevallier.
5) Add aRFS support to hns3 driver, from Jian Shen.
6) Fix use after free in inet frags by allocating fqdirs dynamically
and reworking how rhashtable dismantle occurs, from Eric Dumazet.
7) Add act_ctinfo packet classifier action, from Kevin
Darbyshire-Bryant.
8) Add TFO key backup infrastructure, from Jason Baron.
9) Remove several old and unused ISDN drivers, from Arnd Bergmann.
10) Add devlink notifications for flash update status to mlxsw driver,
from Jiri Pirko.
11) Lots of kTLS offload infrastructure fixes, from Jakub Kicinski.
12) Add support for mv88e6250 DSA chips, from Rasmus Villemoes.
13) Various enhancements to ipv6 flow label handling, from Eric
Dumazet and Willem de Bruijn.
14) Support TLS offload in nfp driver, from Jakub Kicinski, Dirk van
der Merwe, and others.
15) Various improvements to axienet driver including converting it to
phylink, from Robert Hancock.
16) Add PTP support to sja1105 DSA driver, from Vladimir Oltean.
17) Add mqprio qdisc offload support to dpaa2-eth, from Ioana
Radulescu.
18) Add devlink health reporting to mlx5, from Moshe Shemesh.
19) Convert stmmac over to phylink, from Jose Abreu.
20) Add PTP PHC (Physical Hardware Clock) support to mlxsw, from
Shalom Toledo.
21) Add nftables SYNPROXY support, from Fernando Fernandez Mancera.
22) Convert tcp_fastopen over to use SipHash, from Ard Biesheuvel.
23) Track spill/fill of constants in BPF verifier, from Alexei
Starovoitov.
24) Support bounded loops in BPF, from Alexei Starovoitov.
25) Various page_pool API fixes and improvements, from Jesper Dangaard
Brouer.
26) Just like ipv4, support ref-countless ipv6 route handling. From
Wei Wang.
27) Support VLAN offloading in aquantia driver, from Igor Russkikh.
28) Add AF_XDP zero-copy support to mlx5, from Maxim Mikityanskiy.
29) Add flower GRE encap/decap support to nfp driver, from Pieter
Jansen van Vuuren.
30) Protect against stack overflow when using act_mirred, from John
Hurley.
31) Allow devmap map lookups from eBPF, from Toke Høiland-Jørgensen.
32) Use page_pool API in netsec driver, Ilias Apalodimas.
33) Add Google gve network driver, from Catherine Sullivan.
34) More indirect call avoidance, from Paolo Abeni.
35) Add kTLS TX HW offload support to mlx5, from Tariq Toukan.
36) Add XDP_REDIRECT support to bnxt_en, from Andy Gospodarek.
37) Add MPLS manipulation actions to TC, from John Hurley.
38) Add sending a packet to connection tracking from TC actions, and
then allow flower classifier matching on conntrack state. From
Paul Blakey.
39) Netfilter hw offload support, from Pablo Neira Ayuso"
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (2080 commits)
net/mlx5e: Return in default case statement in tx_post_resync_params
mlx5: Return -EINVAL when WARN_ON_ONCE triggers in mlx5e_tls_resync().
net: dsa: add support for BRIDGE_MROUTER attribute
pkt_sched: Include const.h
net: netsec: remove static declaration for netsec_set_tx_de()
net: netsec: remove superfluous if statement
netfilter: nf_tables: add hardware offload support
net: flow_offload: rename tc_cls_flower_offload to flow_cls_offload
net: flow_offload: add flow_block_cb_is_busy() and use it
net: sched: remove tcf block API
drivers: net: use flow block API
net: sched: use flow block API
net: flow_offload: add flow_block_cb_{priv, incref, decref}()
net: flow_offload: add list handling functions
net: flow_offload: add flow_block_cb_alloc() and flow_block_cb_free()
net: flow_offload: rename TCF_BLOCK_BINDER_TYPE_* to FLOW_BLOCK_BINDER_TYPE_*
net: flow_offload: rename TC_BLOCK_{UN}BIND to FLOW_BLOCK_{UN}BIND
net: flow_offload: add flow_block_cb_setup_simple()
net: hisilicon: Add an tx_desc to adapt HI13X1_GMAC
net: hisilicon: Add an rx_desc to adapt HI13X1_GMAC
...
Diffstat (limited to 'tools/bpf/bpftool')
-rw-r--r-- | tools/bpf/bpftool/Documentation/bpftool-btf.rst | 39 | ||||
-rw-r--r-- | tools/bpf/bpftool/Documentation/bpftool-cgroup.rst | 11 | ||||
-rw-r--r-- | tools/bpf/bpftool/Documentation/bpftool-feature.rst | 4 | ||||
-rw-r--r-- | tools/bpf/bpftool/Documentation/bpftool-map.rst | 4 | ||||
-rw-r--r-- | tools/bpf/bpftool/Documentation/bpftool-net.rst | 4 | ||||
-rw-r--r-- | tools/bpf/bpftool/Documentation/bpftool-perf.rst | 4 | ||||
-rw-r--r-- | tools/bpf/bpftool/Documentation/bpftool-prog.rst | 42 | ||||
-rw-r--r-- | tools/bpf/bpftool/Documentation/bpftool.rst | 4 | ||||
-rw-r--r-- | tools/bpf/bpftool/bash-completion/bpftool | 76 | ||||
-rw-r--r-- | tools/bpf/bpftool/btf.c | 162 | ||||
-rw-r--r-- | tools/bpf/bpftool/cgroup.c | 11 | ||||
-rw-r--r-- | tools/bpf/bpftool/common.c | 53 | ||||
-rw-r--r-- | tools/bpf/bpftool/jit_disasm.c | 11 | ||||
-rw-r--r-- | tools/bpf/bpftool/main.c | 45 | ||||
-rw-r--r-- | tools/bpf/bpftool/main.h | 3 | ||||
-rw-r--r-- | tools/bpf/bpftool/map_perf_ring.c | 201 | ||||
-rw-r--r-- | tools/bpf/bpftool/prog.c | 378 | ||||
-rw-r--r-- | tools/bpf/bpftool/xlated_dumper.c | 4 |
18 files changed, 723 insertions, 333 deletions
diff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst index 2dbc1413fabd..6694a0fc8f99 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst @@ -19,10 +19,11 @@ SYNOPSIS BTF COMMANDS ============= -| **bpftool** **btf dump** *BTF_SRC* +| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*] | **bpftool** **btf help** | | *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* } +| *FORMAT* := { **raw** | **c** } | *MAP* := { **id** *MAP_ID* | **pinned** *FILE* } | *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* } @@ -31,23 +32,27 @@ DESCRIPTION **bpftool btf dump** *BTF_SRC* Dump BTF entries from a given *BTF_SRC*. - When **id** is specified, BTF object with that ID will be - loaded and all its BTF types emitted. + When **id** is specified, BTF object with that ID will be + loaded and all its BTF types emitted. - When **map** is provided, it's expected that map has - associated BTF object with BTF types describing key and - value. It's possible to select whether to dump only BTF - type(s) associated with key (**key**), value (**value**), - both key and value (**kv**), or all BTF types present in - associated BTF object (**all**). If not specified, **kv** - is assumed. + When **map** is provided, it's expected that map has + associated BTF object with BTF types describing key and + value. It's possible to select whether to dump only BTF + type(s) associated with key (**key**), value (**value**), + both key and value (**kv**), or all BTF types present in + associated BTF object (**all**). If not specified, **kv** + is assumed. - When **prog** is provided, it's expected that program has - associated BTF object with BTF types. + When **prog** is provided, it's expected that program has + associated BTF object with BTF types. - When specifying *FILE*, an ELF file is expected, containing - .BTF section with well-defined BTF binary format data, - typically produced by clang or pahole. + When specifying *FILE*, an ELF file is expected, containing + .BTF section with well-defined BTF binary format data, + typically produced by clang or pahole. + + **format** option can be used to override default (raw) + output format. Raw (**raw**) or C-syntax (**c**) output + formats are supported. **bpftool btf help** Print short help message. @@ -67,6 +72,10 @@ OPTIONS -p, --pretty Generate human-readable JSON output. Implies **-j**. + -d, --debug + Print all logs available from libbpf, including debug-level + information. + EXAMPLES ======== **# bpftool btf dump id 1226** diff --git a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst index e744b3e4e56a..585f270c2d25 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst @@ -29,7 +29,8 @@ CGROUP COMMANDS | *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* } | *ATTACH_TYPE* := { **ingress** | **egress** | **sock_create** | **sock_ops** | **device** | | **bind4** | **bind6** | **post_bind4** | **post_bind6** | **connect4** | **connect6** | -| **sendmsg4** | **sendmsg6** | **recvmsg4** | **recvmsg6** | **sysctl** } +| **sendmsg4** | **sendmsg6** | **recvmsg4** | **recvmsg6** | **sysctl** | +| **getsockopt** | **setsockopt** } | *ATTACH_FLAGS* := { **multi** | **override** } DESCRIPTION @@ -90,7 +91,9 @@ DESCRIPTION an unconnected udp4 socket (since 5.2); **recvmsg6** call to recvfrom(2), recvmsg(2), recvmmsg(2) for an unconnected udp6 socket (since 5.2); - **sysctl** sysctl access (since 5.2). + **sysctl** sysctl access (since 5.2); + **getsockopt** call to getsockopt (since 5.3); + **setsockopt** call to setsockopt (since 5.3). **bpftool cgroup detach** *CGROUP* *ATTACH_TYPE* *PROG* Detach *PROG* from the cgroup *CGROUP* and attach type @@ -117,6 +120,10 @@ OPTIONS -f, --bpffs Show file names of pinned programs. + -d, --debug + Print all logs available from libbpf, including debug-level + information. + EXAMPLES ======== | diff --git a/tools/bpf/bpftool/Documentation/bpftool-feature.rst b/tools/bpf/bpftool/Documentation/bpftool-feature.rst index 14180e887082..4d08f35034a2 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-feature.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-feature.rst @@ -73,6 +73,10 @@ OPTIONS -p, --pretty Generate human-readable JSON output. Implies **-j**. + -d, --debug + Print all logs available from libbpf, including debug-level + information. + SEE ALSO ======== **bpf**\ (2), diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst b/tools/bpf/bpftool/Documentation/bpftool-map.rst index 13ef27b39f20..490b4501cb6e 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-map.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst @@ -152,6 +152,10 @@ OPTIONS Do not automatically attempt to mount any virtual file system (such as tracefs or BPF virtual file system) when necessary. + -d, --debug + Print all logs available from libbpf, including debug-level + information. + EXAMPLES ======== **# bpftool map show** diff --git a/tools/bpf/bpftool/Documentation/bpftool-net.rst b/tools/bpf/bpftool/Documentation/bpftool-net.rst index 934580850f42..d8e5237a2085 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-net.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-net.rst @@ -65,6 +65,10 @@ OPTIONS -p, --pretty Generate human-readable JSON output. Implies **-j**. + -d, --debug + Print all logs available from libbpf, including debug-level + information. + EXAMPLES ======== diff --git a/tools/bpf/bpftool/Documentation/bpftool-perf.rst b/tools/bpf/bpftool/Documentation/bpftool-perf.rst index 0c7576523a21..e252bd0bc434 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-perf.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-perf.rst @@ -53,6 +53,10 @@ OPTIONS -p, --pretty Generate human-readable JSON output. Implies **-j**. + -d, --debug + Print all logs available from libbpf, including debug-level + information. + EXAMPLES ======== diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index 018ecef8dc13..7a374b3c851d 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -29,6 +29,7 @@ PROG COMMANDS | **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*] | **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*] | **bpftool** **prog tracelog** +| **bpftool** **prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*] | **bpftool** **prog help** | | *MAP* := { **id** *MAP_ID* | **pinned** *FILE* } @@ -40,7 +41,8 @@ PROG COMMANDS | **lwt_seg6local** | **sockops** | **sk_skb** | **sk_msg** | **lirc_mode2** | | **cgroup/bind4** | **cgroup/bind6** | **cgroup/post_bind4** | **cgroup/post_bind6** | | **cgroup/connect4** | **cgroup/connect6** | **cgroup/sendmsg4** | **cgroup/sendmsg6** | -| **cgroup/recvmsg4** | **cgroup/recvmsg6** | **cgroup/sysctl** +| **cgroup/recvmsg4** | **cgroup/recvmsg6** | **cgroup/sysctl** | +| **cgroup/getsockopt** | **cgroup/setsockopt** | } | *ATTACH_TYPE* := { | **msg_verdict** | **stream_verdict** | **stream_parser** | **flow_dissector** @@ -145,6 +147,39 @@ DESCRIPTION streaming data from BPF programs to user space, one can use perf events (see also **bpftool-map**\ (8)). + **bpftool prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*] + Run BPF program *PROG* in the kernel testing infrastructure + for BPF, meaning that the program works on the data and + context provided by the user, and not on actual packets or + monitored functions etc. Return value and duration for the + test run are printed out to the console. + + Input data is read from the *FILE* passed with **data_in**. + If this *FILE* is "**-**", input data is read from standard + input. Input context, if any, is read from *FILE* passed with + **ctx_in**. Again, "**-**" can be used to read from standard + input, but only if standard input is not already in use for + input data. If a *FILE* is passed with **data_out**, output + data is written to that file. Similarly, output context is + written to the *FILE* passed with **ctx_out**. For both + output flows, "**-**" can be used to print to the standard + output (as plain text, or JSON if relevant option was + passed). If output keywords are omitted, output data and + context are discarded. Keywords **data_size_out** and + **ctx_size_out** are used to pass the size (in bytes) for the + output buffers to the kernel, although the default of 32 kB + should be more than enough for most cases. + + Keyword **repeat** is used to indicate the number of + consecutive runs to perform. Note that output data and + context printed to files correspond to the last of those + runs. The duration printed out at the end of the runs is an + average over all runs performed by the command. + + Not all program types support test run. Among those which do, + not all of them can take the **ctx_in**/**ctx_out** + arguments. bpftool does not perform checks on program types. + **bpftool prog help** Print short help message. @@ -174,6 +209,11 @@ OPTIONS Do not automatically attempt to mount any virtual file system (such as tracefs or BPF virtual file system) when necessary. + -d, --debug + Print all logs available, even debug-level information. This + includes logs from libbpf as well as from the verifier, when + attempting to load programs. + EXAMPLES ======== **# bpftool prog show** diff --git a/tools/bpf/bpftool/Documentation/bpftool.rst b/tools/bpf/bpftool/Documentation/bpftool.rst index 3e562d7fd56f..6a9c52ef84a9 100644 --- a/tools/bpf/bpftool/Documentation/bpftool.rst +++ b/tools/bpf/bpftool/Documentation/bpftool.rst @@ -66,6 +66,10 @@ OPTIONS Do not automatically attempt to mount any virtual file system (such as tracefs or BPF virtual file system) when necessary. + -d, --debug + Print all logs available, even debug-level information. This + includes logs from libbpf as well as from the verifier, when + attempting to load programs. SEE ALSO ======== diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 4300adf6e5ab..c8f42e1fcbc9 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -71,6 +71,12 @@ _bpftool_get_prog_tags() command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) ) } +_bpftool_get_btf_ids() +{ + COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \ + command sed -n 's/.*"btf_id": \(.*\),\?$/\1/p' )" -- "$cur" ) ) +} + _bpftool_get_obj_map_names() { local obj @@ -181,7 +187,7 @@ _bpftool() # Deal with options if [[ ${words[cword]} == -* ]]; then - local c='--version --json --pretty --bpffs --mapcompat' + local c='--version --json --pretty --bpffs --mapcompat --debug' COMPREPLY=( $( compgen -W "$c" -- "$cur" ) ) return 0 fi @@ -336,6 +342,13 @@ _bpftool() load|loadall) local obj + # Propose "load/loadall" to complete "bpftool prog load", + # or bash tries to complete "load" as a filename below. + if [[ ${#words[@]} -eq 3 ]]; then + COMPREPLY=( $( compgen -W "load loadall" -- "$cur" ) ) + return 0 + fi + if [[ ${#words[@]} -lt 6 ]]; then _filedir return 0 @@ -373,7 +386,8 @@ _bpftool() cgroup/sendmsg4 cgroup/sendmsg6 \ cgroup/recvmsg4 cgroup/recvmsg6 \ cgroup/post_bind4 cgroup/post_bind6 \ - cgroup/sysctl" -- \ + cgroup/sysctl cgroup/getsockopt \ + cgroup/setsockopt" -- \ "$cur" ) ) return 0 ;; @@ -401,10 +415,34 @@ _bpftool() tracelog) return 0 ;; + run) + if [[ ${#words[@]} -lt 5 ]]; then + _filedir + return 0 + fi + case $prev in + id) + _bpftool_get_prog_ids + return 0 + ;; + data_in|data_out|ctx_in|ctx_out) + _filedir + return 0 + ;; + repeat|data_size_out|ctx_size_out) + return 0 + ;; + *) + _bpftool_once_attr 'data_in data_out data_size_out \ + ctx_in ctx_out ctx_size_out repeat' + return 0 + ;; + esac + ;; *) [[ $prev == $object ]] && \ - COMPREPLY=( $( compgen -W 'dump help pin attach detach load \ - show list tracelog' -- "$cur" ) ) + COMPREPLY=( $( compgen -W 'dump help pin attach detach \ + load loadall show list tracelog run' -- "$cur" ) ) ;; esac ;; @@ -636,14 +674,30 @@ _bpftool() map) _bpftool_get_map_ids ;; + dump) + _bpftool_get_btf_ids + ;; esac return 0 ;; + format) + COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) ) + ;; *) - if [[ $cword == 6 ]] && [[ ${words[3]} == "map" ]]; then - COMPREPLY+=( $( compgen -W 'key value kv all' -- \ - "$cur" ) ) - fi + # emit extra options + case ${words[3]} in + id|file) + _bpftool_once_attr 'format' + ;; + map|prog) + if [[ ${words[3]} == "map" ]] && [[ $cword == 6 ]]; then + COMPREPLY+=( $( compgen -W "key value kv all" -- "$cur" ) ) + fi + _bpftool_once_attr 'format' + ;; + *) + ;; + esac return 0 ;; esac @@ -667,7 +721,8 @@ _bpftool() attach|detach) local ATTACH_TYPES='ingress egress sock_create sock_ops \ device bind4 bind6 post_bind4 post_bind6 connect4 \ - connect6 sendmsg4 sendmsg6 recvmsg4 recvmsg6 sysctl' + connect6 sendmsg4 sendmsg6 recvmsg4 recvmsg6 sysctl \ + getsockopt setsockopt' local ATTACH_FLAGS='multi override' local PROG_TYPE='id pinned tag' case $prev in @@ -677,7 +732,8 @@ _bpftool() ;; ingress|egress|sock_create|sock_ops|device|bind4|bind6|\ post_bind4|post_bind6|connect4|connect6|sendmsg4|\ - sendmsg6|recvmsg4|recvmsg6|sysctl) + sendmsg6|recvmsg4|recvmsg6|sysctl|getsockopt|\ + setsockopt) COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \ "$cur" ) ) return 0 diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c index 7317438ecd9e..1b8ec91899e6 100644 --- a/tools/bpf/bpftool/btf.c +++ b/tools/bpf/bpftool/btf.c @@ -8,8 +8,8 @@ #include <stdio.h> #include <string.h> #include <unistd.h> -#include <gelf.h> #include <bpf.h> +#include <libbpf.h> #include <linux/btf.h> #include "btf.h" @@ -340,109 +340,40 @@ static int dump_btf_raw(const struct btf *btf, return 0; } -static bool check_btf_endianness(GElf_Ehdr *ehdr) +static void __printf(2, 0) btf_dump_printf(void *ctx, + const char *fmt, va_list args) { - static unsigned int const endian = 1; - - switch (ehdr->e_ident[EI_DATA]) { - case ELFDATA2LSB: - return *(unsigned char const *)&endian == 1; - case ELFDATA2MSB: - return *(unsigned char const *)&endian == 0; - default: - return 0; - } + vfprintf(stdout, fmt, args); } -static int btf_load_from_elf(const char *path, struct btf **btf) +static int dump_btf_c(const struct btf *btf, + __u32 *root_type_ids, int root_type_cnt) { - int err = -1, fd = -1, idx = 0; - Elf_Data *btf_data = NULL; - Elf_Scn *scn = NULL; - Elf *elf = NULL; - GElf_Ehdr ehdr; - - if (elf_version(EV_CURRENT) == EV_NONE) { - p_err("failed to init libelf for %s", path); - return -1; - } - - fd = open(path, O_RDONLY); - if (fd < 0) { - p_err("failed to open %s: %s", path, strerror(errno)); - return -1; - } - - elf = elf_begin(fd, ELF_C_READ, NULL); - if (!elf) { - p_err("failed to open %s as ELF file", path); - goto done; - } - if (!gelf_getehdr(elf, &ehdr)) { - p_err("failed to get EHDR from %s", path); - goto done; - } - if (!check_btf_endianness(&ehdr)) { - p_err("non-native ELF endianness is not supported"); - goto done; - } - if (!elf_rawdata(elf_getscn(elf, ehdr.e_shstrndx), NULL)) { - p_err("failed to get e_shstrndx from %s\n", path); - goto done; - } + struct btf_dump *d; + int err = 0, i; - while ((scn = elf_nextscn(elf, scn)) != NULL) { - GElf_Shdr sh; - char *name; + d = btf_dump__new(btf, NULL, NULL, btf_dump_printf); + if (IS_ERR(d)) + return PTR_ERR(d); - idx++; - if (gelf_getshdr(scn, &sh) != &sh) { - p_err("failed to get section(%d) header from %s", - idx, path); - goto done; - } - name = elf_strptr(elf, ehdr.e_shstrndx, sh.sh_name); - if (!name) { - p_err("failed to get section(%d) name from %s", - idx, path); - goto done; - } - if (strcmp(name, BTF_ELF_SEC) == 0) { - btf_data = elf_getdata(scn, 0); - if (!btf_data) { - p_err("failed to get section(%d, %s) data from %s", - idx, name, path); + if (root_type_cnt) { + for (i = 0; i < root_type_cnt; i++) { + err = btf_dump__dump_type(d, root_type_ids[i]); + if (err) goto done; - } - break; } - } - - if (!btf_data) { - p_err("%s ELF section not found in %s", BTF_ELF_SEC, path); - goto done; - } + } else { + int cnt = btf__get_nr_types(btf); - *btf = btf__new(btf_data->d_buf, btf_data->d_size); - if (IS_ERR(*btf)) { - err = PTR_ERR(*btf); - *btf = NULL; - p_err("failed to load BTF data from %s: %s", - path, strerror(err)); - goto done; + for (i = 1; i <= cnt; i++) { + err = btf_dump__dump_type(d, i); + if (err) + goto done; + } } - err = 0; done: - if (err) { - if (*btf) { - btf__free(*btf); - *btf = NULL; - } - } - if (elf) - elf_end(elf); - close(fd); + btf_dump__free(d); return err; } @@ -451,6 +382,7 @@ static int do_dump(int argc, char **argv) struct btf *btf = NULL; __u32 root_type_ids[2]; int root_type_cnt = 0; + bool dump_c = false; __u32 btf_id = -1; const char *src; int fd = -1; @@ -522,9 +454,14 @@ static int do_dump(int argc, char **argv) } NEXT_ARG(); } else if (is_prefix(src, "file")) { - err = btf_load_from_elf(*argv, &btf); - if (err) + btf = btf__parse_elf(*argv, NULL); + if (IS_ERR(btf)) { + err = PTR_ERR(btf); + btf = NULL; + p_err("failed to load BTF from %s: %s", + *argv, strerror(err)); goto done; + } NEXT_ARG(); } else { err = -1; @@ -532,6 +469,29 @@ static int do_dump(int argc, char **argv) goto done; } + while (argc) { + if (is_prefix(*argv, "format")) { + NEXT_ARG(); + if (argc < 1) { + p_err("expecting value for 'format' option\n"); + goto done; + } + if (strcmp(*argv, "c") == 0) { + dump_c = true; + } else if (strcmp(*argv, "raw") == 0) { + dump_c = false; + } else { + p_err("unrecognized format specifier: '%s', possible values: raw, c", + *argv); + goto done; + } + NEXT_ARG(); + } else { + p_err("unrecognized option: '%s'", *argv); + goto done; + } + } + if (!btf) { err = btf__get_from_id(btf_id, &btf); if (err) { @@ -545,7 +505,16 @@ static int do_dump(int argc, char **argv) } } - dump_btf_raw(btf, root_type_ids, root_type_cnt); + if (dump_c) { + if (json_output) { + p_err("JSON output for C-syntax dump is not supported"); + err = -ENOTSUP; + goto done; + } + err = dump_btf_c(btf, root_type_ids, root_type_cnt); + } else { + err = dump_btf_raw(btf, root_type_ids, root_type_cnt); + } done: close(fd); @@ -561,10 +530,11 @@ static int do_help(int argc, char **argv) } fprintf(stderr, - "Usage: %s btf dump BTF_SRC\n" + "Usage: %s btf dump BTF_SRC [format FORMAT]\n" " %s btf help\n" "\n" " BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n" + " FORMAT := { raw | c }\n" " " HELP_SPEC_MAP "\n" " " HELP_SPEC_PROGRAM "\n" " " HELP_SPEC_OPTIONS "\n" diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c index 73ec8ea33fb4..f3c05b08c68c 100644 --- a/tools/bpf/bpftool/cgroup.c +++ b/tools/bpf/bpftool/cgroup.c @@ -26,7 +26,8 @@ " sock_ops | device | bind4 | bind6 |\n" \ " post_bind4 | post_bind6 | connect4 |\n" \ " connect6 | sendmsg4 | sendmsg6 |\n" \ - " recvmsg4 | recvmsg6 | sysctl }" + " recvmsg4 | recvmsg6 | sysctl |\n" \ + " getsockopt | setsockopt }" static const char * const attach_type_strings[] = { [BPF_CGROUP_INET_INGRESS] = "ingress", @@ -45,6 +46,8 @@ static const char * const attach_type_strings[] = { [BPF_CGROUP_SYSCTL] = "sysctl", [BPF_CGROUP_UDP4_RECVMSG] = "recvmsg4", [BPF_CGROUP_UDP6_RECVMSG] = "recvmsg6", + [BPF_CGROUP_GETSOCKOPT] = "getsockopt", + [BPF_CGROUP_SETSOCKOPT] = "setsockopt", [__MAX_BPF_ATTACH_TYPE] = NULL, }; @@ -168,7 +171,7 @@ static int do_show(int argc, char **argv) cgroup_fd = open(argv[0], O_RDONLY); if (cgroup_fd < 0) { - p_err("can't open cgroup %s", argv[1]); + p_err("can't open cgroup %s", argv[0]); goto exit; } @@ -356,7 +359,7 @@ static int do_attach(int argc, char **argv) cgroup_fd = open(argv[0], O_RDONLY); if (cgroup_fd < 0) { - p_err("can't open cgroup %s", argv[1]); + p_err("can't open cgroup %s", argv[0]); goto exit; } @@ -414,7 +417,7 @@ static int do_detach(int argc, char **argv) cgroup_fd = open(argv[0], O_RDONLY); if (cgroup_fd < 0) { - p_err("can't open cgroup %s", argv[1]); + p_err("can't open cgroup %s", argv[0]); goto exit; } diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index f7261fad45c1..5215e0870bcb 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -21,6 +21,7 @@ #include <sys/vfs.h> #include <bpf.h> +#include <libbpf.h> /* libbpf_num_possible_cpus */ #include "main.h" @@ -439,57 +440,13 @@ unsigned int get_page_size(void) 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) { - p_err("can't open sysfs possible cpus"); - exit(-1); - } - - n = read(fd, buf, sizeof(buf)); - if (n < 2) { - p_err("can't read sysfs possible cpus"); - exit(-1); - } - close(fd); + int cpus = libbpf_num_possible_cpus(); - if (n == sizeof(buf)) { - p_err("read sysfs possible cpus overflow"); + if (cpus < 0) { + p_err("Can't get # of possible cpus: %s", strerror(-cpus)); 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; + return cpus; } static char * diff --git a/tools/bpf/bpftool/jit_disasm.c b/tools/bpf/bpftool/jit_disasm.c index 3ef3093560ba..bfed711258ce 100644 --- a/tools/bpf/bpftool/jit_disasm.c +++ b/tools/bpf/bpftool/jit_disasm.c @@ -11,6 +11,8 @@ * Licensed under the GNU General Public License, version 2.0 (GPLv2) */ +#define _GNU_SOURCE +#include <stdio.h> #include <stdarg.h> #include <stdint.h> #include <stdio.h> @@ -44,11 +46,13 @@ static int fprintf_json(void *out, const char *fmt, ...) char *s; va_start(ap, fmt); + if (vasprintf(&s, fmt, ap) < 0) + return -1; + va_end(ap); + if (!oper_count) { int i; - s = va_arg(ap, char *); - /* Strip trailing spaces */ i = strlen(s) - 1; while (s[i] == ' ') @@ -61,11 +65,10 @@ static int fprintf_json(void *out, const char *fmt, ...) } else if (!strcmp(fmt, ",")) { /* Skip */ } else { - s = va_arg(ap, char *); jsonw_string(json_wtr, s); oper_count++; } - va_end(ap); + free(s); return 0; } diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 1ac1fc520e6a..e916ff25697f 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -10,6 +10,7 @@ #include <string.h> #include <bpf.h> +#include <libbpf.h> #include "main.h" @@ -25,6 +26,7 @@ bool pretty_output; bool json_output; bool show_pinned; bool block_mount; +bool verifier_logs; int bpf_flags; struct pinned_obj_table prog_table; struct pinned_obj_table map_table; @@ -77,6 +79,13 @@ static int do_version(int argc, char **argv) return 0; } +static int __printf(2, 0) +print_all_levels(__maybe_unused enum libbpf_print_level level, + const char *format, va_list args) +{ + return vfprintf(stderr, format, args); +} + int cmd_select(const struct cmd *cmds, int argc, char **argv, int (*help)(int argc, char **argv)) { @@ -108,6 +117,35 @@ bool is_prefix(const char *pfx, const char *str) return !memcmp(str, pfx, strlen(pfx)); } +/* Last argument MUST be NULL pointer */ +int detect_common_prefix(const char *arg, ...) +{ + unsigned int count = 0; + const char *ref; + char msg[256]; + va_list ap; + + snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg); + va_start(ap, arg); + while ((ref = va_arg(ap, const char *))) { + if (!is_prefix(arg, ref)) + continue; + count++; + if (count > 1) + strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1); + strncat(msg, ref, sizeof(msg) - strlen(msg) - 1); + } + va_end(ap); + strncat(msg, "'", sizeof(msg) - strlen(msg) - 1); + + if (count >= 2) { + p_err(msg); + return -1; + } + + return 0; +} + void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep) { unsigned char *data = arg; @@ -317,6 +355,7 @@ int main(int argc, char **argv) { "bpffs", no_argument, NULL, 'f' }, { "mapcompat", no_argument, NULL, 'm' }, { "nomount", no_argument, NULL, 'n' }, + { "debug", no_argument, NULL, 'd' }, { 0 } }; int opt, ret; @@ -332,7 +371,7 @@ int main(int argc, char **argv) hash_init(map_table.table); opterr = 0; - while ((opt = getopt_long(argc, argv, "Vhpjfmn", + while ((opt = getopt_long(argc, argv, "Vhpjfmnd", options, NULL)) >= 0) { switch (opt) { case 'V': @@ -362,6 +401,10 @@ int main(int argc, char **argv) case 'n': block_mount = true; break; + case 'd': + libbpf_set_print(print_all_levels); + verifier_logs = true; + break; default: p_err("unrecognized option '%s'", argv[optind - 1]); if (json_output) diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 3d63feb7f852..3ef0d9051e10 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -74,6 +74,7 @@ static const char * const prog_type_name[] = { [BPF_PROG_TYPE_SK_REUSEPORT] = "sk_reuseport", [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector", [BPF_PROG_TYPE_CGROUP_SYSCTL] = "cgroup_sysctl", + [BPF_PROG_TYPE_CGROUP_SOCKOPT] = "cgroup_sockopt", }; extern const char * const map_type_name[]; @@ -91,6 +92,7 @@ extern json_writer_t *json_wtr; extern bool json_output; extern bool show_pinned; extern bool block_mount; +extern bool verifier_logs; extern int bpf_flags; extern struct pinned_obj_table prog_table; extern struct pinned_obj_table map_table; @@ -99,6 +101,7 @@ void p_err(const char *fmt, ...); void p_info(const char *fmt, ...); bool is_prefix(const char *pfx, const char *str); +int detect_common_prefix(const char *arg, ...); void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep); void usage(void) __noreturn; diff --git a/tools/bpf/bpftool/map_perf_ring.c b/tools/bpf/bpftool/map_perf_ring.c index 0507dfaf7a8f..3f108ab17797 100644 --- a/tools/bpf/bpftool/map_perf_ring.c +++ b/tools/bpf/bpftool/map_perf_ring.c @@ -28,7 +28,7 @@ #define MMAP_PAGE_CNT 16 -static bool stop; +static volatile bool stop; struct event_ring_info { int fd; @@ -44,32 +44,44 @@ struct perf_event_sample { unsigned char data[]; }; +struct perf_event_lost { + struct perf_event_header header; + __u64 id; + __u64 lost; +}; + static void int_exit(int signo) { fprintf(stderr, "Stopping...\n"); stop = true; } +struct event_pipe_ctx { + bool all_cpus; + int cpu; + int idx; +}; + static enum bpf_perf_event_ret -print_bpf_output(struct perf_event_header *event, void *private_data) +print_bpf_output(void *private_data, int cpu, struct perf_event_header *event) { - struct perf_event_sample *e = container_of(event, struct perf_event_sample, + struct perf_event_sample *e = container_of(event, + struct perf_event_sample, header); - struct event_ring_info *ring = private_data; - struct { - struct perf_event_header header; - __u64 id; - __u64 lost; - } *lost = (typeof(lost))event; + struct perf_event_lost *lost = container_of(event, + struct perf_event_lost, + header); + struct event_pipe_ctx *ctx = private_data; + int idx = ctx->all_cpus ? cpu : ctx->idx; if (json_output) { jsonw_start_object(json_wtr); jsonw_name(json_wtr, "type"); jsonw_uint(json_wtr, e->header.type); jsonw_name(json_wtr, "cpu"); - jsonw_uint(json_wtr, ring->cpu); + jsonw_uint(json_wtr, cpu); jsonw_name(json_wtr, "index"); - jsonw_uint(json_wtr, ring->key); + jsonw_uint(json_wtr, idx); if (e->header.type == PERF_RECORD_SAMPLE) { jsonw_name(json_wtr, "timestamp"); jsonw_uint(json_wtr, e->time); @@ -89,7 +101,7 @@ print_bpf_output(struct perf_event_header *event, void *private_data) if (e->header.type == PERF_RECORD_SAMPLE) { printf("== @%lld.%09lld CPU: %d index: %d =====\n", e->time / 1000000000ULL, e->time % 1000000000ULL, - ring->cpu, ring->key); + cpu, idx); fprint_hex(stdout, e->data, e->size, " "); printf("\n"); } else if (e->header.type == PERF_RECORD_LOST) { @@ -103,87 +115,25 @@ print_bpf_output(struct perf_event_header *event, void *private_data) return LIBBPF_PERF_EVENT_CONT; } -static void -perf_event_read(struct event_ring_info *ring, void **buf, size_t *buf_len) -{ - enum bpf_perf_event_ret ret; - - ret = bpf_perf_event_read_simple(ring->mem, - MMAP_PAGE_CNT * get_page_size(), - get_page_size(), buf, buf_len, - print_bpf_output, ring); - if (ret != LIBBPF_PERF_EVENT_CONT) { - fprintf(stderr, "perf read loop failed with %d\n", ret); - stop = true; - } -} - -static int perf_mmap_size(void) -{ - return get_page_size() * (MMAP_PAGE_CNT + 1); -} - -static void *perf_event_mmap(int fd) -{ - int mmap_size = perf_mmap_size(); - void *base; - - base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (base == MAP_FAILED) { - p_err("event mmap failed: %s\n", strerror(errno)); - return NULL; - } - - return base; -} - -static void perf_event_unmap(void *mem) -{ - if (munmap(mem, perf_mmap_size())) - fprintf(stderr, "Can't unmap ring memory!\n"); -} - -static int bpf_perf_event_open(int map_fd, int key, int cpu) +int do_event_pipe(int argc, char **argv) { - struct perf_event_attr attr = { + struct perf_event_attr perf_attr = { .sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME, .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_BPF_OUTPUT, + .sample_period = 1, + .wakeup_events = 1, }; - int pmu_fd; - - pmu_fd = sys_perf_event_open(&attr, -1, cpu, -1, 0); - if (pmu_fd < 0) { - p_err("failed to open perf event %d for CPU %d", key, cpu); - return -1; - } - - if (bpf_map_update_elem(map_fd, &key, &pmu_fd, BPF_ANY)) { - p_err("failed to update map for event %d for CPU %d", key, cpu); - goto err_close; - } - if (ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) { - p_err("failed to enable event %d for CPU %d", key, cpu); - goto err_close; - } - - return pmu_fd; - -err_close: - close(pmu_fd); - return -1; -} - -int do_event_pipe(int argc, char **argv) -{ - int i, nfds, map_fd, index = -1, cpu = -1; struct bpf_map_info map_info = {}; - struct event_ring_info *rings; - size_t tmp_buf_sz = 0; - void *tmp_buf = NULL; - struct pollfd *pfds; + struct perf_buffer_raw_opts opts = {}; + struct event_pipe_ctx ctx = { + .all_cpus = true, + .cpu = -1, + .idx = -1, + }; + struct perf_buffer *pb; __u32 map_info_len; - bool do_all = true; + int err, map_fd; map_info_len = sizeof(map_info); map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len); @@ -205,7 +155,7 @@ int do_event_pipe(int argc, char **argv) char *endptr; NEXT_ARG(); - cpu = strtoul(*argv, &endptr, 0); + ctx.cpu = strtoul(*argv, &endptr, 0); if (*endptr) { p_err("can't parse %s as CPU ID", **argv); goto err_close_map; @@ -216,7 +166,7 @@ int do_event_pipe(int argc, char **argv) char *endptr; NEXT_ARG(); - index = strtoul(*argv, &endptr, 0); + ctx.idx = strtoul(*argv, &endptr, 0); if (*endptr) { p_err("can't parse %s as index", **argv); goto err_close_map; @@ -228,45 +178,32 @@ int do_event_pipe(int argc, char **argv) goto err_close_map; } - do_all = false; + ctx.all_cpus = false; } - if (!do_all) { - if (index == -1 || cpu == -1) { + if (!ctx.all_cpus) { + if (ctx.idx == -1 || ctx.cpu == -1) { p_err("cpu and index must be specified together"); goto err_close_map; } - - nfds = 1; } else { - nfds = min(get_possible_cpus(), map_info.max_entries); - cpu = 0; - index = 0; + ctx.cpu = 0; + ctx.idx = 0; } - rings = calloc(nfds, sizeof(rings[0])); - if (!rings) + opts.attr = &perf_attr; + opts.event_cb = print_bpf_output; + opts.ctx = &ctx; + opts.cpu_cnt = ctx.all_cpus ? 0 : 1; + opts.cpus = &ctx.cpu; + opts.map_keys = &ctx.idx; + + pb = perf_buffer__new_raw(map_fd, MMAP_PAGE_CNT, &opts); + err = libbpf_get_error(pb); + if (err) { + p_err("failed to create perf buffer: %s (%d)", + strerror(err), err); goto err_close_map; - - pfds = calloc(nfds, sizeof(pfds[0])); - if (!pfds) - goto err_free_rings; - - for (i = 0; i < nfds; i++) { - rings[i].cpu = cpu + i; - rings[i].key = index + i; - - rings[i].fd = bpf_perf_event_open(map_fd, rings[i].key, - rings[i].cpu); - if (rings[i].fd < 0) - goto err_close_fds_prev; - - rings[i].mem = perf_event_mmap(rings[i].fd); - if (!rings[i].mem) - goto err_close_fds_current; - - pfds[i].fd = rings[i].fd; - pfds[i].events = POLLIN; } signal(SIGINT, int_exit); @@ -277,34 +214,24 @@ int do_event_pipe(int argc, char **argv) jsonw_start_array(json_wtr); while (!stop) { - poll(pfds, nfds, 200); - for (i = 0; i < nfds; i++) - perf_event_read(&rings[i], &tmp_buf, &tmp_buf_sz); + err = perf_buffer__poll(pb, 200); + if (err < 0 && err != -EINTR) { + p_err("perf buffer polling failed: %s (%d)", + strerror(err), err); + goto err_close_pb; + } } - free(tmp_buf); if (json_output) jsonw_end_array(json_wtr); - for (i = 0; i < nfds; i++) { - perf_event_unmap(rings[i].mem); - close(rings[i].fd); - } - free(pfds); - free(rings); + perf_buffer__free(pb); close(map_fd); return 0; -err_close_fds_prev: - while (i--) { - perf_event_unmap(rings[i].mem); -err_close_fds_current: - close(rings[i].fd); - } - free(pfds); -err_free_rings: - free(rings); +err_close_pb: + perf_buffer__free(pb); err_close_map: close(map_fd); return -1; diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 7a4e21a31523..66f04a4846a5 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -15,6 +15,7 @@ #include <sys/stat.h> #include <linux/err.h> +#include <linux/sizes.h> #include <bpf.h> #include <btf.h> @@ -748,12 +749,351 @@ static int do_detach(int argc, char **argv) return 0; } +static int check_single_stdin(char *file_data_in, char *file_ctx_in) +{ + if (file_data_in && file_ctx_in && + !strcmp(file_data_in, "-") && !strcmp(file_ctx_in, "-")) { + p_err("cannot use standard input for both data_in and ctx_in"); + return -1; + } + + return 0; +} + +static int get_run_data(const char *fname, void **data_ptr, unsigned int *size) +{ + size_t block_size = 256; + size_t buf_size = block_size; + size_t nb_read = 0; + void *tmp; + FILE *f; + + if (!fname) { + *data_ptr = NULL; + *size = 0; + return 0; + } + + if (!strcmp(fname, "-")) + f = stdin; + else + f = fopen(fname, "r"); + if (!f) { + p_err("failed to open %s: %s", fname, strerror(errno)); + return -1; + } + + *data_ptr = malloc(block_size); + if (!*data_ptr) { + p_err("failed to allocate memory for data_in/ctx_in: %s", + strerror(errno)); + goto err_fclose; + } + + while ((nb_read += fread(*data_ptr + nb_read, 1, block_size, f))) { + if (feof(f)) + break; + if (ferror(f)) { + p_err("failed to read data_in/ctx_in from %s: %s", + fname, strerror(errno)); + goto err_free; + } + if (nb_read > buf_size - block_size) { + if (buf_size == UINT32_MAX) { + p_err("data_in/ctx_in is too long (max: %d)", + UINT32_MAX); + goto err_free; + } + /* No space for fread()-ing next chunk; realloc() */ + buf_size *= 2; + tmp = realloc(*data_ptr, buf_size); + if (!tmp) { + p_err("failed to reallocate data_in/ctx_in: %s", + strerror(errno)); + goto err_free; + } + *data_ptr = tmp; + } + } + if (f != stdin) + fclose(f); + + *size = nb_read; + return 0; + +err_free: + free(*data_ptr); + *data_ptr = NULL; +err_fclose: + if (f != stdin) + fclose(f); + return -1; +} + +static void hex_print(void *data, unsigned int size, FILE *f) +{ + size_t i, j; + char c; + + for (i = 0; i < size; i += 16) { + /* Row offset */ + fprintf(f, "%07zx\t", i); + + /* Hexadecimal values */ + for (j = i; j < i + 16 && j < size; j++) + fprintf(f, "%02x%s", *(uint8_t *)(data + j), + j % 2 ? " " : ""); + for (; j < i + 16; j++) + fprintf(f, " %s", j % 2 ? " " : ""); + + /* ASCII values (if relevant), '.' otherwise */ + fprintf(f, "| "); + for (j = i; j < i + 16 && j < size; j++) { + c = *(char *)(data + j); + if (c < ' ' || c > '~') + c = '.'; + fprintf(f, "%c%s", c, j == i + 7 ? " " : ""); + } + + fprintf(f, "\n"); + } +} + +static int +print_run_output(void *data, unsigned int size, const char *fname, + const char *json_key) +{ + size_t nb_written; + FILE *f; + + if (!fname) + return 0; + + if (!strcmp(fname, "-")) { + f = stdout; + if (json_output) { + jsonw_name(json_wtr, json_key); + print_data_json(data, size); + } else { + hex_print(data, size, f); + } + return 0; + } + + f = fopen(fname, "w"); + if (!f) { + p_err("failed to open %s: %s", fname, strerror(errno)); + return -1; + } + + nb_written = fwrite(data, 1, size, f); + fclose(f); + if (nb_written != size) { + p_err("failed to write output data/ctx: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int alloc_run_data(void **data_ptr, unsigned int size_out) +{ + *data_ptr = calloc(size_out, 1); + if (!*data_ptr) { + p_err("failed to allocate memory for output data/ctx: %s", + strerror(errno)); + return -1; + } + + return 0; +} + +static int do_run(int argc, char **argv) +{ + char *data_fname_in = NULL, *data_fname_out = NULL; + char *ctx_fname_in = NULL, *ctx_fname_out = NULL; + struct bpf_prog_test_run_attr test_attr = {0}; + const unsigned int default_size = SZ_32K; + void *data_in = NULL, *data_out = NULL; + void *ctx_in = NULL, *ctx_out = NULL; + unsigned int repeat = 1; + int fd, err; + + if (!REQ_ARGS(4)) + return -1; + + fd = prog_parse_fd(&argc, &argv); + if (fd < 0) + return -1; + + while (argc) { + if (detect_common_prefix(*argv, "data_in", "data_out", + "data_size_out", NULL)) + return -1; + if (detect_common_prefix(*argv, "ctx_in", "ctx_out", + "ctx_size_out", NULL)) + return -1; + + if (is_prefix(*argv, "data_in")) { + NEXT_ARG(); + if (!REQ_ARGS(1)) + return -1; + + data_fname_in = GET_ARG(); + if (check_single_stdin(data_fname_in, ctx_fname_in)) + return -1; + } else if (is_prefix(*argv, "data_out")) { + NEXT_ARG(); + if (!REQ_ARGS(1)) + return -1; + + data_fname_out = GET_ARG(); + } else if (is_prefix(*argv, "data_size_out")) { + char *endptr; + + NEXT_ARG(); + if (!REQ_ARGS(1)) + return -1; + + test_attr.data_size_out = strtoul(*argv, &endptr, 0); + if (*endptr) { + p_err("can't parse %s as output data size", + *argv); + return -1; + } + NEXT_ARG(); + } else if (is_prefix(*argv, "ctx_in")) { + NEXT_ARG(); + if (!REQ_ARGS(1)) + return -1; + + ctx_fname_in = GET_ARG(); + if (check_single_stdin(data_fname_in, ctx_fname_in)) + return -1; + } else if (is_prefix(*argv, "ctx_out")) { + NEXT_ARG(); + if (!REQ_ARGS(1)) + return -1; + + ctx_fname_out = GET_ARG(); + } else if (is_prefix(*argv, "ctx_size_out")) { + char *endptr; + + NEXT_ARG(); + if (!REQ_ARGS(1)) + return -1; + + test_attr.ctx_size_out = strtoul(*argv, &endptr, 0); + if (*endptr) { + p_err("can't parse %s as output context size", + *argv); + return -1; + } + NEXT_ARG(); + } else if (is_prefix(*argv, "repeat")) { + char *endptr; + + NEXT_ARG(); + if (!REQ_ARGS(1)) + return -1; + + repeat = strtoul(*argv, &endptr, 0); + if (*endptr) { + p_err("can't parse %s as repeat number", + *argv); + return -1; + } + NEXT_ARG(); + } else { + p_err("expected no more arguments, 'data_in', 'data_out', 'data_size_out', 'ctx_in', 'ctx_out', 'ctx_size_out' or 'repeat', got: '%s'?", + *argv); + return -1; + } + } + + err = get_run_data(data_fname_in, &data_in, &test_attr.data_size_in); + if (err) + return -1; + + if (data_in) { + if (!test_attr.data_size_out) + test_attr.data_size_out = default_size; + err = alloc_run_data(&data_out, test_attr.data_size_out); + if (err) + goto free_data_in; + } + + err = get_run_data(ctx_fname_in, &ctx_in, &test_attr.ctx_size_in); + if (err) + goto free_data_out; + + if (ctx_in) { + if (!test_attr.ctx_size_out) + test_attr.ctx_size_out = default_size; + err = alloc_run_data(&ctx_out, test_attr.ctx_size_out); + if (err) + goto free_ctx_in; + } + + test_attr.prog_fd = fd; + test_attr.repeat = repeat; + test_attr.data_in = data_in; + test_attr.data_out = data_out; + test_attr.ctx_in = ctx_in; + test_attr.ctx_out = ctx_out; + + err = bpf_prog_test_run_xattr(&test_attr); + if (err) { + p_err("failed to run program: %s", strerror(errno)); + goto free_ctx_out; + } + + err = 0; + + if (json_output) + jsonw_start_object(json_wtr); /* root */ + + /* Do not exit on errors occurring when printing output data/context, + * we still want to print return value and duration for program run. + */ + if (test_attr.data_size_out) + err += print_run_output(test_attr.data_out, + test_attr.data_size_out, + data_fname_out, "data_out"); + if (test_attr.ctx_size_out) + err += print_run_output(test_attr.ctx_out, + test_attr.ctx_size_out, + ctx_fname_out, "ctx_out"); + + if (json_output) { + jsonw_uint_field(json_wtr, "retval", test_attr.retval); + jsonw_uint_field(json_wtr, "duration", test_attr.duration); + jsonw_end_object(json_wtr); /* root */ + } else { + fprintf(stdout, "Return value: %u, duration%s: %uns\n", + test_attr.retval, + repeat > 1 ? " (average)" : "", test_attr.duration); + } + +free_ctx_out: + free(ctx_out); +free_ctx_in: + free(ctx_in); +free_data_out: + free(data_out); +free_data_in: + free(data_in); + + return err; +} + static int load_with_options(int argc, char **argv, bool first_prog_only) { - enum bpf_attach_type expected_attach_type; - struct bpf_object_open_attr attr = { - .prog_type = BPF_PROG_TYPE_UNSPEC, + struct bpf_object_load_attr load_attr = { 0 }; + struct bpf_object_open_attr open_attr = { + .prog_type = BPF_PROG_TYPE_UNSPEC, }; + enum bpf_attach_type expected_attach_type; struct map_replace *map_replace = NULL; struct bpf_program *prog = NULL, *pos; unsigned int old_map_fds = 0; @@ -767,7 +1107,7 @@ static int load_with_options(int argc, char **argv, bool first_prog_only) if (!REQ_ARGS(2)) return -1; - attr.file = GET_ARG(); + open_attr.file = GET_ARG(); pinfile = GET_ARG(); while (argc) { @@ -776,7 +1116,7 @@ static int load_with_options(int argc, char **argv, bool first_prog_only) NEXT_ARG(); - if (attr.prog_type != BPF_PROG_TYPE_UNSPEC) { + if (open_attr.prog_type != BPF_PROG_TYPE_UNSPEC) { p_err("program type already specified"); goto err_free_reuse_maps; } @@ -793,7 +1133,8 @@ static int load_with_options(int argc, char **argv, bool first_prog_only) strcat(type, *argv); strcat(type, "/"); - err = libbpf_prog_type_by_name(type, &attr.prog_type, + err = libbpf_prog_type_by_name(type, + &open_attr.prog_type, &expected_attach_type); free(type); if (err < 0) @@ -881,16 +1222,16 @@ static int load_with_options(int argc, char **argv, bool first_prog_only) set_max_rlimit(); - obj = __bpf_object__open_xattr(&attr, bpf_flags); + obj = __bpf_object__open_xattr(&open_attr, bpf_flags); if (IS_ERR_OR_NULL(obj)) { p_err("failed to open object file"); goto err_free_reuse_maps; } bpf_object__for_each_program(pos, obj) { - enum bpf_prog_type prog_type = attr.prog_type; + enum bpf_prog_type prog_type = open_attr.prog_type; - if (attr.prog_type == BPF_PROG_TYPE_UNSPEC) { + if (open_attr.prog_type == BPF_PROG_TYPE_UNSPEC) { const char *sec_name = bpf_program__title(pos, false); err = libbpf_prog_type_by_name(sec_name, &prog_type, @@ -960,7 +1301,12 @@ static int load_with_options(int argc, char **argv, bool first_prog_only) goto err_close_obj; } - err = bpf_object__load(obj); + load_attr.obj = obj; + if (verifier_logs) + /* log_level1 + log_level2 + stats, but not stable UAPI */ + load_attr.log_level = 1 + 2 + 4; + + err = bpf_object__load_xattr(&load_attr); if (err) { p_err("failed to load object file"); goto err_close_obj; @@ -1051,6 +1397,11 @@ static int do_help(int argc, char **argv) " [pinmaps MAP_DIR]\n" " %s %s attach PROG ATTACH_TYPE [MAP]\n" " %s %s detach PROG ATTACH_TYPE [MAP]\n" + " %s %s run PROG \\\n" + " data_in FILE \\\n" + " [data_out FILE [data_size_out L]] \\\n" + " [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n" + " [repeat N]\n" " %s %s tracelog\n" " %s %s help\n" "\n" @@ -1064,14 +1415,16 @@ static int do_help(int argc, char **argv) " cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n" " cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n" " cgroup/sendmsg4 | cgroup/sendmsg6 | cgroup/recvmsg4 |\n" - " cgroup/recvmsg6 }\n" + " cgroup/recvmsg6 | cgroup/getsockopt |\n" + " cgroup/setsockopt }\n" " ATTACH_TYPE := { msg_verdict | stream_verdict | stream_parser |\n" " flow_dissector }\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], bin_name, argv[-2]); + bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], + bin_name, argv[-2]); return 0; } @@ -1087,6 +1440,7 @@ static const struct cmd cmds[] = { { "attach", do_attach }, { "detach", do_detach }, { "tracelog", do_tracelog }, + { "run", do_run }, { 0 } }; diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c index 0bb17bf88b18..494d7ae3614d 100644 --- a/tools/bpf/bpftool/xlated_dumper.c +++ b/tools/bpf/bpftool/xlated_dumper.c @@ -31,9 +31,7 @@ void kernel_syms_load(struct dump_data *dd) if (!fp) return; - while (!feof(fp)) { - if (!fgets(buff, sizeof(buff), fp)) - break; + while (fgets(buff, sizeof(buff), fp)) { tmp = reallocarray(dd->sym_mapping, dd->sym_count + 1, sizeof(*dd->sym_mapping)); if (!tmp) { |