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