blob: cc731475c74bb7567fc551fa4166c46636f97873 [file] [log] [blame]
Quentin Monnet49eb7ab2019-01-17 15:27:50 +00001// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2/* Copyright (c) 2019 Netronome Systems, Inc. */
3
Quentin Monnet1bf4b052019-01-17 15:27:53 +00004#include <ctype.h>
Quentin Monnet49eb7ab2019-01-17 15:27:50 +00005#include <errno.h>
6#include <string.h>
7#include <unistd.h>
8#include <sys/utsname.h>
Quentin Monnet7a4522b2019-01-17 15:27:51 +00009#include <sys/vfs.h>
Quentin Monnet49eb7ab2019-01-17 15:27:50 +000010
11#include <linux/filter.h>
12#include <linux/limits.h>
13
14#include <bpf.h>
Quentin Monnet1bf4b052019-01-17 15:27:53 +000015#include <libbpf.h>
Quentin Monnet49eb7ab2019-01-17 15:27:50 +000016
17#include "main.h"
18
Quentin Monnet7a4522b2019-01-17 15:27:51 +000019#ifndef PROC_SUPER_MAGIC
20# define PROC_SUPER_MAGIC 0x9fa0
21#endif
22
Quentin Monnet49eb7ab2019-01-17 15:27:50 +000023enum probe_component {
24 COMPONENT_UNSPEC,
25 COMPONENT_KERNEL,
26};
27
Quentin Monnet7a4522b2019-01-17 15:27:51 +000028/* Miscellaneous utility functions */
29
30static bool check_procfs(void)
31{
32 struct statfs st_fs;
33
34 if (statfs("/proc", &st_fs) < 0)
35 return false;
36 if ((unsigned long)st_fs.f_type != PROC_SUPER_MAGIC)
37 return false;
38
39 return true;
40}
41
Quentin Monnet49eb7ab2019-01-17 15:27:50 +000042/* Printing utility functions */
43
44static void
45print_bool_feature(const char *feat_name, const char *plain_name, bool res)
46{
47 if (json_output)
48 jsonw_bool_field(json_wtr, feat_name, res);
49 else
50 printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");
51}
52
Quentin Monnet4567b982019-01-17 15:27:52 +000053static void print_kernel_option(const char *name, const char *value)
54{
55 char *endptr;
56 int res;
57
58 if (json_output) {
59 if (!value) {
60 jsonw_null_field(json_wtr, name);
61 return;
62 }
63 errno = 0;
64 res = strtol(value, &endptr, 0);
65 if (!errno && *endptr == '\n')
66 jsonw_int_field(json_wtr, name, res);
67 else
68 jsonw_string_field(json_wtr, name, value);
69 } else {
70 if (value)
71 printf("%s is set to %s\n", name, value);
72 else
73 printf("%s is not set\n", name);
74 }
75}
76
Quentin Monnet49eb7ab2019-01-17 15:27:50 +000077static void
78print_start_section(const char *json_title, const char *plain_title)
79{
80 if (json_output) {
81 jsonw_name(json_wtr, json_title);
82 jsonw_start_object(json_wtr);
83 } else {
84 printf("%s\n", plain_title);
85 }
86}
87
Quentin Monnet1bf4b052019-01-17 15:27:53 +000088static 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
Quentin Monnet49eb7ab2019-01-17 15:27:50 +000099/* Probing functions */
100
Quentin Monnet7a4522b2019-01-17 15:27:51 +0000101static int read_procfs(const char *path)
102{
103 char *endptr, *line = NULL;
104 size_t len = 0;
105 FILE *fd;
106 int res;
107
108 fd = fopen(path, "r");
109 if (!fd)
110 return -1;
111
112 res = getline(&line, &len, fd);
113 fclose(fd);
114 if (res < 0)
115 return -1;
116
117 errno = 0;
118 res = strtol(line, &endptr, 10);
119 if (errno || *line == '\0' || *endptr != '\n')
120 res = -1;
121 free(line);
122
123 return res;
124}
125
126static void probe_unprivileged_disabled(void)
127{
128 int res;
129
130 res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled");
131 if (json_output) {
132 jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res);
133 } else {
134 switch (res) {
135 case 0:
136 printf("bpf() syscall for unprivileged users is enabled\n");
137 break;
138 case 1:
139 printf("bpf() syscall restricted to privileged users\n");
140 break;
141 case -1:
142 printf("Unable to retrieve required privileges for bpf() syscall\n");
143 break;
144 default:
145 printf("bpf() syscall restriction has unknown value %d\n", res);
146 }
147 }
148}
149
150static void probe_jit_enable(void)
151{
152 int res;
153
154 res = read_procfs("/proc/sys/net/core/bpf_jit_enable");
155 if (json_output) {
156 jsonw_int_field(json_wtr, "bpf_jit_enable", res);
157 } else {
158 switch (res) {
159 case 0:
160 printf("JIT compiler is disabled\n");
161 break;
162 case 1:
163 printf("JIT compiler is enabled\n");
164 break;
165 case 2:
166 printf("JIT compiler is enabled with debugging traces in kernel logs\n");
167 break;
168 case -1:
169 printf("Unable to retrieve JIT-compiler status\n");
170 break;
171 default:
172 printf("JIT-compiler status has unknown value %d\n",
173 res);
174 }
175 }
176}
177
178static void probe_jit_harden(void)
179{
180 int res;
181
182 res = read_procfs("/proc/sys/net/core/bpf_jit_harden");
183 if (json_output) {
184 jsonw_int_field(json_wtr, "bpf_jit_harden", res);
185 } else {
186 switch (res) {
187 case 0:
188 printf("JIT compiler hardening is disabled\n");
189 break;
190 case 1:
191 printf("JIT compiler hardening is enabled for unprivileged users\n");
192 break;
193 case 2:
194 printf("JIT compiler hardening is enabled for all users\n");
195 break;
196 case -1:
197 printf("Unable to retrieve JIT hardening status\n");
198 break;
199 default:
200 printf("JIT hardening status has unknown value %d\n",
201 res);
202 }
203 }
204}
205
206static void probe_jit_kallsyms(void)
207{
208 int res;
209
210 res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms");
211 if (json_output) {
212 jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res);
213 } else {
214 switch (res) {
215 case 0:
216 printf("JIT compiler kallsyms exports are disabled\n");
217 break;
218 case 1:
219 printf("JIT compiler kallsyms exports are enabled for root\n");
220 break;
221 case -1:
222 printf("Unable to retrieve JIT kallsyms export status\n");
223 break;
224 default:
225 printf("JIT kallsyms exports status has unknown value %d\n", res);
226 }
227 }
228}
229
230static void probe_jit_limit(void)
231{
232 int res;
233
234 res = read_procfs("/proc/sys/net/core/bpf_jit_limit");
235 if (json_output) {
236 jsonw_int_field(json_wtr, "bpf_jit_limit", res);
237 } else {
238 switch (res) {
239 case -1:
240 printf("Unable to retrieve global memory limit for JIT compiler for unprivileged users\n");
241 break;
242 default:
243 printf("Global memory limit for JIT compiler for unprivileged users is %d bytes\n", res);
244 }
245 }
246}
247
Quentin Monnet4567b982019-01-17 15:27:52 +0000248static char *get_kernel_config_option(FILE *fd, const char *option)
249{
250 size_t line_n = 0, optlen = strlen(option);
251 char *res, *strval, *line = NULL;
252 ssize_t n;
253
254 rewind(fd);
255 while ((n = getline(&line, &line_n, fd)) > 0) {
256 if (strncmp(line, option, optlen))
257 continue;
258 /* Check we have at least '=', value, and '\n' */
259 if (strlen(line) < optlen + 3)
260 continue;
261 if (*(line + optlen) != '=')
262 continue;
263
264 /* Trim ending '\n' */
265 line[strlen(line) - 1] = '\0';
266
267 /* Copy and return config option value */
268 strval = line + optlen + 1;
269 res = strdup(strval);
270 free(line);
271 return res;
272 }
273 free(line);
274
275 return NULL;
276}
277
278static void probe_kernel_image_config(void)
279{
280 static const char * const options[] = {
281 /* Enable BPF */
282 "CONFIG_BPF",
283 /* Enable bpf() syscall */
284 "CONFIG_BPF_SYSCALL",
285 /* Does selected architecture support eBPF JIT compiler */
286 "CONFIG_HAVE_EBPF_JIT",
287 /* Compile eBPF JIT compiler */
288 "CONFIG_BPF_JIT",
289 /* Avoid compiling eBPF interpreter (use JIT only) */
290 "CONFIG_BPF_JIT_ALWAYS_ON",
291
292 /* cgroups */
293 "CONFIG_CGROUPS",
294 /* BPF programs attached to cgroups */
295 "CONFIG_CGROUP_BPF",
296 /* bpf_get_cgroup_classid() helper */
297 "CONFIG_CGROUP_NET_CLASSID",
298 /* bpf_skb_{,ancestor_}cgroup_id() helpers */
299 "CONFIG_SOCK_CGROUP_DATA",
300
301 /* Tracing: attach BPF to kprobes, tracepoints, etc. */
302 "CONFIG_BPF_EVENTS",
303 /* Kprobes */
304 "CONFIG_KPROBE_EVENTS",
305 /* Uprobes */
306 "CONFIG_UPROBE_EVENTS",
307 /* Tracepoints */
308 "CONFIG_TRACING",
309 /* Syscall tracepoints */
310 "CONFIG_FTRACE_SYSCALLS",
311 /* bpf_override_return() helper support for selected arch */
312 "CONFIG_FUNCTION_ERROR_INJECTION",
313 /* bpf_override_return() helper */
314 "CONFIG_BPF_KPROBE_OVERRIDE",
315
316 /* Network */
317 "CONFIG_NET",
318 /* AF_XDP sockets */
319 "CONFIG_XDP_SOCKETS",
320 /* BPF_PROG_TYPE_LWT_* and related helpers */
321 "CONFIG_LWTUNNEL_BPF",
322 /* BPF_PROG_TYPE_SCHED_ACT, TC (traffic control) actions */
323 "CONFIG_NET_ACT_BPF",
324 /* BPF_PROG_TYPE_SCHED_CLS, TC filters */
325 "CONFIG_NET_CLS_BPF",
326 /* TC clsact qdisc */
327 "CONFIG_NET_CLS_ACT",
328 /* Ingress filtering with TC */
329 "CONFIG_NET_SCH_INGRESS",
330 /* bpf_skb_get_xfrm_state() helper */
331 "CONFIG_XFRM",
332 /* bpf_get_route_realm() helper */
333 "CONFIG_IP_ROUTE_CLASSID",
334 /* BPF_PROG_TYPE_LWT_SEG6_LOCAL and related helpers */
335 "CONFIG_IPV6_SEG6_BPF",
336 /* BPF_PROG_TYPE_LIRC_MODE2 and related helpers */
337 "CONFIG_BPF_LIRC_MODE2",
338 /* BPF stream parser and BPF socket maps */
339 "CONFIG_BPF_STREAM_PARSER",
340 /* xt_bpf module for passing BPF programs to netfilter */
341 "CONFIG_NETFILTER_XT_MATCH_BPF",
342 /* bpfilter back-end for iptables */
343 "CONFIG_BPFILTER",
344 /* bpftilter module with "user mode helper" */
345 "CONFIG_BPFILTER_UMH",
346
347 /* test_bpf module for BPF tests */
348 "CONFIG_TEST_BPF",
349 };
350 char *value, *buf = NULL;
351 struct utsname utsn;
352 char path[PATH_MAX];
353 size_t i, n;
354 ssize_t ret;
355 FILE *fd;
356
357 if (uname(&utsn))
358 goto no_config;
359
360 snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
361
362 fd = fopen(path, "r");
363 if (!fd && errno == ENOENT) {
364 /* Some distributions put the config file at /proc/config, give
365 * it a try.
366 * Sometimes it is also at /proc/config.gz but we do not try
367 * this one for now, it would require linking against libz.
368 */
369 fd = fopen("/proc/config", "r");
370 }
371 if (!fd) {
372 p_info("skipping kernel config, can't open file: %s",
373 strerror(errno));
374 goto no_config;
375 }
376 /* Sanity checks */
377 ret = getline(&buf, &n, fd);
378 ret = getline(&buf, &n, fd);
379 if (!buf || !ret) {
380 p_info("skipping kernel config, can't read from file: %s",
381 strerror(errno));
382 free(buf);
383 goto no_config;
384 }
385 if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) {
386 p_info("skipping kernel config, can't find correct file");
387 free(buf);
388 goto no_config;
389 }
390 free(buf);
391
392 for (i = 0; i < ARRAY_SIZE(options); i++) {
393 value = get_kernel_config_option(fd, options[i]);
394 print_kernel_option(options[i], value);
395 free(value);
396 }
397 fclose(fd);
398 return;
399
400no_config:
401 for (i = 0; i < ARRAY_SIZE(options); i++)
402 print_kernel_option(options[i], NULL);
403}
404
Quentin Monnet49eb7ab2019-01-17 15:27:50 +0000405static bool probe_bpf_syscall(void)
406{
407 bool res;
408
409 bpf_load_program(BPF_PROG_TYPE_UNSPEC, NULL, 0, NULL, 0, NULL, 0);
410 res = (errno != ENOSYS);
411
412 print_bool_feature("have_bpf_syscall",
413 "bpf() syscall",
414 res);
415
416 return res;
417}
418
Quentin Monnet1bf4b052019-01-17 15:27:53 +0000419static 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
Quentin Monnetf99e1662019-01-17 15:27:54 +0000441static void probe_map_type(enum bpf_map_type map_type)
442{
443 const char *plain_comment = "eBPF map_type ";
444 char feat_name[128], plain_desc[128];
445 size_t maxlen;
446 bool res;
447
448 res = bpf_probe_map_type(map_type, 0);
449
450 maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
451 if (strlen(map_type_name[map_type]) > maxlen) {
452 p_info("map type name too long");
453 return;
454 }
455
456 sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]);
457 sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]);
458 print_bool_feature(feat_name, plain_desc, res);
459}
460
Quentin Monnet49eb7ab2019-01-17 15:27:50 +0000461static int do_probe(int argc, char **argv)
462{
463 enum probe_component target = COMPONENT_UNSPEC;
Quentin Monnet1bf4b052019-01-17 15:27:53 +0000464 bool supported_types[128] = {};
465 unsigned int i;
Quentin Monnet49eb7ab2019-01-17 15:27:50 +0000466
467 /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).
468 * Let's approximate, and restrict usage to root user only.
469 */
470 if (geteuid()) {
471 p_err("please run this command as root user");
472 return -1;
473 }
474
475 set_max_rlimit();
476
477 while (argc) {
478 if (is_prefix(*argv, "kernel")) {
479 if (target != COMPONENT_UNSPEC) {
480 p_err("component to probe already specified");
481 return -1;
482 }
483 target = COMPONENT_KERNEL;
484 NEXT_ARG();
485 } else {
486 p_err("expected no more arguments, 'kernel', got: '%s'?",
487 *argv);
488 return -1;
489 }
490 }
491
492 if (json_output)
493 jsonw_start_object(json_wtr);
494
Quentin Monnet7a4522b2019-01-17 15:27:51 +0000495 switch (target) {
496 case COMPONENT_KERNEL:
497 case COMPONENT_UNSPEC:
498 print_start_section("system_config",
499 "Scanning system configuration...");
500 if (check_procfs()) {
501 probe_unprivileged_disabled();
502 probe_jit_enable();
503 probe_jit_harden();
504 probe_jit_kallsyms();
505 probe_jit_limit();
506 } else {
507 p_info("/* procfs not mounted, skipping related probes */");
508 }
Quentin Monnet4567b982019-01-17 15:27:52 +0000509 probe_kernel_image_config();
Quentin Monnet7a4522b2019-01-17 15:27:51 +0000510 if (json_output)
511 jsonw_end_object(json_wtr);
512 else
513 printf("\n");
514 break;
515 }
516
Quentin Monnet49eb7ab2019-01-17 15:27:50 +0000517 print_start_section("syscall_config",
518 "Scanning system call availability...");
519
Quentin Monnet1bf4b052019-01-17 15:27:53 +0000520 if (!probe_bpf_syscall())
521 /* bpf() syscall unavailable, don't probe other BPF features */
522 goto exit_close_json;
Quentin Monnet49eb7ab2019-01-17 15:27:50 +0000523
Quentin Monnet1bf4b052019-01-17 15:27:53 +0000524 print_end_then_start_section("program_types",
525 "Scanning eBPF program types...");
526
527 for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
528 probe_prog_type(i, supported_types);
529
Quentin Monnetf99e1662019-01-17 15:27:54 +0000530 print_end_then_start_section("map_types",
531 "Scanning eBPF map types...");
532
533 for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++)
534 probe_map_type(i);
535
Quentin Monnet1bf4b052019-01-17 15:27:53 +0000536exit_close_json:
Quentin Monnet49eb7ab2019-01-17 15:27:50 +0000537 if (json_output) {
538 /* End current "section" of probes */
539 jsonw_end_object(json_wtr);
540 /* End root object */
541 jsonw_end_object(json_wtr);
542 }
543
544 return 0;
545}
546
547static int do_help(int argc, char **argv)
548{
549 if (json_output) {
550 jsonw_null(json_wtr);
551 return 0;
552 }
553
554 fprintf(stderr,
555 "Usage: %s %s probe [kernel]\n"
556 " %s %s help\n"
557 "",
558 bin_name, argv[-2], bin_name, argv[-2]);
559
560 return 0;
561}
562
563static const struct cmd cmds[] = {
564 { "help", do_help },
565 { "probe", do_probe },
566 { 0 }
567};
568
569int do_feature(int argc, char **argv)
570{
571 return cmd_select(cmds, argc, argv, do_help);
572}