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