Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/bpf/bpftool/main.c
29281 views
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
22
const char *bin_name;
23
static int last_argc;
24
static char **last_argv;
25
static int (*last_do_help)(int argc, char **argv);
26
json_writer_t *json_wtr;
27
bool pretty_output;
28
bool json_output;
29
bool show_pinned;
30
bool block_mount;
31
bool verifier_logs;
32
bool relaxed_maps;
33
bool use_loader;
34
struct btf *base_btf;
35
struct hashmap *refs_table;
36
bool sign_progs;
37
const char *private_key_path;
38
const char *cert_path;
39
40
static void __noreturn clean_and_exit(int i)
41
{
42
if (json_output)
43
jsonw_destroy(&json_wtr);
44
45
exit(i);
46
}
47
48
void usage(void)
49
{
50
last_do_help(last_argc - 1, last_argv + 1);
51
52
clean_and_exit(-1);
53
}
54
55
static 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
76
static int do_batch(int argc, char **argv);
77
static int do_version(int argc, char **argv);
78
79
static 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
109
static void
110
print_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
118
static 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
191
int 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
219
bool 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 */
230
int 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
258
void 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. */
280
static 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
327
static 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
}
432
err_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
442
int 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
}
573
574