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 <net/if.h> 45 #include <sys/types.h> 46 #include <sys/stat.h> 47 48 #include <bpf.h> 49 50 #include "main.h" 51 #include "disasm.h" 52 53 static const char * const prog_type_name[] = { 54 [BPF_PROG_TYPE_UNSPEC] = "unspec", 55 [BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter", 56 [BPF_PROG_TYPE_KPROBE] = "kprobe", 57 [BPF_PROG_TYPE_SCHED_CLS] = "sched_cls", 58 [BPF_PROG_TYPE_SCHED_ACT] = "sched_act", 59 [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", 60 [BPF_PROG_TYPE_XDP] = "xdp", 61 [BPF_PROG_TYPE_PERF_EVENT] = "perf_event", 62 [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup_skb", 63 [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup_sock", 64 [BPF_PROG_TYPE_LWT_IN] = "lwt_in", 65 [BPF_PROG_TYPE_LWT_OUT] = "lwt_out", 66 [BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit", 67 [BPF_PROG_TYPE_SOCK_OPS] = "sock_ops", 68 [BPF_PROG_TYPE_SK_SKB] = "sk_skb", 69 }; 70 71 static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) 72 { 73 struct timespec real_time_ts, boot_time_ts; 74 time_t wallclock_secs; 75 struct tm load_tm; 76 77 buf[--size] = '\0'; 78 79 if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || 80 clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { 81 perror("Can't read clocks"); 82 snprintf(buf, size, "%llu", nsecs / 1000000000); 83 return; 84 } 85 86 wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + 87 nsecs / 1000000000; 88 89 if (!localtime_r(&wallclock_secs, &load_tm)) { 90 snprintf(buf, size, "%llu", nsecs / 1000000000); 91 return; 92 } 93 94 strftime(buf, size, "%b %d/%H:%M", &load_tm); 95 } 96 97 static int prog_fd_by_tag(unsigned char *tag) 98 { 99 struct bpf_prog_info info = {}; 100 __u32 len = sizeof(info); 101 unsigned int id = 0; 102 int err; 103 int fd; 104 105 while (true) { 106 err = bpf_prog_get_next_id(id, &id); 107 if (err) { 108 p_err("%s", strerror(errno)); 109 return -1; 110 } 111 112 fd = bpf_prog_get_fd_by_id(id); 113 if (fd < 0) { 114 p_err("can't get prog by id (%u): %s", 115 id, strerror(errno)); 116 return -1; 117 } 118 119 err = bpf_obj_get_info_by_fd(fd, &info, &len); 120 if (err) { 121 p_err("can't get prog info (%u): %s", 122 id, strerror(errno)); 123 close(fd); 124 return -1; 125 } 126 127 if (!memcmp(tag, info.tag, BPF_TAG_SIZE)) 128 return fd; 129 130 close(fd); 131 } 132 } 133 134 int prog_parse_fd(int *argc, char ***argv) 135 { 136 int fd; 137 138 if (is_prefix(**argv, "id")) { 139 unsigned int id; 140 char *endptr; 141 142 NEXT_ARGP(); 143 144 id = strtoul(**argv, &endptr, 0); 145 if (*endptr) { 146 p_err("can't parse %s as ID", **argv); 147 return -1; 148 } 149 NEXT_ARGP(); 150 151 fd = bpf_prog_get_fd_by_id(id); 152 if (fd < 0) 153 p_err("get by id (%u): %s", id, strerror(errno)); 154 return fd; 155 } else if (is_prefix(**argv, "tag")) { 156 unsigned char tag[BPF_TAG_SIZE]; 157 158 NEXT_ARGP(); 159 160 if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2, 161 tag + 3, tag + 4, tag + 5, tag + 6, tag + 7) 162 != BPF_TAG_SIZE) { 163 p_err("can't parse tag"); 164 return -1; 165 } 166 NEXT_ARGP(); 167 168 return prog_fd_by_tag(tag); 169 } else if (is_prefix(**argv, "pinned")) { 170 char *path; 171 172 NEXT_ARGP(); 173 174 path = **argv; 175 NEXT_ARGP(); 176 177 return open_obj_pinned_any(path, BPF_OBJ_PROG); 178 } 179 180 p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv); 181 return -1; 182 } 183 184 static void show_prog_maps(int fd, u32 num_maps) 185 { 186 struct bpf_prog_info info = {}; 187 __u32 len = sizeof(info); 188 __u32 map_ids[num_maps]; 189 unsigned int i; 190 int err; 191 192 info.nr_map_ids = num_maps; 193 info.map_ids = ptr_to_u64(map_ids); 194 195 err = bpf_obj_get_info_by_fd(fd, &info, &len); 196 if (err || !info.nr_map_ids) 197 return; 198 199 if (json_output) { 200 jsonw_name(json_wtr, "map_ids"); 201 jsonw_start_array(json_wtr); 202 for (i = 0; i < info.nr_map_ids; i++) 203 jsonw_uint(json_wtr, map_ids[i]); 204 jsonw_end_array(json_wtr); 205 } else { 206 printf(" map_ids "); 207 for (i = 0; i < info.nr_map_ids; i++) 208 printf("%u%s", map_ids[i], 209 i == info.nr_map_ids - 1 ? "" : ","); 210 } 211 } 212 213 static void print_prog_json(struct bpf_prog_info *info, int fd) 214 { 215 char *memlock; 216 217 jsonw_start_object(json_wtr); 218 jsonw_uint_field(json_wtr, "id", info->id); 219 if (info->type < ARRAY_SIZE(prog_type_name)) 220 jsonw_string_field(json_wtr, "type", 221 prog_type_name[info->type]); 222 else 223 jsonw_uint_field(json_wtr, "type", info->type); 224 225 if (*info->name) 226 jsonw_string_field(json_wtr, "name", info->name); 227 228 jsonw_name(json_wtr, "tag"); 229 jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"", 230 info->tag[0], info->tag[1], info->tag[2], info->tag[3], 231 info->tag[4], info->tag[5], info->tag[6], info->tag[7]); 232 233 if (info->status & BPF_PROG_STATUS_DEV_BOUND) { 234 jsonw_name(json_wtr, "dev"); 235 if (info->ifindex) { 236 char name[IF_NAMESIZE]; 237 238 if (!if_indextoname(info->ifindex, name)) 239 jsonw_printf(json_wtr, "\"ifindex:%d\"", 240 info->ifindex); 241 else 242 jsonw_printf(json_wtr, "\"%s\"", name); 243 } else { 244 jsonw_printf(json_wtr, "\"unknown\""); 245 } 246 } 247 248 if (info->load_time) { 249 char buf[32]; 250 251 print_boot_time(info->load_time, buf, sizeof(buf)); 252 253 /* Piggy back on load_time, since 0 uid is a valid one */ 254 jsonw_string_field(json_wtr, "loaded_at", buf); 255 jsonw_uint_field(json_wtr, "uid", info->created_by_uid); 256 } 257 258 jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len); 259 260 if (info->jited_prog_len) { 261 jsonw_bool_field(json_wtr, "jited", true); 262 jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len); 263 } else { 264 jsonw_bool_field(json_wtr, "jited", false); 265 } 266 267 memlock = get_fdinfo(fd, "memlock"); 268 if (memlock) 269 jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); 270 free(memlock); 271 272 if (info->nr_map_ids) 273 show_prog_maps(fd, info->nr_map_ids); 274 275 if (!hash_empty(prog_table.table)) { 276 struct pinned_obj *obj; 277 278 jsonw_name(json_wtr, "pinned"); 279 jsonw_start_array(json_wtr); 280 hash_for_each_possible(prog_table.table, obj, hash, info->id) { 281 if (obj->id == info->id) 282 jsonw_string(json_wtr, obj->path); 283 } 284 jsonw_end_array(json_wtr); 285 } 286 287 jsonw_end_object(json_wtr); 288 } 289 290 static void print_prog_plain(struct bpf_prog_info *info, int fd) 291 { 292 char *memlock; 293 294 printf("%u: ", info->id); 295 if (info->type < ARRAY_SIZE(prog_type_name)) 296 printf("%s ", prog_type_name[info->type]); 297 else 298 printf("type %u ", info->type); 299 300 if (*info->name) 301 printf("name %s ", info->name); 302 303 printf("tag "); 304 fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); 305 printf(" "); 306 307 if (info->status & BPF_PROG_STATUS_DEV_BOUND) { 308 printf("dev "); 309 if (info->ifindex) { 310 char name[IF_NAMESIZE]; 311 312 if (!if_indextoname(info->ifindex, name)) 313 printf("ifindex:%d ", info->ifindex); 314 else 315 printf("%s ", name); 316 } else { 317 printf("unknown "); 318 } 319 } 320 printf("\n"); 321 322 if (info->load_time) { 323 char buf[32]; 324 325 print_boot_time(info->load_time, buf, sizeof(buf)); 326 327 /* Piggy back on load_time, since 0 uid is a valid one */ 328 printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid); 329 } 330 331 printf("\txlated %uB", info->xlated_prog_len); 332 333 if (info->jited_prog_len) 334 printf(" jited %uB", info->jited_prog_len); 335 else 336 printf(" not jited"); 337 338 memlock = get_fdinfo(fd, "memlock"); 339 if (memlock) 340 printf(" memlock %sB", memlock); 341 free(memlock); 342 343 if (info->nr_map_ids) 344 show_prog_maps(fd, info->nr_map_ids); 345 346 if (!hash_empty(prog_table.table)) { 347 struct pinned_obj *obj; 348 349 printf("\n"); 350 hash_for_each_possible(prog_table.table, obj, hash, info->id) { 351 if (obj->id == info->id) 352 printf("\tpinned %s\n", obj->path); 353 } 354 } 355 356 printf("\n"); 357 } 358 359 static int show_prog(int fd) 360 { 361 struct bpf_prog_info info = {}; 362 __u32 len = sizeof(info); 363 int err; 364 365 err = bpf_obj_get_info_by_fd(fd, &info, &len); 366 if (err) { 367 p_err("can't get prog info: %s", strerror(errno)); 368 return -1; 369 } 370 371 if (json_output) 372 print_prog_json(&info, fd); 373 else 374 print_prog_plain(&info, fd); 375 376 return 0; 377 } 378 379 static int do_show(int argc, char **argv) 380 { 381 __u32 id = 0; 382 int err; 383 int fd; 384 385 if (show_pinned) 386 build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); 387 388 if (argc == 2) { 389 fd = prog_parse_fd(&argc, &argv); 390 if (fd < 0) 391 return -1; 392 393 return show_prog(fd); 394 } 395 396 if (argc) 397 return BAD_ARG(); 398 399 if (json_output) 400 jsonw_start_array(json_wtr); 401 while (true) { 402 err = bpf_prog_get_next_id(id, &id); 403 if (err) { 404 if (errno == ENOENT) { 405 err = 0; 406 break; 407 } 408 p_err("can't get next program: %s%s", strerror(errno), 409 errno == EINVAL ? " -- kernel too old?" : ""); 410 err = -1; 411 break; 412 } 413 414 fd = bpf_prog_get_fd_by_id(id); 415 if (fd < 0) { 416 p_err("can't get prog by id (%u): %s", 417 id, strerror(errno)); 418 err = -1; 419 break; 420 } 421 422 err = show_prog(fd); 423 close(fd); 424 if (err) 425 break; 426 } 427 428 if (json_output) 429 jsonw_end_array(json_wtr); 430 431 return err; 432 } 433 434 static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) 435 { 436 va_list args; 437 438 va_start(args, fmt); 439 vprintf(fmt, args); 440 va_end(args); 441 } 442 443 static void dump_xlated_plain(void *buf, unsigned int len, bool opcodes) 444 { 445 struct bpf_insn *insn = buf; 446 bool double_insn = false; 447 unsigned int i; 448 449 for (i = 0; i < len / sizeof(*insn); i++) { 450 if (double_insn) { 451 double_insn = false; 452 continue; 453 } 454 455 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); 456 457 printf("% 4d: ", i); 458 print_bpf_insn(print_insn, NULL, insn + i, true); 459 460 if (opcodes) { 461 printf(" "); 462 fprint_hex(stdout, insn + i, 8, " "); 463 if (double_insn && i < len - 1) { 464 printf(" "); 465 fprint_hex(stdout, insn + i + 1, 8, " "); 466 } 467 printf("\n"); 468 } 469 } 470 } 471 472 static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...) 473 { 474 unsigned int l = strlen(fmt); 475 char chomped_fmt[l]; 476 va_list args; 477 478 va_start(args, fmt); 479 if (l > 0) { 480 strncpy(chomped_fmt, fmt, l - 1); 481 chomped_fmt[l - 1] = '\0'; 482 } 483 jsonw_vprintf_enquote(json_wtr, chomped_fmt, args); 484 va_end(args); 485 } 486 487 static void dump_xlated_json(void *buf, unsigned int len, bool opcodes) 488 { 489 struct bpf_insn *insn = buf; 490 bool double_insn = false; 491 unsigned int i; 492 493 jsonw_start_array(json_wtr); 494 for (i = 0; i < len / sizeof(*insn); i++) { 495 if (double_insn) { 496 double_insn = false; 497 continue; 498 } 499 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); 500 501 jsonw_start_object(json_wtr); 502 jsonw_name(json_wtr, "disasm"); 503 print_bpf_insn(print_insn_json, NULL, insn + i, true); 504 505 if (opcodes) { 506 jsonw_name(json_wtr, "opcodes"); 507 jsonw_start_object(json_wtr); 508 509 jsonw_name(json_wtr, "code"); 510 jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); 511 512 jsonw_name(json_wtr, "src_reg"); 513 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); 514 515 jsonw_name(json_wtr, "dst_reg"); 516 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); 517 518 jsonw_name(json_wtr, "off"); 519 print_hex_data_json((uint8_t *)(&insn[i].off), 2); 520 521 jsonw_name(json_wtr, "imm"); 522 if (double_insn && i < len - 1) 523 print_hex_data_json((uint8_t *)(&insn[i].imm), 524 12); 525 else 526 print_hex_data_json((uint8_t *)(&insn[i].imm), 527 4); 528 jsonw_end_object(json_wtr); 529 } 530 jsonw_end_object(json_wtr); 531 } 532 jsonw_end_array(json_wtr); 533 } 534 535 static int do_dump(int argc, char **argv) 536 { 537 struct bpf_prog_info info = {}; 538 __u32 len = sizeof(info); 539 unsigned int buf_size; 540 char *filepath = NULL; 541 bool opcodes = false; 542 unsigned char *buf; 543 __u32 *member_len; 544 __u64 *member_ptr; 545 ssize_t n; 546 int err; 547 int fd; 548 549 if (is_prefix(*argv, "jited")) { 550 member_len = &info.jited_prog_len; 551 member_ptr = &info.jited_prog_insns; 552 } else if (is_prefix(*argv, "xlated")) { 553 member_len = &info.xlated_prog_len; 554 member_ptr = &info.xlated_prog_insns; 555 } else { 556 p_err("expected 'xlated' or 'jited', got: %s", *argv); 557 return -1; 558 } 559 NEXT_ARG(); 560 561 if (argc < 2) 562 usage(); 563 564 fd = prog_parse_fd(&argc, &argv); 565 if (fd < 0) 566 return -1; 567 568 if (is_prefix(*argv, "file")) { 569 NEXT_ARG(); 570 if (!argc) { 571 p_err("expected file path"); 572 return -1; 573 } 574 575 filepath = *argv; 576 NEXT_ARG(); 577 } else if (is_prefix(*argv, "opcodes")) { 578 opcodes = true; 579 NEXT_ARG(); 580 } 581 582 if (argc) { 583 usage(); 584 return -1; 585 } 586 587 err = bpf_obj_get_info_by_fd(fd, &info, &len); 588 if (err) { 589 p_err("can't get prog info: %s", strerror(errno)); 590 return -1; 591 } 592 593 if (!*member_len) { 594 p_info("no instructions returned"); 595 close(fd); 596 return 0; 597 } 598 599 buf_size = *member_len; 600 601 buf = malloc(buf_size); 602 if (!buf) { 603 p_err("mem alloc failed"); 604 close(fd); 605 return -1; 606 } 607 608 memset(&info, 0, sizeof(info)); 609 610 *member_ptr = ptr_to_u64(buf); 611 *member_len = buf_size; 612 613 err = bpf_obj_get_info_by_fd(fd, &info, &len); 614 close(fd); 615 if (err) { 616 p_err("can't get prog info: %s", strerror(errno)); 617 goto err_free; 618 } 619 620 if (*member_len > buf_size) { 621 p_err("too many instructions returned"); 622 goto err_free; 623 } 624 625 if (filepath) { 626 fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); 627 if (fd < 0) { 628 p_err("can't open file %s: %s", filepath, 629 strerror(errno)); 630 goto err_free; 631 } 632 633 n = write(fd, buf, *member_len); 634 close(fd); 635 if (n != *member_len) { 636 p_err("error writing output file: %s", 637 n < 0 ? strerror(errno) : "short write"); 638 goto err_free; 639 } 640 } else { 641 if (member_len == &info.jited_prog_len) 642 disasm_print_insn(buf, *member_len, opcodes); 643 else 644 if (json_output) 645 dump_xlated_json(buf, *member_len, opcodes); 646 else 647 dump_xlated_plain(buf, *member_len, opcodes); 648 } 649 650 free(buf); 651 652 return 0; 653 654 err_free: 655 free(buf); 656 return -1; 657 } 658 659 static int do_pin(int argc, char **argv) 660 { 661 int err; 662 663 err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id); 664 if (!err && json_output) 665 jsonw_null(json_wtr); 666 return err; 667 } 668 669 static int do_help(int argc, char **argv) 670 { 671 if (json_output) { 672 jsonw_null(json_wtr); 673 return 0; 674 } 675 676 fprintf(stderr, 677 "Usage: %s %s show [PROG]\n" 678 " %s %s dump xlated PROG [{ file FILE | opcodes }]\n" 679 " %s %s dump jited PROG [{ file FILE | opcodes }]\n" 680 " %s %s pin PROG FILE\n" 681 " %s %s help\n" 682 "\n" 683 " " HELP_SPEC_PROGRAM "\n" 684 " " HELP_SPEC_OPTIONS "\n" 685 "", 686 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 687 bin_name, argv[-2], bin_name, argv[-2]); 688 689 return 0; 690 } 691 692 static const struct cmd cmds[] = { 693 { "show", do_show }, 694 { "help", do_help }, 695 { "dump", do_dump }, 696 { "pin", do_pin }, 697 { 0 } 698 }; 699 700 int do_prog(int argc, char **argv) 701 { 702 return cmd_select(cmds, argc, argv, do_help); 703 } 704