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