Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
3
4#include <ctype.h>
5#include <errno.h>
6#include <getopt.h>
7#include <linux/bpf.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include <bpf/bpf.h>
13#include <bpf/btf.h>
14#include <bpf/hashmap.h>
15#include <bpf/libbpf.h>
16
17#include "main.h"
18
19#define BATCH_LINE_LEN_MAX 65536
20#define BATCH_ARG_NB_MAX 4096
21
22const char *bin_name;
23static int last_argc;
24static char **last_argv;
25static int (*last_do_help)(int argc, char **argv);
26json_writer_t *json_wtr;
27bool pretty_output;
28bool json_output;
29bool show_pinned;
30bool block_mount;
31bool verifier_logs;
32bool relaxed_maps;
33bool use_loader;
34struct btf *base_btf;
35struct hashmap *refs_table;
36bool sign_progs;
37const char *private_key_path;
38const char *cert_path;
39
40static void __noreturn clean_and_exit(int i)
41{
42 if (json_output)
43 jsonw_destroy(&json_wtr);
44
45 exit(i);
46}
47
48void usage(void)
49{
50 last_do_help(last_argc - 1, last_argv + 1);
51
52 clean_and_exit(-1);
53}
54
55static int do_help(int argc, char **argv)
56{
57 if (json_output) {
58 jsonw_null(json_wtr);
59 return 0;
60 }
61
62 fprintf(stderr,
63 "Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n"
64 " %s batch file FILE\n"
65 " %s version\n"
66 "\n"
67 " OBJECT := { prog | map | link | cgroup | perf | net | feature | btf | gen | struct_ops | iter | token }\n"
68 " " HELP_SPEC_OPTIONS " |\n"
69 " {-V|--version} }\n"
70 "",
71 bin_name, bin_name, bin_name);
72
73 return 0;
74}
75
76static int do_batch(int argc, char **argv);
77static int do_version(int argc, char **argv);
78
79static const struct cmd commands[] = {
80 { "help", do_help },
81 { "batch", do_batch },
82 { "prog", do_prog },
83 { "map", do_map },
84 { "link", do_link },
85 { "cgroup", do_cgroup },
86 { "perf", do_perf },
87 { "net", do_net },
88 { "feature", do_feature },
89 { "btf", do_btf },
90 { "gen", do_gen },
91 { "struct_ops", do_struct_ops },
92 { "iter", do_iter },
93 { "token", do_token },
94 { "version", do_version },
95 { 0 }
96};
97
98#ifndef BPFTOOL_VERSION
99/* bpftool's major and minor version numbers are aligned on libbpf's. There is
100 * an offset of 6 for the version number, because bpftool's version was higher
101 * than libbpf's when we adopted this scheme. The patch number remains at 0
102 * for now. Set BPFTOOL_VERSION to override.
103 */
104#define BPFTOOL_MAJOR_VERSION (LIBBPF_MAJOR_VERSION + 6)
105#define BPFTOOL_MINOR_VERSION LIBBPF_MINOR_VERSION
106#define BPFTOOL_PATCH_VERSION 0
107#endif
108
109static void
110print_feature(const char *feature, bool state, unsigned int *nb_features)
111{
112 if (state) {
113 printf("%s %s", *nb_features ? "," : "", feature);
114 *nb_features = *nb_features + 1;
115 }
116}
117
118static int do_version(int argc, char **argv)
119{
120#ifdef HAVE_LIBBFD_SUPPORT
121 const bool has_libbfd = true;
122#else
123 const bool has_libbfd = false;
124#endif
125#ifdef HAVE_LLVM_SUPPORT
126 const bool has_llvm = true;
127#else
128 const bool has_llvm = false;
129#endif
130#ifdef BPFTOOL_WITHOUT_SKELETONS
131 const bool has_skeletons = false;
132#else
133 const bool has_skeletons = true;
134#endif
135 bool bootstrap = false;
136 int i;
137
138 for (i = 0; commands[i].cmd; i++) {
139 if (!strcmp(commands[i].cmd, "prog")) {
140 /* Assume we run a bootstrap version if "bpftool prog"
141 * is not available.
142 */
143 bootstrap = !commands[i].func;
144 break;
145 }
146 }
147
148 if (json_output) {
149 jsonw_start_object(json_wtr); /* root object */
150
151 jsonw_name(json_wtr, "version");
152#ifdef BPFTOOL_VERSION
153 jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION);
154#else
155 jsonw_printf(json_wtr, "\"%d.%d.%d\"", BPFTOOL_MAJOR_VERSION,
156 BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
157#endif
158 jsonw_name(json_wtr, "libbpf_version");
159 jsonw_printf(json_wtr, "\"%u.%u\"",
160 libbpf_major_version(), libbpf_minor_version());
161
162 jsonw_name(json_wtr, "features");
163 jsonw_start_object(json_wtr); /* features */
164 jsonw_bool_field(json_wtr, "libbfd", has_libbfd);
165 jsonw_bool_field(json_wtr, "llvm", has_llvm);
166 jsonw_bool_field(json_wtr, "skeletons", has_skeletons);
167 jsonw_bool_field(json_wtr, "bootstrap", bootstrap);
168 jsonw_end_object(json_wtr); /* features */
169
170 jsonw_end_object(json_wtr); /* root object */
171 } else {
172 unsigned int nb_features = 0;
173
174#ifdef BPFTOOL_VERSION
175 printf("%s v%s\n", bin_name, BPFTOOL_VERSION);
176#else
177 printf("%s v%d.%d.%d\n", bin_name, BPFTOOL_MAJOR_VERSION,
178 BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
179#endif
180 printf("using libbpf %s\n", libbpf_version_string());
181 printf("features:");
182 print_feature("libbfd", has_libbfd, &nb_features);
183 print_feature("llvm", has_llvm, &nb_features);
184 print_feature("skeletons", has_skeletons, &nb_features);
185 print_feature("bootstrap", bootstrap, &nb_features);
186 printf("\n");
187 }
188 return 0;
189}
190
191int cmd_select(const struct cmd *cmds, int argc, char **argv,
192 int (*help)(int argc, char **argv))
193{
194 unsigned int i;
195
196 last_argc = argc;
197 last_argv = argv;
198 last_do_help = help;
199
200 if (argc < 1 && cmds[0].func)
201 return cmds[0].func(argc, argv);
202
203 for (i = 0; cmds[i].cmd; i++) {
204 if (is_prefix(*argv, cmds[i].cmd)) {
205 if (!cmds[i].func) {
206 p_err("command '%s' is not supported in bootstrap mode",
207 cmds[i].cmd);
208 return -1;
209 }
210 return cmds[i].func(argc - 1, argv + 1);
211 }
212 }
213
214 help(argc - 1, argv + 1);
215
216 return -1;
217}
218
219bool is_prefix(const char *pfx, const char *str)
220{
221 if (!pfx)
222 return false;
223 if (strlen(str) < strlen(pfx))
224 return false;
225
226 return !memcmp(str, pfx, strlen(pfx));
227}
228
229/* Last argument MUST be NULL pointer */
230int detect_common_prefix(const char *arg, ...)
231{
232 unsigned int count = 0;
233 const char *ref;
234 char msg[256];
235 va_list ap;
236
237 snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg);
238 va_start(ap, arg);
239 while ((ref = va_arg(ap, const char *))) {
240 if (!is_prefix(arg, ref))
241 continue;
242 count++;
243 if (count > 1)
244 strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1);
245 strncat(msg, ref, sizeof(msg) - strlen(msg) - 1);
246 }
247 va_end(ap);
248 strncat(msg, "'", sizeof(msg) - strlen(msg) - 1);
249
250 if (count >= 2) {
251 p_err("%s", msg);
252 return -1;
253 }
254
255 return 0;
256}
257
258void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
259{
260 unsigned char *data = arg;
261 unsigned int i;
262
263 for (i = 0; i < n; i++) {
264 const char *pfx = "";
265
266 if (!i)
267 /* nothing */;
268 else if (!(i % 16))
269 fprintf(f, "\n");
270 else if (!(i % 8))
271 fprintf(f, " ");
272 else
273 pfx = sep;
274
275 fprintf(f, "%s%02hhx", i ? pfx : "", data[i]);
276 }
277}
278
279/* Split command line into argument vector. */
280static int make_args(char *line, char *n_argv[], int maxargs, int cmd_nb)
281{
282 static const char ws[] = " \t\r\n";
283 char *cp = line;
284 int n_argc = 0;
285
286 while (*cp) {
287 /* Skip leading whitespace. */
288 cp += strspn(cp, ws);
289
290 if (*cp == '\0')
291 break;
292
293 if (n_argc >= (maxargs - 1)) {
294 p_err("too many arguments to command %d", cmd_nb);
295 return -1;
296 }
297
298 /* Word begins with quote. */
299 if (*cp == '\'' || *cp == '"') {
300 char quote = *cp++;
301
302 n_argv[n_argc++] = cp;
303 /* Find ending quote. */
304 cp = strchr(cp, quote);
305 if (!cp) {
306 p_err("unterminated quoted string in command %d",
307 cmd_nb);
308 return -1;
309 }
310 } else {
311 n_argv[n_argc++] = cp;
312
313 /* Find end of word. */
314 cp += strcspn(cp, ws);
315 if (*cp == '\0')
316 break;
317 }
318
319 /* Separate words. */
320 *cp++ = 0;
321 }
322 n_argv[n_argc] = NULL;
323
324 return n_argc;
325}
326
327static int do_batch(int argc, char **argv)
328{
329 char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX];
330 char *n_argv[BATCH_ARG_NB_MAX];
331 unsigned int lines = 0;
332 int n_argc;
333 FILE *fp;
334 char *cp;
335 int err = 0;
336 int i;
337
338 if (argc < 2) {
339 p_err("too few parameters for batch");
340 return -1;
341 } else if (argc > 2) {
342 p_err("too many parameters for batch");
343 return -1;
344 } else if (!is_prefix(*argv, "file")) {
345 p_err("expected 'file', got: %s", *argv);
346 return -1;
347 }
348 NEXT_ARG();
349
350 if (!strcmp(*argv, "-"))
351 fp = stdin;
352 else
353 fp = fopen(*argv, "r");
354 if (!fp) {
355 p_err("Can't open file (%s): %s", *argv, strerror(errno));
356 return -1;
357 }
358
359 if (json_output)
360 jsonw_start_array(json_wtr);
361 while (fgets(buf, sizeof(buf), fp)) {
362 cp = strchr(buf, '#');
363 if (cp)
364 *cp = '\0';
365
366 if (strlen(buf) == sizeof(buf) - 1) {
367 errno = E2BIG;
368 break;
369 }
370
371 /* Append continuation lines if any (coming after a line ending
372 * with '\' in the batch file).
373 */
374 while ((cp = strstr(buf, "\\\n")) != NULL) {
375 if (!fgets(contline, sizeof(contline), fp) ||
376 strlen(contline) == 0) {
377 p_err("missing continuation line on command %u",
378 lines);
379 err = -1;
380 goto err_close;
381 }
382
383 cp = strchr(contline, '#');
384 if (cp)
385 *cp = '\0';
386
387 if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) {
388 p_err("command %u is too long", lines);
389 err = -1;
390 goto err_close;
391 }
392 buf[strlen(buf) - 2] = '\0';
393 strcat(buf, contline);
394 }
395
396 n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines);
397 if (!n_argc)
398 continue;
399 if (n_argc < 0) {
400 err = n_argc;
401 goto err_close;
402 }
403
404 if (json_output) {
405 jsonw_start_object(json_wtr);
406 jsonw_name(json_wtr, "command");
407 jsonw_start_array(json_wtr);
408 for (i = 0; i < n_argc; i++)
409 jsonw_string(json_wtr, n_argv[i]);
410 jsonw_end_array(json_wtr);
411 jsonw_name(json_wtr, "output");
412 }
413
414 err = cmd_select(commands, n_argc, n_argv, do_help);
415
416 if (json_output)
417 jsonw_end_object(json_wtr);
418
419 if (err)
420 goto err_close;
421
422 lines++;
423 }
424
425 if (errno && errno != ENOENT) {
426 p_err("reading batch file failed: %s", strerror(errno));
427 err = -1;
428 } else {
429 if (!json_output)
430 printf("processed %u commands\n", lines);
431 }
432err_close:
433 if (fp != stdin)
434 fclose(fp);
435
436 if (json_output)
437 jsonw_end_array(json_wtr);
438
439 return err;
440}
441
442int main(int argc, char **argv)
443{
444 static const struct option options[] = {
445 { "json", no_argument, NULL, 'j' },
446 { "help", no_argument, NULL, 'h' },
447 { "pretty", no_argument, NULL, 'p' },
448 { "version", no_argument, NULL, 'V' },
449 { "bpffs", no_argument, NULL, 'f' },
450 { "mapcompat", no_argument, NULL, 'm' },
451 { "nomount", no_argument, NULL, 'n' },
452 { "debug", no_argument, NULL, 'd' },
453 { "use-loader", no_argument, NULL, 'L' },
454 { "sign", no_argument, NULL, 'S' },
455 { "base-btf", required_argument, NULL, 'B' },
456 { 0 }
457 };
458 bool version_requested = false;
459 int opt, ret;
460
461 setlinebuf(stdout);
462
463#ifdef USE_LIBCAP
464 /* Libcap < 2.63 hooks before main() to compute the number of
465 * capabilities of the running kernel, and doing so it calls prctl()
466 * which may fail and set errno to non-zero.
467 * Let's reset errno to make sure this does not interfere with the
468 * batch mode.
469 */
470 errno = 0;
471#endif
472
473 last_do_help = do_help;
474 pretty_output = false;
475 json_output = false;
476 show_pinned = false;
477 block_mount = false;
478 bin_name = "bpftool";
479
480 opterr = 0;
481 while ((opt = getopt_long(argc, argv, "VhpjfLmndSi:k:B:l",
482 options, NULL)) >= 0) {
483 switch (opt) {
484 case 'V':
485 version_requested = true;
486 break;
487 case 'h':
488 return do_help(argc, argv);
489 case 'p':
490 pretty_output = true;
491 /* fall through */
492 case 'j':
493 if (!json_output) {
494 json_wtr = jsonw_new(stdout);
495 if (!json_wtr) {
496 p_err("failed to create JSON writer");
497 return -1;
498 }
499 json_output = true;
500 }
501 jsonw_pretty(json_wtr, pretty_output);
502 break;
503 case 'f':
504 show_pinned = true;
505 break;
506 case 'm':
507 relaxed_maps = true;
508 break;
509 case 'n':
510 block_mount = true;
511 break;
512 case 'd':
513 libbpf_set_print(print_all_levels);
514 verifier_logs = true;
515 break;
516 case 'B':
517 base_btf = btf__parse(optarg, NULL);
518 if (!base_btf) {
519 p_err("failed to parse base BTF at '%s': %d\n",
520 optarg, -errno);
521 return -1;
522 }
523 break;
524 case 'L':
525 use_loader = true;
526 break;
527 case 'S':
528 sign_progs = true;
529 use_loader = true;
530 break;
531 case 'k':
532 private_key_path = optarg;
533 break;
534 case 'i':
535 cert_path = optarg;
536 break;
537 default:
538 p_err("unrecognized option '%s'", argv[optind - 1]);
539 if (json_output)
540 clean_and_exit(-1);
541 else
542 usage();
543 }
544 }
545
546 argc -= optind;
547 argv += optind;
548 if (argc < 0)
549 usage();
550
551 if (sign_progs && (private_key_path == NULL || cert_path == NULL)) {
552 p_err("-i <identity_x509_cert> and -k <private_key> must be supplied with -S for signing");
553 return -EINVAL;
554 }
555
556 if (!sign_progs && (private_key_path != NULL || cert_path != NULL)) {
557 p_err("--sign (or -S) must be explicitly passed with -i <identity_x509_cert> and -k <private_key> to sign the programs");
558 return -EINVAL;
559 }
560
561 if (version_requested)
562 ret = do_version(argc, argv);
563 else
564 ret = cmd_select(commands, argc, argv, do_help);
565
566 if (json_output)
567 jsonw_destroy(&json_wtr);
568
569 btf__free(base_btf);
570
571 return ret;
572}