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