1 /* 2 * Copyright (C) 2017 Netronome Systems, Inc. 3 * 4 * This software is dual licensed under the GNU General License Version 2, 5 * June 1991 as shown in the file COPYING in the top-level directory of this 6 * source tree or the BSD 2-Clause License provided below. You have the 7 * option to license this software under the complete terms of either license. 8 * 9 * The BSD 2-Clause License: 10 * 11 * Redistribution and use in source and binary forms, with or 12 * without modification, are permitted provided that the following 13 * conditions are met: 14 * 15 * 1. Redistributions of source code must retain the above 16 * copyright notice, this list of conditions and the following 17 * disclaimer. 18 * 19 * 2. Redistributions in binary form must reproduce the above 20 * copyright notice, this list of conditions and the following 21 * disclaimer in the documentation and/or other materials 22 * provided with the distribution. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 * SOFTWARE. 32 */ 33 34 /* Author: Jakub Kicinski <kubakici@wp.pl> */ 35 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <stdarg.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <time.h> 43 #include <unistd.h> 44 #include <sys/types.h> 45 #include <sys/stat.h> 46 47 #include <bpf.h> 48 #include <libbpf.h> 49 50 #include "cfg.h" 51 #include "main.h" 52 #include "xlated_dumper.h" 53 54 static const char * const prog_type_name[] = { 55 [BPF_PROG_TYPE_UNSPEC] = "unspec", 56 [BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter", 57 [BPF_PROG_TYPE_KPROBE] = "kprobe", 58 [BPF_PROG_TYPE_SCHED_CLS] = "sched_cls", 59 [BPF_PROG_TYPE_SCHED_ACT] = "sched_act", 60 [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", 61 [BPF_PROG_TYPE_XDP] = "xdp", 62 [BPF_PROG_TYPE_PERF_EVENT] = "perf_event", 63 [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup_skb", 64 [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup_sock", 65 [BPF_PROG_TYPE_LWT_IN] = "lwt_in", 66 [BPF_PROG_TYPE_LWT_OUT] = "lwt_out", 67 [BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit", 68 [BPF_PROG_TYPE_SOCK_OPS] = "sock_ops", 69 [BPF_PROG_TYPE_SK_SKB] = "sk_skb", 70 [BPF_PROG_TYPE_CGROUP_DEVICE] = "cgroup_device", 71 [BPF_PROG_TYPE_SK_MSG] = "sk_msg", 72 [BPF_PROG_TYPE_RAW_TRACEPOINT] = "raw_tracepoint", 73 [BPF_PROG_TYPE_CGROUP_SOCK_ADDR] = "cgroup_sock_addr", 74 }; 75 76 static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) 77 { 78 struct timespec real_time_ts, boot_time_ts; 79 time_t wallclock_secs; 80 struct tm load_tm; 81 82 buf[--size] = '\0'; 83 84 if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || 85 clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { 86 perror("Can't read clocks"); 87 snprintf(buf, size, "%llu", nsecs / 1000000000); 88 return; 89 } 90 91 wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + 92 nsecs / 1000000000; 93 94 if (!localtime_r(&wallclock_secs, &load_tm)) { 95 snprintf(buf, size, "%llu", nsecs / 1000000000); 96 return; 97 } 98 99 if (json_output) 100 strftime(buf, size, "%s", &load_tm); 101 else 102 strftime(buf, size, "%FT%T%z", &load_tm); 103 } 104 105 static int prog_fd_by_tag(unsigned char *tag) 106 { 107 struct bpf_prog_info info = {}; 108 __u32 len = sizeof(info); 109 unsigned int id = 0; 110 int err; 111 int fd; 112 113 while (true) { 114 err = bpf_prog_get_next_id(id, &id); 115 if (err) { 116 p_err("%s", strerror(errno)); 117 return -1; 118 } 119 120 fd = bpf_prog_get_fd_by_id(id); 121 if (fd < 0) { 122 p_err("can't get prog by id (%u): %s", 123 id, strerror(errno)); 124 return -1; 125 } 126 127 err = bpf_obj_get_info_by_fd(fd, &info, &len); 128 if (err) { 129 p_err("can't get prog info (%u): %s", 130 id, strerror(errno)); 131 close(fd); 132 return -1; 133 } 134 135 if (!memcmp(tag, info.tag, BPF_TAG_SIZE)) 136 return fd; 137 138 close(fd); 139 } 140 } 141 142 int prog_parse_fd(int *argc, char ***argv) 143 { 144 int fd; 145 146 if (is_prefix(**argv, "id")) { 147 unsigned int id; 148 char *endptr; 149 150 NEXT_ARGP(); 151 152 id = strtoul(**argv, &endptr, 0); 153 if (*endptr) { 154 p_err("can't parse %s as ID", **argv); 155 return -1; 156 } 157 NEXT_ARGP(); 158 159 fd = bpf_prog_get_fd_by_id(id); 160 if (fd < 0) 161 p_err("get by id (%u): %s", id, strerror(errno)); 162 return fd; 163 } else if (is_prefix(**argv, "tag")) { 164 unsigned char tag[BPF_TAG_SIZE]; 165 166 NEXT_ARGP(); 167 168 if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2, 169 tag + 3, tag + 4, tag + 5, tag + 6, tag + 7) 170 != BPF_TAG_SIZE) { 171 p_err("can't parse tag"); 172 return -1; 173 } 174 NEXT_ARGP(); 175 176 return prog_fd_by_tag(tag); 177 } else if (is_prefix(**argv, "pinned")) { 178 char *path; 179 180 NEXT_ARGP(); 181 182 path = **argv; 183 NEXT_ARGP(); 184 185 return open_obj_pinned_any(path, BPF_OBJ_PROG); 186 } 187 188 p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv); 189 return -1; 190 } 191 192 static void show_prog_maps(int fd, u32 num_maps) 193 { 194 struct bpf_prog_info info = {}; 195 __u32 len = sizeof(info); 196 __u32 map_ids[num_maps]; 197 unsigned int i; 198 int err; 199 200 info.nr_map_ids = num_maps; 201 info.map_ids = ptr_to_u64(map_ids); 202 203 err = bpf_obj_get_info_by_fd(fd, &info, &len); 204 if (err || !info.nr_map_ids) 205 return; 206 207 if (json_output) { 208 jsonw_name(json_wtr, "map_ids"); 209 jsonw_start_array(json_wtr); 210 for (i = 0; i < info.nr_map_ids; i++) 211 jsonw_uint(json_wtr, map_ids[i]); 212 jsonw_end_array(json_wtr); 213 } else { 214 printf(" map_ids "); 215 for (i = 0; i < info.nr_map_ids; i++) 216 printf("%u%s", map_ids[i], 217 i == info.nr_map_ids - 1 ? "" : ","); 218 } 219 } 220 221 static void print_prog_json(struct bpf_prog_info *info, int fd) 222 { 223 char *memlock; 224 225 jsonw_start_object(json_wtr); 226 jsonw_uint_field(json_wtr, "id", info->id); 227 if (info->type < ARRAY_SIZE(prog_type_name)) 228 jsonw_string_field(json_wtr, "type", 229 prog_type_name[info->type]); 230 else 231 jsonw_uint_field(json_wtr, "type", info->type); 232 233 if (*info->name) 234 jsonw_string_field(json_wtr, "name", info->name); 235 236 jsonw_name(json_wtr, "tag"); 237 jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"", 238 info->tag[0], info->tag[1], info->tag[2], info->tag[3], 239 info->tag[4], info->tag[5], info->tag[6], info->tag[7]); 240 241 jsonw_bool_field(json_wtr, "gpl_compatible", info->gpl_compatible); 242 243 print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); 244 245 if (info->load_time) { 246 char buf[32]; 247 248 print_boot_time(info->load_time, buf, sizeof(buf)); 249 250 /* Piggy back on load_time, since 0 uid is a valid one */ 251 jsonw_name(json_wtr, "loaded_at"); 252 jsonw_printf(json_wtr, "%s", buf); 253 jsonw_uint_field(json_wtr, "uid", info->created_by_uid); 254 } 255 256 jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len); 257 258 if (info->jited_prog_len) { 259 jsonw_bool_field(json_wtr, "jited", true); 260 jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len); 261 } else { 262 jsonw_bool_field(json_wtr, "jited", false); 263 } 264 265 memlock = get_fdinfo(fd, "memlock"); 266 if (memlock) 267 jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); 268 free(memlock); 269 270 if (info->nr_map_ids) 271 show_prog_maps(fd, info->nr_map_ids); 272 273 if (!hash_empty(prog_table.table)) { 274 struct pinned_obj *obj; 275 276 jsonw_name(json_wtr, "pinned"); 277 jsonw_start_array(json_wtr); 278 hash_for_each_possible(prog_table.table, obj, hash, info->id) { 279 if (obj->id == info->id) 280 jsonw_string(json_wtr, obj->path); 281 } 282 jsonw_end_array(json_wtr); 283 } 284 285 jsonw_end_object(json_wtr); 286 } 287 288 static void print_prog_plain(struct bpf_prog_info *info, int fd) 289 { 290 char *memlock; 291 292 printf("%u: ", info->id); 293 if (info->type < ARRAY_SIZE(prog_type_name)) 294 printf("%s ", prog_type_name[info->type]); 295 else 296 printf("type %u ", info->type); 297 298 if (*info->name) 299 printf("name %s ", info->name); 300 301 printf("tag "); 302 fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); 303 print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino); 304 printf("%s", info->gpl_compatible ? " gpl" : ""); 305 printf("\n"); 306 307 if (info->load_time) { 308 char buf[32]; 309 310 print_boot_time(info->load_time, buf, sizeof(buf)); 311 312 /* Piggy back on load_time, since 0 uid is a valid one */ 313 printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid); 314 } 315 316 printf("\txlated %uB", info->xlated_prog_len); 317 318 if (info->jited_prog_len) 319 printf(" jited %uB", info->jited_prog_len); 320 else 321 printf(" not jited"); 322 323 memlock = get_fdinfo(fd, "memlock"); 324 if (memlock) 325 printf(" memlock %sB", memlock); 326 free(memlock); 327 328 if (info->nr_map_ids) 329 show_prog_maps(fd, info->nr_map_ids); 330 331 if (!hash_empty(prog_table.table)) { 332 struct pinned_obj *obj; 333 334 printf("\n"); 335 hash_for_each_possible(prog_table.table, obj, hash, info->id) { 336 if (obj->id == info->id) 337 printf("\tpinned %s\n", obj->path); 338 } 339 } 340 341 printf("\n"); 342 } 343 344 static int show_prog(int fd) 345 { 346 struct bpf_prog_info info = {}; 347 __u32 len = sizeof(info); 348 int err; 349 350 err = bpf_obj_get_info_by_fd(fd, &info, &len); 351 if (err) { 352 p_err("can't get prog info: %s", strerror(errno)); 353 return -1; 354 } 355 356 if (json_output) 357 print_prog_json(&info, fd); 358 else 359 print_prog_plain(&info, fd); 360 361 return 0; 362 } 363 364 static int do_show(int argc, char **argv) 365 { 366 __u32 id = 0; 367 int err; 368 int fd; 369 370 if (show_pinned) 371 build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); 372 373 if (argc == 2) { 374 fd = prog_parse_fd(&argc, &argv); 375 if (fd < 0) 376 return -1; 377 378 return show_prog(fd); 379 } 380 381 if (argc) 382 return BAD_ARG(); 383 384 if (json_output) 385 jsonw_start_array(json_wtr); 386 while (true) { 387 err = bpf_prog_get_next_id(id, &id); 388 if (err) { 389 if (errno == ENOENT) { 390 err = 0; 391 break; 392 } 393 p_err("can't get next program: %s%s", strerror(errno), 394 errno == EINVAL ? " -- kernel too old?" : ""); 395 err = -1; 396 break; 397 } 398 399 fd = bpf_prog_get_fd_by_id(id); 400 if (fd < 0) { 401 if (errno == ENOENT) 402 continue; 403 p_err("can't get prog by id (%u): %s", 404 id, strerror(errno)); 405 err = -1; 406 break; 407 } 408 409 err = show_prog(fd); 410 close(fd); 411 if (err) 412 break; 413 } 414 415 if (json_output) 416 jsonw_end_array(json_wtr); 417 418 return err; 419 } 420 421 static int do_dump(int argc, char **argv) 422 { 423 unsigned long *func_ksyms = NULL; 424 struct bpf_prog_info info = {}; 425 unsigned int *func_lens = NULL; 426 unsigned int nr_func_ksyms; 427 unsigned int nr_func_lens; 428 struct dump_data dd = {}; 429 __u32 len = sizeof(info); 430 unsigned int buf_size; 431 char *filepath = NULL; 432 bool opcodes = false; 433 bool visual = false; 434 unsigned char *buf; 435 __u32 *member_len; 436 __u64 *member_ptr; 437 ssize_t n; 438 int err; 439 int fd; 440 441 if (is_prefix(*argv, "jited")) { 442 member_len = &info.jited_prog_len; 443 member_ptr = &info.jited_prog_insns; 444 } else if (is_prefix(*argv, "xlated")) { 445 member_len = &info.xlated_prog_len; 446 member_ptr = &info.xlated_prog_insns; 447 } else { 448 p_err("expected 'xlated' or 'jited', got: %s", *argv); 449 return -1; 450 } 451 NEXT_ARG(); 452 453 if (argc < 2) 454 usage(); 455 456 fd = prog_parse_fd(&argc, &argv); 457 if (fd < 0) 458 return -1; 459 460 if (is_prefix(*argv, "file")) { 461 NEXT_ARG(); 462 if (!argc) { 463 p_err("expected file path"); 464 return -1; 465 } 466 467 filepath = *argv; 468 NEXT_ARG(); 469 } else if (is_prefix(*argv, "opcodes")) { 470 opcodes = true; 471 NEXT_ARG(); 472 } else if (is_prefix(*argv, "visual")) { 473 visual = true; 474 NEXT_ARG(); 475 } 476 477 if (argc) { 478 usage(); 479 return -1; 480 } 481 482 err = bpf_obj_get_info_by_fd(fd, &info, &len); 483 if (err) { 484 p_err("can't get prog info: %s", strerror(errno)); 485 return -1; 486 } 487 488 if (!*member_len) { 489 p_info("no instructions returned"); 490 close(fd); 491 return 0; 492 } 493 494 buf_size = *member_len; 495 496 buf = malloc(buf_size); 497 if (!buf) { 498 p_err("mem alloc failed"); 499 close(fd); 500 return -1; 501 } 502 503 nr_func_ksyms = info.nr_jited_ksyms; 504 if (nr_func_ksyms) { 505 func_ksyms = malloc(nr_func_ksyms * sizeof(__u64)); 506 if (!func_ksyms) { 507 p_err("mem alloc failed"); 508 close(fd); 509 goto err_free; 510 } 511 } 512 513 nr_func_lens = info.nr_jited_func_lens; 514 if (nr_func_lens) { 515 func_lens = malloc(nr_func_lens * sizeof(__u32)); 516 if (!func_lens) { 517 p_err("mem alloc failed"); 518 close(fd); 519 goto err_free; 520 } 521 } 522 523 memset(&info, 0, sizeof(info)); 524 525 *member_ptr = ptr_to_u64(buf); 526 *member_len = buf_size; 527 info.jited_ksyms = ptr_to_u64(func_ksyms); 528 info.nr_jited_ksyms = nr_func_ksyms; 529 info.jited_func_lens = ptr_to_u64(func_lens); 530 info.nr_jited_func_lens = nr_func_lens; 531 532 err = bpf_obj_get_info_by_fd(fd, &info, &len); 533 close(fd); 534 if (err) { 535 p_err("can't get prog info: %s", strerror(errno)); 536 goto err_free; 537 } 538 539 if (*member_len > buf_size) { 540 p_err("too many instructions returned"); 541 goto err_free; 542 } 543 544 if (info.nr_jited_ksyms > nr_func_ksyms) { 545 p_err("too many addresses returned"); 546 goto err_free; 547 } 548 549 if (info.nr_jited_func_lens > nr_func_lens) { 550 p_err("too many values returned"); 551 goto err_free; 552 } 553 554 if ((member_len == &info.jited_prog_len && 555 info.jited_prog_insns == 0) || 556 (member_len == &info.xlated_prog_len && 557 info.xlated_prog_insns == 0)) { 558 p_err("error retrieving insn dump: kernel.kptr_restrict set?"); 559 goto err_free; 560 } 561 562 if (filepath) { 563 fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); 564 if (fd < 0) { 565 p_err("can't open file %s: %s", filepath, 566 strerror(errno)); 567 goto err_free; 568 } 569 570 n = write(fd, buf, *member_len); 571 close(fd); 572 if (n != *member_len) { 573 p_err("error writing output file: %s", 574 n < 0 ? strerror(errno) : "short write"); 575 goto err_free; 576 } 577 578 if (json_output) 579 jsonw_null(json_wtr); 580 } else if (member_len == &info.jited_prog_len) { 581 const char *name = NULL; 582 583 if (info.ifindex) { 584 name = ifindex_to_bfd_name_ns(info.ifindex, 585 info.netns_dev, 586 info.netns_ino); 587 if (!name) 588 goto err_free; 589 } 590 591 if (info.nr_jited_func_lens && info.jited_func_lens) { 592 struct kernel_sym *sym = NULL; 593 char sym_name[SYM_MAX_NAME]; 594 unsigned char *img = buf; 595 __u64 *ksyms = NULL; 596 __u32 *lens; 597 __u32 i; 598 599 if (info.nr_jited_ksyms) { 600 kernel_syms_load(&dd); 601 ksyms = (__u64 *) info.jited_ksyms; 602 } 603 604 if (json_output) 605 jsonw_start_array(json_wtr); 606 607 lens = (__u32 *) info.jited_func_lens; 608 for (i = 0; i < info.nr_jited_func_lens; i++) { 609 if (ksyms) { 610 sym = kernel_syms_search(&dd, ksyms[i]); 611 if (sym) 612 sprintf(sym_name, "%s", sym->name); 613 else 614 sprintf(sym_name, "0x%016llx", ksyms[i]); 615 } else { 616 strcpy(sym_name, "unknown"); 617 } 618 619 if (json_output) { 620 jsonw_start_object(json_wtr); 621 jsonw_name(json_wtr, "name"); 622 jsonw_string(json_wtr, sym_name); 623 jsonw_name(json_wtr, "insns"); 624 } else { 625 printf("%s:\n", sym_name); 626 } 627 628 disasm_print_insn(img, lens[i], opcodes, name); 629 img += lens[i]; 630 631 if (json_output) 632 jsonw_end_object(json_wtr); 633 else 634 printf("\n"); 635 } 636 637 if (json_output) 638 jsonw_end_array(json_wtr); 639 } else { 640 disasm_print_insn(buf, *member_len, opcodes, name); 641 } 642 } else if (visual) { 643 if (json_output) 644 jsonw_null(json_wtr); 645 else 646 dump_xlated_cfg(buf, *member_len); 647 } else { 648 kernel_syms_load(&dd); 649 dd.nr_jited_ksyms = info.nr_jited_ksyms; 650 dd.jited_ksyms = (__u64 *) info.jited_ksyms; 651 652 if (json_output) 653 dump_xlated_json(&dd, buf, *member_len, opcodes); 654 else 655 dump_xlated_plain(&dd, buf, *member_len, opcodes); 656 kernel_syms_destroy(&dd); 657 } 658 659 free(buf); 660 free(func_ksyms); 661 free(func_lens); 662 return 0; 663 664 err_free: 665 free(buf); 666 free(func_ksyms); 667 free(func_lens); 668 return -1; 669 } 670 671 static int do_pin(int argc, char **argv) 672 { 673 int err; 674 675 err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id); 676 if (!err && json_output) 677 jsonw_null(json_wtr); 678 return err; 679 } 680 681 static int do_load(int argc, char **argv) 682 { 683 struct bpf_object *obj; 684 int prog_fd; 685 686 if (argc != 2) 687 usage(); 688 689 if (bpf_prog_load(argv[0], BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) { 690 p_err("failed to load program"); 691 return -1; 692 } 693 694 if (do_pin_fd(prog_fd, argv[1])) { 695 p_err("failed to pin program"); 696 return -1; 697 } 698 699 if (json_output) 700 jsonw_null(json_wtr); 701 702 return 0; 703 } 704 705 static int do_help(int argc, char **argv) 706 { 707 if (json_output) { 708 jsonw_null(json_wtr); 709 return 0; 710 } 711 712 fprintf(stderr, 713 "Usage: %s %s { show | list } [PROG]\n" 714 " %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n" 715 " %s %s dump jited PROG [{ file FILE | opcodes }]\n" 716 " %s %s pin PROG FILE\n" 717 " %s %s load OBJ FILE\n" 718 " %s %s help\n" 719 "\n" 720 " " HELP_SPEC_PROGRAM "\n" 721 " " HELP_SPEC_OPTIONS "\n" 722 "", 723 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 724 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]); 725 726 return 0; 727 } 728 729 static const struct cmd cmds[] = { 730 { "show", do_show }, 731 { "list", do_show }, 732 { "help", do_help }, 733 { "dump", do_dump }, 734 { "pin", do_pin }, 735 { "load", do_load }, 736 { 0 } 737 }; 738 739 int do_prog(int argc, char **argv) 740 { 741 return cmd_select(cmds, argc, argv, do_help); 742 } 743