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 #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 198 int 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 226 bool 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 */ 237 int 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 265 void 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. */ 287 static 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 334 static 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 } 439 err_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 449 int 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 } 580