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 p_err("can't get prog by id (%u): %s", 386 id, strerror(errno)); 387 err = -1; 388 break; 389 } 390 391 err = show_prog(fd); 392 close(fd); 393 if (err) 394 break; 395 } 396 397 if (json_output) 398 jsonw_end_array(json_wtr); 399 400 return err; 401 } 402 403 static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) 404 { 405 va_list args; 406 407 va_start(args, fmt); 408 vprintf(fmt, args); 409 va_end(args); 410 } 411 412 static void dump_xlated_plain(void *buf, unsigned int len, bool opcodes) 413 { 414 struct bpf_insn *insn = buf; 415 bool double_insn = false; 416 unsigned int i; 417 418 for (i = 0; i < len / sizeof(*insn); i++) { 419 if (double_insn) { 420 double_insn = false; 421 continue; 422 } 423 424 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); 425 426 printf("% 4d: ", i); 427 print_bpf_insn(print_insn, NULL, insn + i, true); 428 429 if (opcodes) { 430 printf(" "); 431 fprint_hex(stdout, insn + i, 8, " "); 432 if (double_insn && i < len - 1) { 433 printf(" "); 434 fprint_hex(stdout, insn + i + 1, 8, " "); 435 } 436 printf("\n"); 437 } 438 } 439 } 440 441 static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...) 442 { 443 unsigned int l = strlen(fmt); 444 char chomped_fmt[l]; 445 va_list args; 446 447 va_start(args, fmt); 448 if (l > 0) { 449 strncpy(chomped_fmt, fmt, l - 1); 450 chomped_fmt[l - 1] = '\0'; 451 } 452 jsonw_vprintf_enquote(json_wtr, chomped_fmt, args); 453 va_end(args); 454 } 455 456 static void dump_xlated_json(void *buf, unsigned int len, bool opcodes) 457 { 458 struct bpf_insn *insn = buf; 459 bool double_insn = false; 460 unsigned int i; 461 462 jsonw_start_array(json_wtr); 463 for (i = 0; i < len / sizeof(*insn); i++) { 464 if (double_insn) { 465 double_insn = false; 466 continue; 467 } 468 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); 469 470 jsonw_start_object(json_wtr); 471 jsonw_name(json_wtr, "disasm"); 472 print_bpf_insn(print_insn_json, NULL, insn + i, true); 473 474 if (opcodes) { 475 jsonw_name(json_wtr, "opcodes"); 476 jsonw_start_object(json_wtr); 477 478 jsonw_name(json_wtr, "code"); 479 jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); 480 481 jsonw_name(json_wtr, "src_reg"); 482 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); 483 484 jsonw_name(json_wtr, "dst_reg"); 485 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); 486 487 jsonw_name(json_wtr, "off"); 488 print_hex_data_json((uint8_t *)(&insn[i].off), 2); 489 490 jsonw_name(json_wtr, "imm"); 491 if (double_insn && i < len - 1) 492 print_hex_data_json((uint8_t *)(&insn[i].imm), 493 12); 494 else 495 print_hex_data_json((uint8_t *)(&insn[i].imm), 496 4); 497 jsonw_end_object(json_wtr); 498 } 499 jsonw_end_object(json_wtr); 500 } 501 jsonw_end_array(json_wtr); 502 } 503 504 static int do_dump(int argc, char **argv) 505 { 506 struct bpf_prog_info info = {}; 507 __u32 len = sizeof(info); 508 unsigned int buf_size; 509 char *filepath = NULL; 510 bool opcodes = false; 511 unsigned char *buf; 512 __u32 *member_len; 513 __u64 *member_ptr; 514 ssize_t n; 515 int err; 516 int fd; 517 518 if (is_prefix(*argv, "jited")) { 519 member_len = &info.jited_prog_len; 520 member_ptr = &info.jited_prog_insns; 521 } else if (is_prefix(*argv, "xlated")) { 522 member_len = &info.xlated_prog_len; 523 member_ptr = &info.xlated_prog_insns; 524 } else { 525 p_err("expected 'xlated' or 'jited', got: %s", *argv); 526 return -1; 527 } 528 NEXT_ARG(); 529 530 if (argc < 2) 531 usage(); 532 533 fd = prog_parse_fd(&argc, &argv); 534 if (fd < 0) 535 return -1; 536 537 if (is_prefix(*argv, "file")) { 538 NEXT_ARG(); 539 if (!argc) { 540 p_err("expected file path"); 541 return -1; 542 } 543 544 filepath = *argv; 545 NEXT_ARG(); 546 } else if (is_prefix(*argv, "opcodes")) { 547 opcodes = true; 548 NEXT_ARG(); 549 } 550 551 if (argc) { 552 usage(); 553 return -1; 554 } 555 556 err = bpf_obj_get_info_by_fd(fd, &info, &len); 557 if (err) { 558 p_err("can't get prog info: %s", strerror(errno)); 559 return -1; 560 } 561 562 if (!*member_len) { 563 p_info("no instructions returned"); 564 close(fd); 565 return 0; 566 } 567 568 buf_size = *member_len; 569 570 buf = malloc(buf_size); 571 if (!buf) { 572 p_err("mem alloc failed"); 573 close(fd); 574 return -1; 575 } 576 577 memset(&info, 0, sizeof(info)); 578 579 *member_ptr = ptr_to_u64(buf); 580 *member_len = buf_size; 581 582 err = bpf_obj_get_info_by_fd(fd, &info, &len); 583 close(fd); 584 if (err) { 585 p_err("can't get prog info: %s", strerror(errno)); 586 goto err_free; 587 } 588 589 if (*member_len > buf_size) { 590 p_err("too many instructions returned"); 591 goto err_free; 592 } 593 594 if (filepath) { 595 fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); 596 if (fd < 0) { 597 p_err("can't open file %s: %s", filepath, 598 strerror(errno)); 599 goto err_free; 600 } 601 602 n = write(fd, buf, *member_len); 603 close(fd); 604 if (n != *member_len) { 605 p_err("error writing output file: %s", 606 n < 0 ? strerror(errno) : "short write"); 607 goto err_free; 608 } 609 } else { 610 if (member_len == &info.jited_prog_len) 611 disasm_print_insn(buf, *member_len, opcodes); 612 else 613 if (json_output) 614 dump_xlated_json(buf, *member_len, opcodes); 615 else 616 dump_xlated_plain(buf, *member_len, opcodes); 617 } 618 619 free(buf); 620 621 return 0; 622 623 err_free: 624 free(buf); 625 return -1; 626 } 627 628 static int do_pin(int argc, char **argv) 629 { 630 int err; 631 632 err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id); 633 if (!err && json_output) 634 jsonw_null(json_wtr); 635 return err; 636 } 637 638 static int do_help(int argc, char **argv) 639 { 640 if (json_output) { 641 jsonw_null(json_wtr); 642 return 0; 643 } 644 645 fprintf(stderr, 646 "Usage: %s %s show [PROG]\n" 647 " %s %s dump xlated PROG [{ file FILE | opcodes }]\n" 648 " %s %s dump jited PROG [{ file FILE | opcodes }]\n" 649 " %s %s pin PROG FILE\n" 650 " %s %s help\n" 651 "\n" 652 " " HELP_SPEC_PROGRAM "\n" 653 " " HELP_SPEC_OPTIONS "\n" 654 "", 655 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 656 bin_name, argv[-2], bin_name, argv[-2]); 657 658 return 0; 659 } 660 661 static const struct cmd cmds[] = { 662 { "show", do_show }, 663 { "help", do_help }, 664 { "dump", do_dump }, 665 { "pin", do_pin }, 666 { 0 } 667 }; 668 669 int do_prog(int argc, char **argv) 670 { 671 return cmd_select(cmds, argc, argv, do_help); 672 } 673