summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQuentin Monnet <quentin.monnet@netronome.com>2019-01-17 10:27:53 -0500
committerAlexei Starovoitov <ast@kernel.org>2019-01-23 01:15:40 -0500
commit1bf4b05810fe38c5f09973295e8d4234a4fd5d87 (patch)
tree37f5034cdadf718e85747da1997a95393e27f890
parent4567b983f78c8c496d09868a3b108fb496c8d2c9 (diff)
tools: bpftool: add probes for eBPF program types
Introduce probes for supported BPF program types in libbpf, and call it from bpftool to test what types are available on the system. The probe simply consists in loading a very basic program of that type and see if the verifier complains or not. Sample output: # bpftool feature probe kernel ... Scanning eBPF program types... eBPF program_type socket_filter is available eBPF program_type kprobe is available eBPF program_type sched_cls is available ... # bpftool --json --pretty feature probe kernel { ... "program_types": { "have_socket_filter_prog_type": true, "have_kprobe_prog_type": true, "have_sched_cls_prog_type": true, ... } } v5: - In libbpf.map, move global symbol to a new LIBBPF_0.0.2 section. - Rename (non-API function) prog_load() as probe_load(). v3: - Get kernel version for checking kprobes availability from libbpf instead of from bpftool. Do not pass kernel_version as an argument when calling libbpf probes. - Use a switch with all enum values for setting specific program parameters just before probing, so that gcc complains at compile time (-Wswitch-enum) if new prog types were added to the kernel but libbpf was not updated. - Add a comment in libbpf.h about setrlimit() usage to allow many consecutive probe attempts. v2: - Move probes from bpftool to libbpf. - Remove C-style macros output from this patch. Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Stanislav Fomichev <sdf@google.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-rw-r--r--tools/bpf/bpftool/feature.c48
-rw-r--r--tools/lib/bpf/Build2
-rw-r--r--tools/lib/bpf/libbpf.h11
-rw-r--r--tools/lib/bpf/libbpf.map5
-rw-r--r--tools/lib/bpf/libbpf_probes.c95
5 files changed, 159 insertions, 2 deletions
diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
index 4a2867439ab7..d6508dde4808 100644
--- a/tools/bpf/bpftool/feature.c
+++ b/tools/bpf/bpftool/feature.c
@@ -1,6 +1,7 @@
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2/* Copyright (c) 2019 Netronome Systems, Inc. */ 2/* Copyright (c) 2019 Netronome Systems, Inc. */
3 3
4#include <ctype.h>
4#include <errno.h> 5#include <errno.h>
5#include <string.h> 6#include <string.h>
6#include <unistd.h> 7#include <unistd.h>
@@ -11,6 +12,7 @@
11#include <linux/limits.h> 12#include <linux/limits.h>
12 13
13#include <bpf.h> 14#include <bpf.h>
15#include <libbpf.h>
14 16
15#include "main.h" 17#include "main.h"
16 18
@@ -83,6 +85,17 @@ print_start_section(const char *json_title, const char *plain_title)
83 } 85 }
84} 86}
85 87
88static void
89print_end_then_start_section(const char *json_title, const char *plain_title)
90{
91 if (json_output)
92 jsonw_end_object(json_wtr);
93 else
94 printf("\n");
95
96 print_start_section(json_title, plain_title);
97}
98
86/* Probing functions */ 99/* Probing functions */
87 100
88static int read_procfs(const char *path) 101static int read_procfs(const char *path)
@@ -403,9 +416,33 @@ static bool probe_bpf_syscall(void)
403 return res; 416 return res;
404} 417}
405 418
419static void probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types)
420{
421 const char *plain_comment = "eBPF program_type ";
422 char feat_name[128], plain_desc[128];
423 size_t maxlen;
424 bool res;
425
426 res = bpf_probe_prog_type(prog_type, 0);
427
428 supported_types[prog_type] |= res;
429
430 maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
431 if (strlen(prog_type_name[prog_type]) > maxlen) {
432 p_info("program type name too long");
433 return;
434 }
435
436 sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]);
437 sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]);
438 print_bool_feature(feat_name, plain_desc, res);
439}
440
406static int do_probe(int argc, char **argv) 441static int do_probe(int argc, char **argv)
407{ 442{
408 enum probe_component target = COMPONENT_UNSPEC; 443 enum probe_component target = COMPONENT_UNSPEC;
444 bool supported_types[128] = {};
445 unsigned int i;
409 446
410 /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN). 447 /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).
411 * Let's approximate, and restrict usage to root user only. 448 * Let's approximate, and restrict usage to root user only.
@@ -460,8 +497,17 @@ static int do_probe(int argc, char **argv)
460 print_start_section("syscall_config", 497 print_start_section("syscall_config",
461 "Scanning system call availability..."); 498 "Scanning system call availability...");
462 499
463 probe_bpf_syscall(); 500 if (!probe_bpf_syscall())
501 /* bpf() syscall unavailable, don't probe other BPF features */
502 goto exit_close_json;
503
504 print_end_then_start_section("program_types",
505 "Scanning eBPF program types...");
506
507 for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
508 probe_prog_type(i, supported_types);
464 509
510exit_close_json:
465 if (json_output) { 511 if (json_output) {
466 /* End current "section" of probes */ 512 /* End current "section" of probes */
467 jsonw_end_object(json_wtr); 513 jsonw_end_object(json_wtr);
diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build
index 197b40f5b5c6..bfd9bfc82c3b 100644
--- a/tools/lib/bpf/Build
+++ b/tools/lib/bpf/Build
@@ -1 +1 @@
libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o libbpf_probes.o
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 5f68d7b75215..8e63821109ab 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -355,6 +355,17 @@ LIBBPF_API const struct bpf_line_info *
355bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo, 355bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
356 __u32 insn_off, __u32 nr_skip); 356 __u32 insn_off, __u32 nr_skip);
357 357
358/*
359 * Probe for supported system features
360 *
361 * Note that running many of these probes in a short amount of time can cause
362 * the kernel to reach the maximal size of lockable memory allowed for the
363 * user, causing subsequent probes to fail. In this case, the caller may want
364 * to adjust that limit with setrlimit().
365 */
366LIBBPF_API bool bpf_probe_prog_type(enum bpf_prog_type prog_type,
367 __u32 ifindex);
368
358#ifdef __cplusplus 369#ifdef __cplusplus
359} /* extern "C" */ 370} /* extern "C" */
360#endif 371#endif
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index cd02cd4e2cc3..c7ec3ffa24e9 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -124,3 +124,8 @@ LIBBPF_0.0.1 {
124 local: 124 local:
125 *; 125 *;
126}; 126};
127
128LIBBPF_0.0.2 {
129 global:
130 bpf_probe_prog_type;
131} LIBBPF_0.0.1;
diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
new file mode 100644
index 000000000000..056c0c186f2a
--- /dev/null
+++ b/tools/lib/bpf/libbpf_probes.c
@@ -0,0 +1,95 @@
1// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2/* Copyright (c) 2019 Netronome Systems, Inc. */
3
4#include <errno.h>
5#include <unistd.h>
6#include <sys/utsname.h>
7
8#include <linux/filter.h>
9#include <linux/kernel.h>
10
11#include "bpf.h"
12#include "libbpf.h"
13
14static int get_kernel_version(void)
15{
16 int version, subversion, patchlevel;
17 struct utsname utsn;
18
19 /* Return 0 on failure, and attempt to probe with empty kversion */
20 if (uname(&utsn))
21 return 0;
22
23 if (sscanf(utsn.release, "%d.%d.%d",
24 &version, &subversion, &patchlevel) != 3)
25 return 0;
26
27 return (version << 16) + (subversion << 8) + patchlevel;
28}
29
30static void
31probe_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns,
32 size_t insns_cnt, char *buf, size_t buf_len, __u32 ifindex)
33{
34 struct bpf_load_program_attr xattr = {};
35 int fd;
36
37 switch (prog_type) {
38 case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
39 xattr.expected_attach_type = BPF_CGROUP_INET4_CONNECT;
40 break;
41 case BPF_PROG_TYPE_KPROBE:
42 xattr.kern_version = get_kernel_version();
43 break;
44 case BPF_PROG_TYPE_UNSPEC:
45 case BPF_PROG_TYPE_SOCKET_FILTER:
46 case BPF_PROG_TYPE_SCHED_CLS:
47 case BPF_PROG_TYPE_SCHED_ACT:
48 case BPF_PROG_TYPE_TRACEPOINT:
49 case BPF_PROG_TYPE_XDP:
50 case BPF_PROG_TYPE_PERF_EVENT:
51 case BPF_PROG_TYPE_CGROUP_SKB:
52 case BPF_PROG_TYPE_CGROUP_SOCK:
53 case BPF_PROG_TYPE_LWT_IN:
54 case BPF_PROG_TYPE_LWT_OUT:
55 case BPF_PROG_TYPE_LWT_XMIT:
56 case BPF_PROG_TYPE_SOCK_OPS:
57 case BPF_PROG_TYPE_SK_SKB:
58 case BPF_PROG_TYPE_CGROUP_DEVICE:
59 case BPF_PROG_TYPE_SK_MSG:
60 case BPF_PROG_TYPE_RAW_TRACEPOINT:
61 case BPF_PROG_TYPE_LWT_SEG6LOCAL:
62 case BPF_PROG_TYPE_LIRC_MODE2:
63 case BPF_PROG_TYPE_SK_REUSEPORT:
64 case BPF_PROG_TYPE_FLOW_DISSECTOR:
65 default:
66 break;
67 }
68
69 xattr.prog_type = prog_type;
70 xattr.insns = insns;
71 xattr.insns_cnt = insns_cnt;
72 xattr.license = "GPL";
73 xattr.prog_ifindex = ifindex;
74
75 fd = bpf_load_program_xattr(&xattr, buf, buf_len);
76 if (fd >= 0)
77 close(fd);
78}
79
80bool bpf_probe_prog_type(enum bpf_prog_type prog_type, __u32 ifindex)
81{
82 struct bpf_insn insns[2] = {
83 BPF_MOV64_IMM(BPF_REG_0, 0),
84 BPF_EXIT_INSN()
85 };
86
87 if (ifindex && prog_type == BPF_PROG_TYPE_SCHED_CLS)
88 /* nfp returns -EINVAL on exit(0) with TC offload */
89 insns[0].imm = 2;
90
91 errno = 0;
92 probe_load(prog_type, insns, ARRAY_SIZE(insns), NULL, 0, ifindex);
93
94 return errno != EINVAL && errno != EOPNOTSUPP;
95}