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 jsonw_end_object(json_wtr); 276 } 277 278 static void print_prog_plain(struct bpf_prog_info *info, int fd) 279 { 280 char *memlock; 281 282 printf("%u: ", info->id); 283 if (info->type < ARRAY_SIZE(prog_type_name)) 284 printf("%s ", prog_type_name[info->type]); 285 else 286 printf("type %u ", info->type); 287 288 if (*info->name) 289 printf("name %s ", info->name); 290 291 printf("tag "); 292 fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); 293 printf(" "); 294 295 if (info->status & BPF_PROG_STATUS_DEV_BOUND) { 296 printf("dev "); 297 if (info->ifindex) { 298 char name[IF_NAMESIZE]; 299 300 if (!if_indextoname(info->ifindex, name)) 301 printf("ifindex:%d ", info->ifindex); 302 else 303 printf("%s ", name); 304 } else { 305 printf("unknown "); 306 } 307 } 308 printf("\n"); 309 310 if (info->load_time) { 311 char buf[32]; 312 313 print_boot_time(info->load_time, buf, sizeof(buf)); 314 315 /* Piggy back on load_time, since 0 uid is a valid one */ 316 printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid); 317 } 318 319 printf("\txlated %uB", info->xlated_prog_len); 320 321 if (info->jited_prog_len) 322 printf(" jited %uB", info->jited_prog_len); 323 else 324 printf(" not jited"); 325 326 memlock = get_fdinfo(fd, "memlock"); 327 if (memlock) 328 printf(" memlock %sB", memlock); 329 free(memlock); 330 331 if (info->nr_map_ids) 332 show_prog_maps(fd, info->nr_map_ids); 333 334 printf("\n"); 335 } 336 337 static int show_prog(int fd) 338 { 339 struct bpf_prog_info info = {}; 340 __u32 len = sizeof(info); 341 int err; 342 343 err = bpf_obj_get_info_by_fd(fd, &info, &len); 344 if (err) { 345 p_err("can't get prog info: %s", strerror(errno)); 346 return -1; 347 } 348 349 if (json_output) 350 print_prog_json(&info, fd); 351 else 352 print_prog_plain(&info, fd); 353 354 return 0; 355 } 356 357 static int do_show(int argc, char **argv) 358 { 359 __u32 id = 0; 360 int err; 361 int fd; 362 363 if (argc == 2) { 364 fd = prog_parse_fd(&argc, &argv); 365 if (fd < 0) 366 return -1; 367 368 return show_prog(fd); 369 } 370 371 if (argc) 372 return BAD_ARG(); 373 374 if (json_output) 375 jsonw_start_array(json_wtr); 376 while (true) { 377 err = bpf_prog_get_next_id(id, &id); 378 if (err) { 379 if (errno == ENOENT) { 380 err = 0; 381 break; 382 } 383 p_err("can't get next program: %s%s", strerror(errno), 384 errno == EINVAL ? " -- kernel too old?" : ""); 385 err = -1; 386 break; 387 } 388 389 fd = bpf_prog_get_fd_by_id(id); 390 if (fd < 0) { 391 p_err("can't get prog by id (%u): %s", 392 id, strerror(errno)); 393 err = -1; 394 break; 395 } 396 397 err = show_prog(fd); 398 close(fd); 399 if (err) 400 break; 401 } 402 403 if (json_output) 404 jsonw_end_array(json_wtr); 405 406 return err; 407 } 408 409 static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) 410 { 411 va_list args; 412 413 va_start(args, fmt); 414 vprintf(fmt, args); 415 va_end(args); 416 } 417 418 static void dump_xlated_plain(void *buf, unsigned int len, bool opcodes) 419 { 420 struct bpf_insn *insn = buf; 421 bool double_insn = false; 422 unsigned int i; 423 424 for (i = 0; i < len / sizeof(*insn); i++) { 425 if (double_insn) { 426 double_insn = false; 427 continue; 428 } 429 430 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); 431 432 printf("% 4d: ", i); 433 print_bpf_insn(print_insn, NULL, insn + i, true); 434 435 if (opcodes) { 436 printf(" "); 437 fprint_hex(stdout, insn + i, 8, " "); 438 if (double_insn && i < len - 1) { 439 printf(" "); 440 fprint_hex(stdout, insn + i + 1, 8, " "); 441 } 442 printf("\n"); 443 } 444 } 445 } 446 447 static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...) 448 { 449 unsigned int l = strlen(fmt); 450 char chomped_fmt[l]; 451 va_list args; 452 453 va_start(args, fmt); 454 if (l > 0) { 455 strncpy(chomped_fmt, fmt, l - 1); 456 chomped_fmt[l - 1] = '\0'; 457 } 458 jsonw_vprintf_enquote(json_wtr, chomped_fmt, args); 459 va_end(args); 460 } 461 462 static void dump_xlated_json(void *buf, unsigned int len, bool opcodes) 463 { 464 struct bpf_insn *insn = buf; 465 bool double_insn = false; 466 unsigned int i; 467 468 jsonw_start_array(json_wtr); 469 for (i = 0; i < len / sizeof(*insn); i++) { 470 if (double_insn) { 471 double_insn = false; 472 continue; 473 } 474 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); 475 476 jsonw_start_object(json_wtr); 477 jsonw_name(json_wtr, "disasm"); 478 print_bpf_insn(print_insn_json, NULL, insn + i, true); 479 480 if (opcodes) { 481 jsonw_name(json_wtr, "opcodes"); 482 jsonw_start_object(json_wtr); 483 484 jsonw_name(json_wtr, "code"); 485 jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); 486 487 jsonw_name(json_wtr, "src_reg"); 488 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); 489 490 jsonw_name(json_wtr, "dst_reg"); 491 jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); 492 493 jsonw_name(json_wtr, "off"); 494 print_hex_data_json((uint8_t *)(&insn[i].off), 2); 495 496 jsonw_name(json_wtr, "imm"); 497 if (double_insn && i < len - 1) 498 print_hex_data_json((uint8_t *)(&insn[i].imm), 499 12); 500 else 501 print_hex_data_json((uint8_t *)(&insn[i].imm), 502 4); 503 jsonw_end_object(json_wtr); 504 } 505 jsonw_end_object(json_wtr); 506 } 507 jsonw_end_array(json_wtr); 508 } 509 510 static int do_dump(int argc, char **argv) 511 { 512 struct bpf_prog_info info = {}; 513 __u32 len = sizeof(info); 514 unsigned int buf_size; 515 char *filepath = NULL; 516 bool opcodes = false; 517 unsigned char *buf; 518 __u32 *member_len; 519 __u64 *member_ptr; 520 ssize_t n; 521 int err; 522 int fd; 523 524 if (is_prefix(*argv, "jited")) { 525 member_len = &info.jited_prog_len; 526 member_ptr = &info.jited_prog_insns; 527 } else if (is_prefix(*argv, "xlated")) { 528 member_len = &info.xlated_prog_len; 529 member_ptr = &info.xlated_prog_insns; 530 } else { 531 p_err("expected 'xlated' or 'jited', got: %s", *argv); 532 return -1; 533 } 534 NEXT_ARG(); 535 536 if (argc < 2) 537 usage(); 538 539 fd = prog_parse_fd(&argc, &argv); 540 if (fd < 0) 541 return -1; 542 543 if (is_prefix(*argv, "file")) { 544 NEXT_ARG(); 545 if (!argc) { 546 p_err("expected file path"); 547 return -1; 548 } 549 550 filepath = *argv; 551 NEXT_ARG(); 552 } else if (is_prefix(*argv, "opcodes")) { 553 opcodes = true; 554 NEXT_ARG(); 555 } 556 557 if (argc) { 558 usage(); 559 return -1; 560 } 561 562 err = bpf_obj_get_info_by_fd(fd, &info, &len); 563 if (err) { 564 p_err("can't get prog info: %s", strerror(errno)); 565 return -1; 566 } 567 568 if (!*member_len) { 569 p_info("no instructions returned"); 570 close(fd); 571 return 0; 572 } 573 574 buf_size = *member_len; 575 576 buf = malloc(buf_size); 577 if (!buf) { 578 p_err("mem alloc failed"); 579 close(fd); 580 return -1; 581 } 582 583 memset(&info, 0, sizeof(info)); 584 585 *member_ptr = ptr_to_u64(buf); 586 *member_len = buf_size; 587 588 err = bpf_obj_get_info_by_fd(fd, &info, &len); 589 close(fd); 590 if (err) { 591 p_err("can't get prog info: %s", strerror(errno)); 592 goto err_free; 593 } 594 595 if (*member_len > buf_size) { 596 p_err("too many instructions returned"); 597 goto err_free; 598 } 599 600 if (filepath) { 601 fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); 602 if (fd < 0) { 603 p_err("can't open file %s: %s", filepath, 604 strerror(errno)); 605 goto err_free; 606 } 607 608 n = write(fd, buf, *member_len); 609 close(fd); 610 if (n != *member_len) { 611 p_err("error writing output file: %s", 612 n < 0 ? strerror(errno) : "short write"); 613 goto err_free; 614 } 615 } else { 616 if (member_len == &info.jited_prog_len) 617 disasm_print_insn(buf, *member_len, opcodes); 618 else 619 if (json_output) 620 dump_xlated_json(buf, *member_len, opcodes); 621 else 622 dump_xlated_plain(buf, *member_len, opcodes); 623 } 624 625 free(buf); 626 627 return 0; 628 629 err_free: 630 free(buf); 631 return -1; 632 } 633 634 static int do_pin(int argc, char **argv) 635 { 636 int err; 637 638 err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id); 639 if (!err && json_output) 640 jsonw_null(json_wtr); 641 return err; 642 } 643 644 static int do_help(int argc, char **argv) 645 { 646 if (json_output) { 647 jsonw_null(json_wtr); 648 return 0; 649 } 650 651 fprintf(stderr, 652 "Usage: %s %s show [PROG]\n" 653 " %s %s dump xlated PROG [{ file FILE | opcodes }]\n" 654 " %s %s dump jited PROG [{ file FILE | opcodes }]\n" 655 " %s %s pin PROG FILE\n" 656 " %s %s help\n" 657 "\n" 658 " " HELP_SPEC_PROGRAM "\n" 659 " " HELP_SPEC_OPTIONS "\n" 660 "", 661 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 662 bin_name, argv[-2], bin_name, argv[-2]); 663 664 return 0; 665 } 666 667 static const struct cmd cmds[] = { 668 { "show", do_show }, 669 { "help", do_help }, 670 { "dump", do_dump }, 671 { "pin", do_pin }, 672 { 0 } 673 }; 674 675 int do_prog(int argc, char **argv) 676 { 677 return cmd_select(cmds, argc, argv, do_help); 678 } 679