1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 /* Copyright (C) 2017-2018 Netronome Systems, Inc. */ 3 4 #ifndef _GNU_SOURCE 5 #define _GNU_SOURCE 6 #endif 7 #include <assert.h> 8 #include <ctype.h> 9 #include <errno.h> 10 #include <fcntl.h> 11 #include <ftw.h> 12 #include <libgen.h> 13 #include <mntent.h> 14 #include <stdbool.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <unistd.h> 19 #include <net/if.h> 20 #include <sys/mount.h> 21 #include <sys/resource.h> 22 #include <sys/stat.h> 23 #include <sys/vfs.h> 24 25 #include <linux/filter.h> 26 #include <linux/limits.h> 27 #include <linux/magic.h> 28 #include <linux/unistd.h> 29 30 #include <bpf/bpf.h> 31 #include <bpf/hashmap.h> 32 #include <bpf/libbpf.h> /* libbpf_num_possible_cpus */ 33 #include <bpf/btf.h> 34 35 #include "main.h" 36 37 #ifndef BPF_FS_MAGIC 38 #define BPF_FS_MAGIC 0xcafe4a11 39 #endif 40 41 void p_err(const char *fmt, ...) 42 { 43 va_list ap; 44 45 va_start(ap, fmt); 46 if (json_output) { 47 jsonw_start_object(json_wtr); 48 jsonw_name(json_wtr, "error"); 49 jsonw_vprintf_enquote(json_wtr, fmt, ap); 50 jsonw_end_object(json_wtr); 51 } else { 52 fprintf(stderr, "Error: "); 53 vfprintf(stderr, fmt, ap); 54 fprintf(stderr, "\n"); 55 } 56 va_end(ap); 57 } 58 59 void p_info(const char *fmt, ...) 60 { 61 va_list ap; 62 63 if (json_output) 64 return; 65 66 va_start(ap, fmt); 67 vfprintf(stderr, fmt, ap); 68 fprintf(stderr, "\n"); 69 va_end(ap); 70 } 71 72 static bool is_bpffs(const char *path) 73 { 74 struct statfs st_fs; 75 76 if (statfs(path, &st_fs) < 0) 77 return false; 78 79 return (unsigned long)st_fs.f_type == BPF_FS_MAGIC; 80 } 81 82 /* Probe whether kernel switched from memlock-based (RLIMIT_MEMLOCK) to 83 * memcg-based memory accounting for BPF maps and programs. This was done in 84 * commit 97306be45fbe ("Merge branch 'switch to memcg-based memory 85 * accounting'"), in Linux 5.11. 86 * 87 * Libbpf also offers to probe for memcg-based accounting vs rlimit, but does 88 * so by checking for the availability of a given BPF helper and this has 89 * failed on some kernels with backports in the past, see commit 6b4384ff1088 90 * ("Revert "bpftool: Use libbpf 1.0 API mode instead of RLIMIT_MEMLOCK""). 91 * Instead, we can probe by lowering the process-based rlimit to 0, trying to 92 * load a BPF object, and resetting the rlimit. If the load succeeds then 93 * memcg-based accounting is supported. 94 * 95 * This would be too dangerous to do in the library, because multithreaded 96 * applications might attempt to load items while the rlimit is at 0. Given 97 * that bpftool is single-threaded, this is fine to do here. 98 */ 99 static bool known_to_need_rlimit(void) 100 { 101 struct rlimit rlim_init, rlim_cur_zero = {}; 102 struct bpf_insn insns[] = { 103 BPF_MOV64_IMM(BPF_REG_0, 0), 104 BPF_EXIT_INSN(), 105 }; 106 size_t insn_cnt = ARRAY_SIZE(insns); 107 union bpf_attr attr; 108 int prog_fd, err; 109 110 memset(&attr, 0, sizeof(attr)); 111 attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER; 112 attr.insns = ptr_to_u64(insns); 113 attr.insn_cnt = insn_cnt; 114 attr.license = ptr_to_u64("GPL"); 115 116 if (getrlimit(RLIMIT_MEMLOCK, &rlim_init)) 117 return false; 118 119 /* Drop the soft limit to zero. We maintain the hard limit to its 120 * current value, because lowering it would be a permanent operation 121 * for unprivileged users. 122 */ 123 rlim_cur_zero.rlim_max = rlim_init.rlim_max; 124 if (setrlimit(RLIMIT_MEMLOCK, &rlim_cur_zero)) 125 return false; 126 127 /* Do not use bpf_prog_load() from libbpf here, because it calls 128 * bump_rlimit_memlock(), interfering with the current probe. 129 */ 130 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); 131 err = errno; 132 133 /* reset soft rlimit to its initial value */ 134 setrlimit(RLIMIT_MEMLOCK, &rlim_init); 135 136 if (prog_fd < 0) 137 return err == EPERM; 138 139 close(prog_fd); 140 return false; 141 } 142 143 void set_max_rlimit(void) 144 { 145 struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; 146 147 if (known_to_need_rlimit()) 148 setrlimit(RLIMIT_MEMLOCK, &rinf); 149 } 150 151 static int 152 mnt_fs(const char *target, const char *type, char *buff, size_t bufflen) 153 { 154 bool bind_done = false; 155 156 while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) { 157 if (errno != EINVAL || bind_done) { 158 snprintf(buff, bufflen, 159 "mount --make-private %s failed: %s", 160 target, strerror(errno)); 161 return -1; 162 } 163 164 if (mount(target, target, "none", MS_BIND, NULL)) { 165 snprintf(buff, bufflen, 166 "mount --bind %s %s failed: %s", 167 target, target, strerror(errno)); 168 return -1; 169 } 170 171 bind_done = true; 172 } 173 174 if (mount(type, target, type, 0, "mode=0700")) { 175 snprintf(buff, bufflen, "mount -t %s %s %s failed: %s", 176 type, type, target, strerror(errno)); 177 return -1; 178 } 179 180 return 0; 181 } 182 183 int mount_tracefs(const char *target) 184 { 185 char err_str[ERR_MAX_LEN]; 186 int err; 187 188 err = mnt_fs(target, "tracefs", err_str, ERR_MAX_LEN); 189 if (err) { 190 err_str[ERR_MAX_LEN - 1] = '\0'; 191 p_err("can't mount tracefs: %s", err_str); 192 } 193 194 return err; 195 } 196 197 int open_obj_pinned(const char *path, bool quiet, 198 const struct bpf_obj_get_opts *opts) 199 { 200 char *pname; 201 int fd = -1; 202 203 pname = strdup(path); 204 if (!pname) { 205 if (!quiet) 206 p_err("mem alloc failed"); 207 goto out_ret; 208 } 209 210 fd = bpf_obj_get_opts(pname, opts); 211 if (fd < 0) { 212 if (!quiet) 213 p_err("bpf obj get (%s): %s", pname, 214 errno == EACCES && !is_bpffs(dirname(pname)) ? 215 "directory not in bpf file system (bpffs)" : 216 strerror(errno)); 217 goto out_free; 218 } 219 220 out_free: 221 free(pname); 222 out_ret: 223 return fd; 224 } 225 226 int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type, 227 const struct bpf_obj_get_opts *opts) 228 { 229 enum bpf_obj_type type; 230 int fd; 231 232 fd = open_obj_pinned(path, false, opts); 233 if (fd < 0) 234 return -1; 235 236 type = get_fd_type(fd); 237 if (type < 0) { 238 close(fd); 239 return type; 240 } 241 if (type != exp_type) { 242 p_err("incorrect object type: %s", get_fd_type_name(type)); 243 close(fd); 244 return -1; 245 } 246 247 return fd; 248 } 249 250 int create_and_mount_bpffs_dir(const char *dir_name) 251 { 252 char err_str[ERR_MAX_LEN]; 253 bool dir_exists; 254 int err = 0; 255 256 if (is_bpffs(dir_name)) 257 return err; 258 259 dir_exists = access(dir_name, F_OK) == 0; 260 261 if (!dir_exists) { 262 char *temp_name; 263 char *parent_name; 264 265 temp_name = strdup(dir_name); 266 if (!temp_name) { 267 p_err("mem alloc failed"); 268 return -1; 269 } 270 271 parent_name = dirname(temp_name); 272 273 if (is_bpffs(parent_name)) { 274 /* nothing to do if already mounted */ 275 free(temp_name); 276 return err; 277 } 278 279 if (access(parent_name, F_OK) == -1) { 280 p_err("can't create dir '%s' to pin BPF object: parent dir '%s' doesn't exist", 281 dir_name, parent_name); 282 free(temp_name); 283 return -1; 284 } 285 286 free(temp_name); 287 } 288 289 if (block_mount) { 290 p_err("no BPF file system found, not mounting it due to --nomount option"); 291 return -1; 292 } 293 294 if (!dir_exists) { 295 err = mkdir(dir_name, S_IRWXU); 296 if (err) { 297 p_err("failed to create dir '%s': %s", dir_name, strerror(errno)); 298 return err; 299 } 300 } 301 302 err = mnt_fs(dir_name, "bpf", err_str, ERR_MAX_LEN); 303 if (err) { 304 err_str[ERR_MAX_LEN - 1] = '\0'; 305 p_err("can't mount BPF file system on given dir '%s': %s", 306 dir_name, err_str); 307 308 if (!dir_exists) 309 rmdir(dir_name); 310 } 311 312 return err; 313 } 314 315 int mount_bpffs_for_file(const char *file_name) 316 { 317 char err_str[ERR_MAX_LEN]; 318 char *temp_name; 319 char *dir; 320 int err = 0; 321 322 if (access(file_name, F_OK) != -1) { 323 p_err("can't pin BPF object: path '%s' already exists", file_name); 324 return -1; 325 } 326 327 temp_name = strdup(file_name); 328 if (!temp_name) { 329 p_err("mem alloc failed"); 330 return -1; 331 } 332 333 dir = dirname(temp_name); 334 335 if (is_bpffs(dir)) 336 /* nothing to do if already mounted */ 337 goto out_free; 338 339 if (access(dir, F_OK) == -1) { 340 p_err("can't pin BPF object: dir '%s' doesn't exist", dir); 341 err = -1; 342 goto out_free; 343 } 344 345 if (block_mount) { 346 p_err("no BPF file system found, not mounting it due to --nomount option"); 347 err = -1; 348 goto out_free; 349 } 350 351 err = mnt_fs(dir, "bpf", err_str, ERR_MAX_LEN); 352 if (err) { 353 err_str[ERR_MAX_LEN - 1] = '\0'; 354 p_err("can't mount BPF file system to pin the object '%s': %s", 355 file_name, err_str); 356 } 357 358 out_free: 359 free(temp_name); 360 return err; 361 } 362 363 int do_pin_fd(int fd, const char *name) 364 { 365 int err; 366 367 err = mount_bpffs_for_file(name); 368 if (err) 369 return err; 370 371 err = bpf_obj_pin(fd, name); 372 if (err) 373 p_err("can't pin the object (%s): %s", name, strerror(errno)); 374 375 return err; 376 } 377 378 int do_pin_any(int argc, char **argv, int (*get_fd)(int *, char ***)) 379 { 380 int err; 381 int fd; 382 383 if (!REQ_ARGS(3)) 384 return -EINVAL; 385 386 fd = get_fd(&argc, &argv); 387 if (fd < 0) 388 return fd; 389 390 err = do_pin_fd(fd, *argv); 391 392 close(fd); 393 return err; 394 } 395 396 const char *get_fd_type_name(enum bpf_obj_type type) 397 { 398 static const char * const names[] = { 399 [BPF_OBJ_UNKNOWN] = "unknown", 400 [BPF_OBJ_PROG] = "prog", 401 [BPF_OBJ_MAP] = "map", 402 [BPF_OBJ_LINK] = "link", 403 }; 404 405 if (type < 0 || type >= ARRAY_SIZE(names) || !names[type]) 406 return names[BPF_OBJ_UNKNOWN]; 407 408 return names[type]; 409 } 410 411 void get_prog_full_name(const struct bpf_prog_info *prog_info, int prog_fd, 412 char *name_buff, size_t buff_len) 413 { 414 const char *prog_name = prog_info->name; 415 const struct btf_type *func_type; 416 struct bpf_func_info finfo = {}; 417 struct bpf_prog_info info = {}; 418 __u32 info_len = sizeof(info); 419 struct btf *prog_btf = NULL; 420 421 if (buff_len <= BPF_OBJ_NAME_LEN || 422 strlen(prog_info->name) < BPF_OBJ_NAME_LEN - 1) 423 goto copy_name; 424 425 if (!prog_info->btf_id || prog_info->nr_func_info == 0) 426 goto copy_name; 427 428 info.nr_func_info = 1; 429 info.func_info_rec_size = prog_info->func_info_rec_size; 430 if (info.func_info_rec_size > sizeof(finfo)) 431 info.func_info_rec_size = sizeof(finfo); 432 info.func_info = ptr_to_u64(&finfo); 433 434 if (bpf_prog_get_info_by_fd(prog_fd, &info, &info_len)) 435 goto copy_name; 436 437 prog_btf = btf__load_from_kernel_by_id(info.btf_id); 438 if (!prog_btf) 439 goto copy_name; 440 441 func_type = btf__type_by_id(prog_btf, finfo.type_id); 442 if (!func_type || !btf_is_func(func_type)) 443 goto copy_name; 444 445 prog_name = btf__name_by_offset(prog_btf, func_type->name_off); 446 447 copy_name: 448 snprintf(name_buff, buff_len, "%s", prog_name); 449 450 if (prog_btf) 451 btf__free(prog_btf); 452 } 453 454 int get_fd_type(int fd) 455 { 456 char path[PATH_MAX]; 457 char buf[512]; 458 ssize_t n; 459 460 snprintf(path, sizeof(path), "/proc/self/fd/%d", fd); 461 462 n = readlink(path, buf, sizeof(buf)); 463 if (n < 0) { 464 p_err("can't read link type: %s", strerror(errno)); 465 return -1; 466 } 467 if (n == sizeof(buf)) { 468 p_err("can't read link type: path too long!"); 469 return -1; 470 } 471 buf[n] = '\0'; 472 473 if (strstr(buf, "bpf-map")) 474 return BPF_OBJ_MAP; 475 else if (strstr(buf, "bpf-prog")) 476 return BPF_OBJ_PROG; 477 else if (strstr(buf, "bpf-link")) 478 return BPF_OBJ_LINK; 479 480 return BPF_OBJ_UNKNOWN; 481 } 482 483 char *get_fdinfo(int fd, const char *key) 484 { 485 char path[PATH_MAX]; 486 char *line = NULL; 487 size_t line_n = 0; 488 ssize_t n; 489 FILE *fdi; 490 491 snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", fd); 492 493 fdi = fopen(path, "r"); 494 if (!fdi) 495 return NULL; 496 497 while ((n = getline(&line, &line_n, fdi)) > 0) { 498 char *value; 499 int len; 500 501 if (!strstr(line, key)) 502 continue; 503 504 fclose(fdi); 505 506 value = strchr(line, '\t'); 507 if (!value || !value[1]) { 508 free(line); 509 return NULL; 510 } 511 value++; 512 513 len = strlen(value); 514 memmove(line, value, len); 515 line[len - 1] = '\0'; 516 517 return line; 518 } 519 520 free(line); 521 fclose(fdi); 522 return NULL; 523 } 524 525 void print_data_json(uint8_t *data, size_t len) 526 { 527 unsigned int i; 528 529 jsonw_start_array(json_wtr); 530 for (i = 0; i < len; i++) 531 jsonw_printf(json_wtr, "%d", data[i]); 532 jsonw_end_array(json_wtr); 533 } 534 535 void print_hex_data_json(uint8_t *data, size_t len) 536 { 537 unsigned int i; 538 539 jsonw_start_array(json_wtr); 540 for (i = 0; i < len; i++) 541 jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]); 542 jsonw_end_array(json_wtr); 543 } 544 545 /* extra params for nftw cb */ 546 static struct hashmap *build_fn_table; 547 static enum bpf_obj_type build_fn_type; 548 549 static int do_build_table_cb(const char *fpath, const struct stat *sb, 550 int typeflag, struct FTW *ftwbuf) 551 { 552 struct bpf_prog_info pinned_info; 553 __u32 len = sizeof(pinned_info); 554 enum bpf_obj_type objtype; 555 int fd, err = 0; 556 char *path; 557 558 if (typeflag != FTW_F) 559 goto out_ret; 560 561 fd = open_obj_pinned(fpath, true, NULL); 562 if (fd < 0) 563 goto out_ret; 564 565 objtype = get_fd_type(fd); 566 if (objtype != build_fn_type) 567 goto out_close; 568 569 memset(&pinned_info, 0, sizeof(pinned_info)); 570 if (bpf_prog_get_info_by_fd(fd, &pinned_info, &len)) 571 goto out_close; 572 573 path = strdup(fpath); 574 if (!path) { 575 err = -1; 576 goto out_close; 577 } 578 579 err = hashmap__append(build_fn_table, pinned_info.id, path); 580 if (err) { 581 p_err("failed to append entry to hashmap for ID %u, path '%s': %s", 582 pinned_info.id, path, strerror(errno)); 583 free(path); 584 goto out_close; 585 } 586 587 out_close: 588 close(fd); 589 out_ret: 590 return err; 591 } 592 593 int build_pinned_obj_table(struct hashmap *tab, 594 enum bpf_obj_type type) 595 { 596 struct mntent *mntent = NULL; 597 FILE *mntfile = NULL; 598 int flags = FTW_PHYS; 599 int nopenfd = 16; 600 int err = 0; 601 602 mntfile = setmntent("/proc/mounts", "r"); 603 if (!mntfile) 604 return -1; 605 606 build_fn_table = tab; 607 build_fn_type = type; 608 609 while ((mntent = getmntent(mntfile))) { 610 char *path = mntent->mnt_dir; 611 612 if (strncmp(mntent->mnt_type, "bpf", 3) != 0) 613 continue; 614 err = nftw(path, do_build_table_cb, nopenfd, flags); 615 if (err) 616 break; 617 } 618 fclose(mntfile); 619 return err; 620 } 621 622 void delete_pinned_obj_table(struct hashmap *map) 623 { 624 struct hashmap_entry *entry; 625 size_t bkt; 626 627 if (!map) 628 return; 629 630 hashmap__for_each_entry(map, entry, bkt) 631 free(entry->pvalue); 632 633 hashmap__free(map); 634 } 635 636 unsigned int get_page_size(void) 637 { 638 static int result; 639 640 if (!result) 641 result = getpagesize(); 642 return result; 643 } 644 645 unsigned int get_possible_cpus(void) 646 { 647 int cpus = libbpf_num_possible_cpus(); 648 649 if (cpus < 0) { 650 p_err("Can't get # of possible cpus: %s", strerror(-cpus)); 651 exit(-1); 652 } 653 return cpus; 654 } 655 656 static char * 657 ifindex_to_name_ns(__u32 ifindex, __u32 ns_dev, __u32 ns_ino, char *buf) 658 { 659 struct stat st; 660 int err; 661 662 err = stat("/proc/self/ns/net", &st); 663 if (err) { 664 p_err("Can't stat /proc/self: %s", strerror(errno)); 665 return NULL; 666 } 667 668 if (st.st_dev != ns_dev || st.st_ino != ns_ino) 669 return NULL; 670 671 return if_indextoname(ifindex, buf); 672 } 673 674 static int read_sysfs_hex_int(char *path) 675 { 676 char vendor_id_buf[8]; 677 int len; 678 int fd; 679 680 fd = open(path, O_RDONLY); 681 if (fd < 0) { 682 p_err("Can't open %s: %s", path, strerror(errno)); 683 return -1; 684 } 685 686 len = read(fd, vendor_id_buf, sizeof(vendor_id_buf)); 687 close(fd); 688 if (len < 0) { 689 p_err("Can't read %s: %s", path, strerror(errno)); 690 return -1; 691 } 692 if (len >= (int)sizeof(vendor_id_buf)) { 693 p_err("Value in %s too long", path); 694 return -1; 695 } 696 697 vendor_id_buf[len] = 0; 698 699 return strtol(vendor_id_buf, NULL, 0); 700 } 701 702 static int read_sysfs_netdev_hex_int(char *devname, const char *entry_name) 703 { 704 char full_path[64]; 705 706 snprintf(full_path, sizeof(full_path), "/sys/class/net/%s/device/%s", 707 devname, entry_name); 708 709 return read_sysfs_hex_int(full_path); 710 } 711 712 const char * 713 ifindex_to_arch(__u32 ifindex, __u64 ns_dev, __u64 ns_ino, const char **opt) 714 { 715 __maybe_unused int device_id; 716 char devname[IF_NAMESIZE]; 717 int vendor_id; 718 719 if (!ifindex_to_name_ns(ifindex, ns_dev, ns_ino, devname)) { 720 p_err("Can't get net device name for ifindex %u: %s", ifindex, 721 strerror(errno)); 722 return NULL; 723 } 724 725 vendor_id = read_sysfs_netdev_hex_int(devname, "vendor"); 726 if (vendor_id < 0) { 727 p_err("Can't get device vendor id for %s", devname); 728 return NULL; 729 } 730 731 switch (vendor_id) { 732 #ifdef HAVE_LIBBFD_SUPPORT 733 case 0x19ee: 734 device_id = read_sysfs_netdev_hex_int(devname, "device"); 735 if (device_id != 0x4000 && 736 device_id != 0x6000 && 737 device_id != 0x6003) 738 p_info("Unknown NFP device ID, assuming it is NFP-6xxx arch"); 739 *opt = "ctx4"; 740 return "NFP-6xxx"; 741 #endif /* HAVE_LIBBFD_SUPPORT */ 742 /* No NFP support in LLVM, we have no valid triple to return. */ 743 default: 744 p_err("Can't get arch name for device vendor id 0x%04x", 745 (unsigned int)vendor_id); 746 return NULL; 747 } 748 } 749 750 void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode) 751 { 752 char name[IF_NAMESIZE]; 753 754 if (!ifindex) 755 return; 756 757 printf(" offloaded_to "); 758 if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name)) 759 printf("%s", name); 760 else 761 printf("ifindex %u ns_dev %llu ns_ino %llu", 762 ifindex, ns_dev, ns_inode); 763 } 764 765 void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode) 766 { 767 char name[IF_NAMESIZE]; 768 769 if (!ifindex) 770 return; 771 772 jsonw_name(json_wtr, "dev"); 773 jsonw_start_object(json_wtr); 774 jsonw_uint_field(json_wtr, "ifindex", ifindex); 775 jsonw_uint_field(json_wtr, "ns_dev", ns_dev); 776 jsonw_uint_field(json_wtr, "ns_inode", ns_inode); 777 if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name)) 778 jsonw_string_field(json_wtr, "ifname", name); 779 jsonw_end_object(json_wtr); 780 } 781 782 int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what) 783 { 784 char *endptr; 785 786 NEXT_ARGP(); 787 788 if (*val) { 789 p_err("%s already specified", what); 790 return -1; 791 } 792 793 *val = strtoul(**argv, &endptr, 0); 794 if (*endptr) { 795 p_err("can't parse %s as %s", **argv, what); 796 return -1; 797 } 798 NEXT_ARGP(); 799 800 return 0; 801 } 802 803 int __printf(2, 0) 804 print_all_levels(__maybe_unused enum libbpf_print_level level, 805 const char *format, va_list args) 806 { 807 return vfprintf(stderr, format, args); 808 } 809 810 static int prog_fd_by_nametag(void *nametag, int **fds, bool tag) 811 { 812 char prog_name[MAX_PROG_FULL_NAME]; 813 unsigned int id = 0; 814 int fd, nb_fds = 0; 815 void *tmp; 816 int err; 817 818 while (true) { 819 struct bpf_prog_info info = {}; 820 __u32 len = sizeof(info); 821 822 err = bpf_prog_get_next_id(id, &id); 823 if (err) { 824 if (errno != ENOENT) { 825 p_err("%s", strerror(errno)); 826 goto err_close_fds; 827 } 828 return nb_fds; 829 } 830 831 fd = bpf_prog_get_fd_by_id(id); 832 if (fd < 0) { 833 p_err("can't get prog by id (%u): %s", 834 id, strerror(errno)); 835 goto err_close_fds; 836 } 837 838 err = bpf_prog_get_info_by_fd(fd, &info, &len); 839 if (err) { 840 p_err("can't get prog info (%u): %s", 841 id, strerror(errno)); 842 goto err_close_fd; 843 } 844 845 if (tag && memcmp(nametag, info.tag, BPF_TAG_SIZE)) { 846 close(fd); 847 continue; 848 } 849 850 if (!tag) { 851 get_prog_full_name(&info, fd, prog_name, 852 sizeof(prog_name)); 853 if (strncmp(nametag, prog_name, sizeof(prog_name))) { 854 close(fd); 855 continue; 856 } 857 } 858 859 if (nb_fds > 0) { 860 tmp = realloc(*fds, (nb_fds + 1) * sizeof(int)); 861 if (!tmp) { 862 p_err("failed to realloc"); 863 goto err_close_fd; 864 } 865 *fds = tmp; 866 } 867 (*fds)[nb_fds++] = fd; 868 } 869 870 err_close_fd: 871 close(fd); 872 err_close_fds: 873 while (--nb_fds >= 0) 874 close((*fds)[nb_fds]); 875 return -1; 876 } 877 878 int prog_parse_fds(int *argc, char ***argv, int **fds) 879 { 880 if (is_prefix(**argv, "id")) { 881 unsigned int id; 882 char *endptr; 883 884 NEXT_ARGP(); 885 886 id = strtoul(**argv, &endptr, 0); 887 if (*endptr) { 888 p_err("can't parse %s as ID", **argv); 889 return -1; 890 } 891 NEXT_ARGP(); 892 893 (*fds)[0] = bpf_prog_get_fd_by_id(id); 894 if ((*fds)[0] < 0) { 895 p_err("get by id (%u): %s", id, strerror(errno)); 896 return -1; 897 } 898 return 1; 899 } else if (is_prefix(**argv, "tag")) { 900 unsigned char tag[BPF_TAG_SIZE]; 901 902 NEXT_ARGP(); 903 904 if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2, 905 tag + 3, tag + 4, tag + 5, tag + 6, tag + 7) 906 != BPF_TAG_SIZE) { 907 p_err("can't parse tag"); 908 return -1; 909 } 910 NEXT_ARGP(); 911 912 return prog_fd_by_nametag(tag, fds, true); 913 } else if (is_prefix(**argv, "name")) { 914 char *name; 915 916 NEXT_ARGP(); 917 918 name = **argv; 919 if (strlen(name) > MAX_PROG_FULL_NAME - 1) { 920 p_err("can't parse name"); 921 return -1; 922 } 923 NEXT_ARGP(); 924 925 return prog_fd_by_nametag(name, fds, false); 926 } else if (is_prefix(**argv, "pinned")) { 927 char *path; 928 929 NEXT_ARGP(); 930 931 path = **argv; 932 NEXT_ARGP(); 933 934 (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_PROG, NULL); 935 if ((*fds)[0] < 0) 936 return -1; 937 return 1; 938 } 939 940 p_err("expected 'id', 'tag', 'name' or 'pinned', got: '%s'?", **argv); 941 return -1; 942 } 943 944 int prog_parse_fd(int *argc, char ***argv) 945 { 946 int *fds = NULL; 947 int nb_fds, fd; 948 949 fds = malloc(sizeof(int)); 950 if (!fds) { 951 p_err("mem alloc failed"); 952 return -1; 953 } 954 nb_fds = prog_parse_fds(argc, argv, &fds); 955 if (nb_fds != 1) { 956 if (nb_fds > 1) { 957 p_err("several programs match this handle"); 958 while (nb_fds--) 959 close(fds[nb_fds]); 960 } 961 fd = -1; 962 goto exit_free; 963 } 964 965 fd = fds[0]; 966 exit_free: 967 free(fds); 968 return fd; 969 } 970 971 static int map_fd_by_name(char *name, int **fds, 972 const struct bpf_get_fd_by_id_opts *opts) 973 { 974 unsigned int id = 0; 975 int fd, nb_fds = 0; 976 void *tmp; 977 int err; 978 979 while (true) { 980 LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts_ro); 981 struct bpf_map_info info = {}; 982 __u32 len = sizeof(info); 983 984 err = bpf_map_get_next_id(id, &id); 985 if (err) { 986 if (errno != ENOENT) { 987 p_err("%s", strerror(errno)); 988 goto err_close_fds; 989 } 990 return nb_fds; 991 } 992 993 /* Request a read-only fd to query the map info */ 994 opts_ro.open_flags = BPF_F_RDONLY; 995 fd = bpf_map_get_fd_by_id_opts(id, &opts_ro); 996 if (fd < 0) { 997 p_err("can't get map by id (%u): %s", 998 id, strerror(errno)); 999 goto err_close_fds; 1000 } 1001 1002 err = bpf_map_get_info_by_fd(fd, &info, &len); 1003 if (err) { 1004 p_err("can't get map info (%u): %s", 1005 id, strerror(errno)); 1006 goto err_close_fd; 1007 } 1008 1009 if (strncmp(name, info.name, BPF_OBJ_NAME_LEN)) { 1010 close(fd); 1011 continue; 1012 } 1013 1014 /* Get an fd with the requested options, if they differ 1015 * from the read-only options used to get the fd above. 1016 */ 1017 if (memcmp(opts, &opts_ro, sizeof(opts_ro))) { 1018 close(fd); 1019 fd = bpf_map_get_fd_by_id_opts(id, opts); 1020 if (fd < 0) { 1021 p_err("can't get map by id (%u): %s", id, 1022 strerror(errno)); 1023 goto err_close_fds; 1024 } 1025 } 1026 1027 if (nb_fds > 0) { 1028 tmp = realloc(*fds, (nb_fds + 1) * sizeof(int)); 1029 if (!tmp) { 1030 p_err("failed to realloc"); 1031 goto err_close_fd; 1032 } 1033 *fds = tmp; 1034 } 1035 (*fds)[nb_fds++] = fd; 1036 } 1037 1038 err_close_fd: 1039 close(fd); 1040 err_close_fds: 1041 while (--nb_fds >= 0) 1042 close((*fds)[nb_fds]); 1043 return -1; 1044 } 1045 1046 int map_parse_fds(int *argc, char ***argv, int **fds, __u32 open_flags) 1047 { 1048 LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts); 1049 1050 assert((open_flags & ~BPF_F_RDONLY) == 0); 1051 opts.open_flags = open_flags; 1052 1053 if (is_prefix(**argv, "id")) { 1054 unsigned int id; 1055 char *endptr; 1056 1057 NEXT_ARGP(); 1058 1059 id = strtoul(**argv, &endptr, 0); 1060 if (*endptr) { 1061 p_err("can't parse %s as ID", **argv); 1062 return -1; 1063 } 1064 NEXT_ARGP(); 1065 1066 (*fds)[0] = bpf_map_get_fd_by_id_opts(id, &opts); 1067 if ((*fds)[0] < 0) { 1068 p_err("get map by id (%u): %s", id, strerror(errno)); 1069 return -1; 1070 } 1071 return 1; 1072 } else if (is_prefix(**argv, "name")) { 1073 char *name; 1074 1075 NEXT_ARGP(); 1076 1077 name = **argv; 1078 if (strlen(name) > BPF_OBJ_NAME_LEN - 1) { 1079 p_err("can't parse name"); 1080 return -1; 1081 } 1082 NEXT_ARGP(); 1083 1084 return map_fd_by_name(name, fds, &opts); 1085 } else if (is_prefix(**argv, "pinned")) { 1086 char *path; 1087 LIBBPF_OPTS(bpf_obj_get_opts, get_opts); 1088 get_opts.file_flags = open_flags; 1089 1090 NEXT_ARGP(); 1091 1092 path = **argv; 1093 NEXT_ARGP(); 1094 1095 (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_MAP, &get_opts); 1096 if ((*fds)[0] < 0) 1097 return -1; 1098 return 1; 1099 } 1100 1101 p_err("expected 'id', 'name' or 'pinned', got: '%s'?", **argv); 1102 return -1; 1103 } 1104 1105 int map_parse_fd(int *argc, char ***argv, __u32 open_flags) 1106 { 1107 int *fds = NULL; 1108 int nb_fds, fd; 1109 1110 fds = malloc(sizeof(int)); 1111 if (!fds) { 1112 p_err("mem alloc failed"); 1113 return -1; 1114 } 1115 nb_fds = map_parse_fds(argc, argv, &fds, open_flags); 1116 if (nb_fds != 1) { 1117 if (nb_fds > 1) { 1118 p_err("several maps match this handle"); 1119 while (nb_fds--) 1120 close(fds[nb_fds]); 1121 } 1122 fd = -1; 1123 goto exit_free; 1124 } 1125 1126 fd = fds[0]; 1127 exit_free: 1128 free(fds); 1129 return fd; 1130 } 1131 1132 int map_parse_fd_and_info(int *argc, char ***argv, struct bpf_map_info *info, 1133 __u32 *info_len, __u32 open_flags) 1134 { 1135 int err; 1136 int fd; 1137 1138 fd = map_parse_fd(argc, argv, open_flags); 1139 if (fd < 0) 1140 return -1; 1141 1142 err = bpf_map_get_info_by_fd(fd, info, info_len); 1143 if (err) { 1144 p_err("can't get map info: %s", strerror(errno)); 1145 close(fd); 1146 return err; 1147 } 1148 1149 return fd; 1150 } 1151 1152 size_t hash_fn_for_key_as_id(long key, void *ctx) 1153 { 1154 return key; 1155 } 1156 1157 bool equal_fn_for_key_as_id(long k1, long k2, void *ctx) 1158 { 1159 return k1 == k2; 1160 } 1161 1162 const char *bpf_attach_type_input_str(enum bpf_attach_type t) 1163 { 1164 switch (t) { 1165 case BPF_CGROUP_INET_INGRESS: return "ingress"; 1166 case BPF_CGROUP_INET_EGRESS: return "egress"; 1167 case BPF_CGROUP_INET_SOCK_CREATE: return "sock_create"; 1168 case BPF_CGROUP_INET_SOCK_RELEASE: return "sock_release"; 1169 case BPF_CGROUP_SOCK_OPS: return "sock_ops"; 1170 case BPF_CGROUP_DEVICE: return "device"; 1171 case BPF_CGROUP_INET4_BIND: return "bind4"; 1172 case BPF_CGROUP_INET6_BIND: return "bind6"; 1173 case BPF_CGROUP_INET4_CONNECT: return "connect4"; 1174 case BPF_CGROUP_INET6_CONNECT: return "connect6"; 1175 case BPF_CGROUP_INET4_POST_BIND: return "post_bind4"; 1176 case BPF_CGROUP_INET6_POST_BIND: return "post_bind6"; 1177 case BPF_CGROUP_INET4_GETPEERNAME: return "getpeername4"; 1178 case BPF_CGROUP_INET6_GETPEERNAME: return "getpeername6"; 1179 case BPF_CGROUP_INET4_GETSOCKNAME: return "getsockname4"; 1180 case BPF_CGROUP_INET6_GETSOCKNAME: return "getsockname6"; 1181 case BPF_CGROUP_UDP4_SENDMSG: return "sendmsg4"; 1182 case BPF_CGROUP_UDP6_SENDMSG: return "sendmsg6"; 1183 case BPF_CGROUP_SYSCTL: return "sysctl"; 1184 case BPF_CGROUP_UDP4_RECVMSG: return "recvmsg4"; 1185 case BPF_CGROUP_UDP6_RECVMSG: return "recvmsg6"; 1186 case BPF_CGROUP_GETSOCKOPT: return "getsockopt"; 1187 case BPF_CGROUP_SETSOCKOPT: return "setsockopt"; 1188 case BPF_TRACE_RAW_TP: return "raw_tp"; 1189 case BPF_TRACE_FENTRY: return "fentry"; 1190 case BPF_TRACE_FEXIT: return "fexit"; 1191 case BPF_MODIFY_RETURN: return "mod_ret"; 1192 case BPF_SK_REUSEPORT_SELECT: return "sk_skb_reuseport_select"; 1193 case BPF_SK_REUSEPORT_SELECT_OR_MIGRATE: return "sk_skb_reuseport_select_or_migrate"; 1194 default: return libbpf_bpf_attach_type_str(t); 1195 } 1196 } 1197 1198 int pathname_concat(char *buf, int buf_sz, const char *path, 1199 const char *name) 1200 { 1201 int len; 1202 1203 len = snprintf(buf, buf_sz, "%s/%s", path, name); 1204 if (len < 0) 1205 return -EINVAL; 1206 if (len >= buf_sz) 1207 return -ENAMETOOLONG; 1208 1209 return 0; 1210 } 1211