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#ifdef BPFTOOL_WITHOUT_CRYPTO
136 const bool has_crypto = false;
137#else
138 const bool has_crypto = true;
139#endif
140 bool bootstrap = false;
141 int i;
142
143 for (i = 0; commands[i].cmd; i++) {
144 if (!strcmp(commands[i].cmd, "prog")) {
145 /* Assume we run a bootstrap version if "bpftool prog"
146 * is not available.
147 */
148 bootstrap = !commands[i].func;
149 break;
150 }
151 }
152
153 if (json_output) {
154 jsonw_start_object(json_wtr); /* root object */
155
156 jsonw_name(json_wtr, "version");
157#ifdef BPFTOOL_VERSION
158 jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION);
159#else
160 jsonw_printf(json_wtr, "\"%d.%d.%d\"", BPFTOOL_MAJOR_VERSION,
161 BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
162#endif
163 jsonw_name(json_wtr, "libbpf_version");
164 jsonw_printf(json_wtr, "\"%u.%u\"",
165 libbpf_major_version(), libbpf_minor_version());
166
167 jsonw_name(json_wtr, "features");
168 jsonw_start_object(json_wtr); /* features */
169 jsonw_bool_field(json_wtr, "libbfd", has_libbfd);
170 jsonw_bool_field(json_wtr, "llvm", has_llvm);
171 jsonw_bool_field(json_wtr, "crypto", has_crypto);
172 jsonw_bool_field(json_wtr, "skeletons", has_skeletons);
173 jsonw_bool_field(json_wtr, "bootstrap", bootstrap);
174 jsonw_end_object(json_wtr); /* features */
175
176 jsonw_end_object(json_wtr); /* root object */
177 } else {
178 unsigned int nb_features = 0;
179
180#ifdef BPFTOOL_VERSION
181 printf("%s v%s\n", bin_name, BPFTOOL_VERSION);
182#else
183 printf("%s v%d.%d.%d\n", bin_name, BPFTOOL_MAJOR_VERSION,
184 BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
185#endif
186 printf("using libbpf %s\n", libbpf_version_string());
187 printf("features:");
188 print_feature("libbfd", has_libbfd, &nb_features);
189 print_feature("llvm", has_llvm, &nb_features);
190 print_feature("crypto", has_crypto, &nb_features);
191 print_feature("skeletons", has_skeletons, &nb_features);
192 print_feature("bootstrap", bootstrap, &nb_features);
193 printf("\n");
194 }
195 return 0;
196}
197
198int cmd_select(const struct cmd *cmds, int argc, char **argv,
199 int (*help)(int argc, char **argv))
200{
201 unsigned int i;
202
203 last_argc = argc;
204 last_argv = argv;
205 last_do_help = help;
206
207 if (argc < 1 && cmds[0].func)
208 return cmds[0].func(argc, argv);
209
210 for (i = 0; cmds[i].cmd; i++) {
211 if (is_prefix(*argv, cmds[i].cmd)) {
212 if (!cmds[i].func) {
213 p_err("command '%s' is not supported in bootstrap mode",
214 cmds[i].cmd);
215 return -1;
216 }
217 return cmds[i].func(argc - 1, argv + 1);
218 }
219 }
220
221 help(argc - 1, argv + 1);
222
223 return -1;
224}
225
226bool is_prefix(const char *pfx, const char *str)
227{
228 if (!pfx)
229 return false;
230 if (strlen(str) < strlen(pfx))
231 return false;
232
233 return !memcmp(str, pfx, strlen(pfx));
234}
235
236/* Last argument MUST be NULL pointer */
237int detect_common_prefix(const char *arg, ...)
238{
239 unsigned int count = 0;
240 const char *ref;
241 char msg[256];
242 va_list ap;
243
244 snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg);
245 va_start(ap, arg);
246 while ((ref = va_arg(ap, const char *))) {
247 if (!is_prefix(arg, ref))
248 continue;
249 count++;
250 if (count > 1)
251 strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1);
252 strncat(msg, ref, sizeof(msg) - strlen(msg) - 1);
253 }
254 va_end(ap);
255 strncat(msg, "'", sizeof(msg) - strlen(msg) - 1);
256
257 if (count >= 2) {
258 p_err("%s", msg);
259 return -1;
260 }
261
262 return 0;
263}
264
265void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
266{
267 unsigned char *data = arg;
268 unsigned int i;
269
270 for (i = 0; i < n; i++) {
271 const char *pfx = "";
272
273 if (!i)
274 /* nothing */;
275 else if (!(i % 16))
276 fprintf(f, "\n");
277 else if (!(i % 8))
278 fprintf(f, " ");
279 else
280 pfx = sep;
281
282 fprintf(f, "%s%02hhx", i ? pfx : "", data[i]);
283 }
284}
285
286/* Split command line into argument vector. */
287static int make_args(char *line, char *n_argv[], int maxargs, int cmd_nb)
288{
289 static const char ws[] = " \t\r\n";
290 char *cp = line;
291 int n_argc = 0;
292
293 while (*cp) {
294 /* Skip leading whitespace. */
295 cp += strspn(cp, ws);
296
297 if (*cp == '\0')
298 break;
299
300 if (n_argc >= (maxargs - 1)) {
301 p_err("too many arguments to command %d", cmd_nb);
302 return -1;
303 }
304
305 /* Word begins with quote. */
306 if (*cp == '\'' || *cp == '"') {
307 char quote = *cp++;
308
309 n_argv[n_argc++] = cp;
310 /* Find ending quote. */
311 cp = strchr(cp, quote);
312 if (!cp) {
313 p_err("unterminated quoted string in command %d",
314 cmd_nb);
315 return -1;
316 }
317 } else {
318 n_argv[n_argc++] = cp;
319
320 /* Find end of word. */
321 cp += strcspn(cp, ws);
322 if (*cp == '\0')
323 break;
324 }
325
326 /* Separate words. */
327 *cp++ = 0;
328 }
329 n_argv[n_argc] = NULL;
330
331 return n_argc;
332}
333
334static int do_batch(int argc, char **argv)
335{
336 char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX];
337 char *n_argv[BATCH_ARG_NB_MAX];
338 unsigned int lines = 0;
339 int n_argc;
340 FILE *fp;
341 char *cp;
342 int err = 0;
343 int i;
344
345 if (argc < 2) {
346 p_err("too few parameters for batch");
347 return -1;
348 } else if (argc > 2) {
349 p_err("too many parameters for batch");
350 return -1;
351 } else if (!is_prefix(*argv, "file")) {
352 p_err("expected 'file', got: %s", *argv);
353 return -1;
354 }
355 NEXT_ARG();
356
357 if (!strcmp(*argv, "-"))
358 fp = stdin;
359 else
360 fp = fopen(*argv, "r");
361 if (!fp) {
362 p_err("Can't open file (%s): %s", *argv, strerror(errno));
363 return -1;
364 }
365
366 if (json_output)
367 jsonw_start_array(json_wtr);
368 while (fgets(buf, sizeof(buf), fp)) {
369 cp = strchr(buf, '#');
370 if (cp)
371 *cp = '\0';
372
373 if (strlen(buf) == sizeof(buf) - 1) {
374 errno = E2BIG;
375 break;
376 }
377
378 /* Append continuation lines if any (coming after a line ending
379 * with '\' in the batch file).
380 */
381 while ((cp = strstr(buf, "\\\n")) != NULL) {
382 if (!fgets(contline, sizeof(contline), fp) ||
383 strlen(contline) == 0) {
384 p_err("missing continuation line on command %u",
385 lines);
386 err = -1;
387 goto err_close;
388 }
389
390 cp = strchr(contline, '#');
391 if (cp)
392 *cp = '\0';
393
394 if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) {
395 p_err("command %u is too long", lines);
396 err = -1;
397 goto err_close;
398 }
399 buf[strlen(buf) - 2] = '\0';
400 strcat(buf, contline);
401 }
402
403 n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines);
404 if (!n_argc)
405 continue;
406 if (n_argc < 0) {
407 err = n_argc;
408 goto err_close;
409 }
410
411 if (json_output) {
412 jsonw_start_object(json_wtr);
413 jsonw_name(json_wtr, "command");
414 jsonw_start_array(json_wtr);
415 for (i = 0; i < n_argc; i++)
416 jsonw_string(json_wtr, n_argv[i]);
417 jsonw_end_array(json_wtr);
418 jsonw_name(json_wtr, "output");
419 }
420
421 err = cmd_select(commands, n_argc, n_argv, do_help);
422
423 if (json_output)
424 jsonw_end_object(json_wtr);
425
426 if (err)
427 goto err_close;
428
429 lines++;
430 }
431
432 if (errno && errno != ENOENT) {
433 p_err("reading batch file failed: %s", strerror(errno));
434 err = -1;
435 } else {
436 if (!json_output)
437 printf("processed %u commands\n", lines);
438 }
439err_close:
440 if (fp != stdin)
441 fclose(fp);
442
443 if (json_output)
444 jsonw_end_array(json_wtr);
445
446 return err;
447}
448
449int main(int argc, char **argv)
450{
451 static const struct option options[] = {
452 { "json", no_argument, NULL, 'j' },
453 { "help", no_argument, NULL, 'h' },
454 { "pretty", no_argument, NULL, 'p' },
455 { "version", no_argument, NULL, 'V' },
456 { "bpffs", no_argument, NULL, 'f' },
457 { "mapcompat", no_argument, NULL, 'm' },
458 { "nomount", no_argument, NULL, 'n' },
459 { "debug", no_argument, NULL, 'd' },
460 { "use-loader", no_argument, NULL, 'L' },
461 { "sign", no_argument, NULL, 'S' },
462 { "base-btf", required_argument, NULL, 'B' },
463 { 0 }
464 };
465 bool version_requested = false;
466 int opt, ret;
467
468 setlinebuf(stdout);
469
470#ifdef USE_LIBCAP
471 /* Libcap < 2.63 hooks before main() to compute the number of
472 * capabilities of the running kernel, and doing so it calls prctl()
473 * which may fail and set errno to non-zero.
474 * Let's reset errno to make sure this does not interfere with the
475 * batch mode.
476 */
477 errno = 0;
478#endif
479
480 last_do_help = do_help;
481 pretty_output = false;
482 json_output = false;
483 show_pinned = false;
484 block_mount = false;
485 bin_name = "bpftool";
486
487 opterr = 0;
488 while ((opt = getopt_long(argc, argv, "VhpjfLmndSi:k:B:l",
489 options, NULL)) >= 0) {
490 switch (opt) {
491 case 'V':
492 version_requested = true;
493 break;
494 case 'h':
495 return do_help(argc, argv);
496 case 'p':
497 pretty_output = true;
498 /* fall through */
499 case 'j':
500 if (!json_output) {
501 json_wtr = jsonw_new(stdout);
502 if (!json_wtr) {
503 p_err("failed to create JSON writer");
504 return -1;
505 }
506 json_output = true;
507 }
508 jsonw_pretty(json_wtr, pretty_output);
509 break;
510 case 'f':
511 show_pinned = true;
512 break;
513 case 'm':
514 relaxed_maps = true;
515 break;
516 case 'n':
517 block_mount = true;
518 break;
519 case 'd':
520 libbpf_set_print(print_all_levels);
521 verifier_logs = true;
522 break;
523 case 'B':
524 base_btf = btf__parse(optarg, NULL);
525 if (!base_btf) {
526 p_err("failed to parse base BTF at '%s': %d\n",
527 optarg, -errno);
528 return -1;
529 }
530 break;
531 case 'L':
532 use_loader = true;
533 break;
534 case 'S':
535 sign_progs = true;
536 use_loader = true;
537 break;
538 case 'k':
539 private_key_path = optarg;
540 break;
541 case 'i':
542 cert_path = optarg;
543 break;
544 default:
545 p_err("unrecognized option '%s'", argv[optind - 1]);
546 if (json_output)
547 clean_and_exit(-1);
548 else
549 usage();
550 }
551 }
552
553 argc -= optind;
554 argv += optind;
555 if (argc < 0)
556 usage();
557
558 if (sign_progs && (private_key_path == NULL || cert_path == NULL)) {
559 p_err("-i <identity_x509_cert> and -k <private_key> must be supplied with -S for signing");
560 return -EINVAL;
561 }
562
563 if (!sign_progs && (private_key_path != NULL || cert_path != NULL)) {
564 p_err("--sign (or -S) must be explicitly passed with -i <identity_x509_cert> and -k <private_key> to sign the programs");
565 return -EINVAL;
566 }
567
568 if (version_requested)
569 ret = do_version(argc, argv);
570 else
571 ret = cmd_select(commands, argc, argv, do_help);
572
573 if (json_output)
574 jsonw_destroy(&json_wtr);
575
576 btf__free(base_btf);
577
578 return ret;
579}