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