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