Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
0002 /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
0003 
0004 #define _GNU_SOURCE
0005 #include <ctype.h>
0006 #include <errno.h>
0007 #include <fcntl.h>
0008 #include <ftw.h>
0009 #include <libgen.h>
0010 #include <mntent.h>
0011 #include <stdbool.h>
0012 #include <stdio.h>
0013 #include <stdlib.h>
0014 #include <string.h>
0015 #include <unistd.h>
0016 #include <net/if.h>
0017 #include <sys/mount.h>
0018 #include <sys/resource.h>
0019 #include <sys/stat.h>
0020 #include <sys/vfs.h>
0021 
0022 #include <linux/filter.h>
0023 #include <linux/limits.h>
0024 #include <linux/magic.h>
0025 #include <linux/unistd.h>
0026 
0027 #include <bpf/bpf.h>
0028 #include <bpf/hashmap.h>
0029 #include <bpf/libbpf.h> /* libbpf_num_possible_cpus */
0030 #include <bpf/btf.h>
0031 
0032 #include "main.h"
0033 
0034 #ifndef BPF_FS_MAGIC
0035 #define BPF_FS_MAGIC        0xcafe4a11
0036 #endif
0037 
0038 void p_err(const char *fmt, ...)
0039 {
0040     va_list ap;
0041 
0042     va_start(ap, fmt);
0043     if (json_output) {
0044         jsonw_start_object(json_wtr);
0045         jsonw_name(json_wtr, "error");
0046         jsonw_vprintf_enquote(json_wtr, fmt, ap);
0047         jsonw_end_object(json_wtr);
0048     } else {
0049         fprintf(stderr, "Error: ");
0050         vfprintf(stderr, fmt, ap);
0051         fprintf(stderr, "\n");
0052     }
0053     va_end(ap);
0054 }
0055 
0056 void p_info(const char *fmt, ...)
0057 {
0058     va_list ap;
0059 
0060     if (json_output)
0061         return;
0062 
0063     va_start(ap, fmt);
0064     vfprintf(stderr, fmt, ap);
0065     fprintf(stderr, "\n");
0066     va_end(ap);
0067 }
0068 
0069 static bool is_bpffs(char *path)
0070 {
0071     struct statfs st_fs;
0072 
0073     if (statfs(path, &st_fs) < 0)
0074         return false;
0075 
0076     return (unsigned long)st_fs.f_type == BPF_FS_MAGIC;
0077 }
0078 
0079 /* Probe whether kernel switched from memlock-based (RLIMIT_MEMLOCK) to
0080  * memcg-based memory accounting for BPF maps and programs. This was done in
0081  * commit 97306be45fbe ("Merge branch 'switch to memcg-based memory
0082  * accounting'"), in Linux 5.11.
0083  *
0084  * Libbpf also offers to probe for memcg-based accounting vs rlimit, but does
0085  * so by checking for the availability of a given BPF helper and this has
0086  * failed on some kernels with backports in the past, see commit 6b4384ff1088
0087  * ("Revert "bpftool: Use libbpf 1.0 API mode instead of RLIMIT_MEMLOCK"").
0088  * Instead, we can probe by lowering the process-based rlimit to 0, trying to
0089  * load a BPF object, and resetting the rlimit. If the load succeeds then
0090  * memcg-based accounting is supported.
0091  *
0092  * This would be too dangerous to do in the library, because multithreaded
0093  * applications might attempt to load items while the rlimit is at 0. Given
0094  * that bpftool is single-threaded, this is fine to do here.
0095  */
0096 static bool known_to_need_rlimit(void)
0097 {
0098     struct rlimit rlim_init, rlim_cur_zero = {};
0099     struct bpf_insn insns[] = {
0100         BPF_MOV64_IMM(BPF_REG_0, 0),
0101         BPF_EXIT_INSN(),
0102     };
0103     size_t insn_cnt = ARRAY_SIZE(insns);
0104     union bpf_attr attr;
0105     int prog_fd, err;
0106 
0107     memset(&attr, 0, sizeof(attr));
0108     attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
0109     attr.insns = ptr_to_u64(insns);
0110     attr.insn_cnt = insn_cnt;
0111     attr.license = ptr_to_u64("GPL");
0112 
0113     if (getrlimit(RLIMIT_MEMLOCK, &rlim_init))
0114         return false;
0115 
0116     /* Drop the soft limit to zero. We maintain the hard limit to its
0117      * current value, because lowering it would be a permanent operation
0118      * for unprivileged users.
0119      */
0120     rlim_cur_zero.rlim_max = rlim_init.rlim_max;
0121     if (setrlimit(RLIMIT_MEMLOCK, &rlim_cur_zero))
0122         return false;
0123 
0124     /* Do not use bpf_prog_load() from libbpf here, because it calls
0125      * bump_rlimit_memlock(), interfering with the current probe.
0126      */
0127     prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
0128     err = errno;
0129 
0130     /* reset soft rlimit to its initial value */
0131     setrlimit(RLIMIT_MEMLOCK, &rlim_init);
0132 
0133     if (prog_fd < 0)
0134         return err == EPERM;
0135 
0136     close(prog_fd);
0137     return false;
0138 }
0139 
0140 void set_max_rlimit(void)
0141 {
0142     struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
0143 
0144     if (known_to_need_rlimit())
0145         setrlimit(RLIMIT_MEMLOCK, &rinf);
0146 }
0147 
0148 static int
0149 mnt_fs(const char *target, const char *type, char *buff, size_t bufflen)
0150 {
0151     bool bind_done = false;
0152 
0153     while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) {
0154         if (errno != EINVAL || bind_done) {
0155             snprintf(buff, bufflen,
0156                  "mount --make-private %s failed: %s",
0157                  target, strerror(errno));
0158             return -1;
0159         }
0160 
0161         if (mount(target, target, "none", MS_BIND, NULL)) {
0162             snprintf(buff, bufflen,
0163                  "mount --bind %s %s failed: %s",
0164                  target, target, strerror(errno));
0165             return -1;
0166         }
0167 
0168         bind_done = true;
0169     }
0170 
0171     if (mount(type, target, type, 0, "mode=0700")) {
0172         snprintf(buff, bufflen, "mount -t %s %s %s failed: %s",
0173              type, type, target, strerror(errno));
0174         return -1;
0175     }
0176 
0177     return 0;
0178 }
0179 
0180 int mount_tracefs(const char *target)
0181 {
0182     char err_str[ERR_MAX_LEN];
0183     int err;
0184 
0185     err = mnt_fs(target, "tracefs", err_str, ERR_MAX_LEN);
0186     if (err) {
0187         err_str[ERR_MAX_LEN - 1] = '\0';
0188         p_err("can't mount tracefs: %s", err_str);
0189     }
0190 
0191     return err;
0192 }
0193 
0194 int open_obj_pinned(const char *path, bool quiet)
0195 {
0196     char *pname;
0197     int fd = -1;
0198 
0199     pname = strdup(path);
0200     if (!pname) {
0201         if (!quiet)
0202             p_err("mem alloc failed");
0203         goto out_ret;
0204     }
0205 
0206     fd = bpf_obj_get(pname);
0207     if (fd < 0) {
0208         if (!quiet)
0209             p_err("bpf obj get (%s): %s", pname,
0210                   errno == EACCES && !is_bpffs(dirname(pname)) ?
0211                 "directory not in bpf file system (bpffs)" :
0212                 strerror(errno));
0213         goto out_free;
0214     }
0215 
0216 out_free:
0217     free(pname);
0218 out_ret:
0219     return fd;
0220 }
0221 
0222 int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type)
0223 {
0224     enum bpf_obj_type type;
0225     int fd;
0226 
0227     fd = open_obj_pinned(path, false);
0228     if (fd < 0)
0229         return -1;
0230 
0231     type = get_fd_type(fd);
0232     if (type < 0) {
0233         close(fd);
0234         return type;
0235     }
0236     if (type != exp_type) {
0237         p_err("incorrect object type: %s", get_fd_type_name(type));
0238         close(fd);
0239         return -1;
0240     }
0241 
0242     return fd;
0243 }
0244 
0245 int mount_bpffs_for_pin(const char *name)
0246 {
0247     char err_str[ERR_MAX_LEN];
0248     char *file;
0249     char *dir;
0250     int err = 0;
0251 
0252     file = malloc(strlen(name) + 1);
0253     if (!file) {
0254         p_err("mem alloc failed");
0255         return -1;
0256     }
0257 
0258     strcpy(file, name);
0259     dir = dirname(file);
0260 
0261     if (is_bpffs(dir))
0262         /* nothing to do if already mounted */
0263         goto out_free;
0264 
0265     if (block_mount) {
0266         p_err("no BPF file system found, not mounting it due to --nomount option");
0267         err = -1;
0268         goto out_free;
0269     }
0270 
0271     err = mnt_fs(dir, "bpf", err_str, ERR_MAX_LEN);
0272     if (err) {
0273         err_str[ERR_MAX_LEN - 1] = '\0';
0274         p_err("can't mount BPF file system to pin the object (%s): %s",
0275               name, err_str);
0276     }
0277 
0278 out_free:
0279     free(file);
0280     return err;
0281 }
0282 
0283 int do_pin_fd(int fd, const char *name)
0284 {
0285     int err;
0286 
0287     err = mount_bpffs_for_pin(name);
0288     if (err)
0289         return err;
0290 
0291     err = bpf_obj_pin(fd, name);
0292     if (err)
0293         p_err("can't pin the object (%s): %s", name, strerror(errno));
0294 
0295     return err;
0296 }
0297 
0298 int do_pin_any(int argc, char **argv, int (*get_fd)(int *, char ***))
0299 {
0300     int err;
0301     int fd;
0302 
0303     fd = get_fd(&argc, &argv);
0304     if (fd < 0)
0305         return fd;
0306 
0307     err = do_pin_fd(fd, *argv);
0308 
0309     close(fd);
0310     return err;
0311 }
0312 
0313 const char *get_fd_type_name(enum bpf_obj_type type)
0314 {
0315     static const char * const names[] = {
0316         [BPF_OBJ_UNKNOWN]   = "unknown",
0317         [BPF_OBJ_PROG]      = "prog",
0318         [BPF_OBJ_MAP]       = "map",
0319         [BPF_OBJ_LINK]      = "link",
0320     };
0321 
0322     if (type < 0 || type >= ARRAY_SIZE(names) || !names[type])
0323         return names[BPF_OBJ_UNKNOWN];
0324 
0325     return names[type];
0326 }
0327 
0328 void get_prog_full_name(const struct bpf_prog_info *prog_info, int prog_fd,
0329             char *name_buff, size_t buff_len)
0330 {
0331     const char *prog_name = prog_info->name;
0332     const struct btf_type *func_type;
0333     const struct bpf_func_info finfo = {};
0334     struct bpf_prog_info info = {};
0335     __u32 info_len = sizeof(info);
0336     struct btf *prog_btf = NULL;
0337 
0338     if (buff_len <= BPF_OBJ_NAME_LEN ||
0339         strlen(prog_info->name) < BPF_OBJ_NAME_LEN - 1)
0340         goto copy_name;
0341 
0342     if (!prog_info->btf_id || prog_info->nr_func_info == 0)
0343         goto copy_name;
0344 
0345     info.nr_func_info = 1;
0346     info.func_info_rec_size = prog_info->func_info_rec_size;
0347     if (info.func_info_rec_size > sizeof(finfo))
0348         info.func_info_rec_size = sizeof(finfo);
0349     info.func_info = ptr_to_u64(&finfo);
0350 
0351     if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len))
0352         goto copy_name;
0353 
0354     prog_btf = btf__load_from_kernel_by_id(info.btf_id);
0355     if (!prog_btf)
0356         goto copy_name;
0357 
0358     func_type = btf__type_by_id(prog_btf, finfo.type_id);
0359     if (!func_type || !btf_is_func(func_type))
0360         goto copy_name;
0361 
0362     prog_name = btf__name_by_offset(prog_btf, func_type->name_off);
0363 
0364 copy_name:
0365     snprintf(name_buff, buff_len, "%s", prog_name);
0366 
0367     if (prog_btf)
0368         btf__free(prog_btf);
0369 }
0370 
0371 int get_fd_type(int fd)
0372 {
0373     char path[PATH_MAX];
0374     char buf[512];
0375     ssize_t n;
0376 
0377     snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
0378 
0379     n = readlink(path, buf, sizeof(buf));
0380     if (n < 0) {
0381         p_err("can't read link type: %s", strerror(errno));
0382         return -1;
0383     }
0384     if (n == sizeof(path)) {
0385         p_err("can't read link type: path too long!");
0386         return -1;
0387     }
0388 
0389     if (strstr(buf, "bpf-map"))
0390         return BPF_OBJ_MAP;
0391     else if (strstr(buf, "bpf-prog"))
0392         return BPF_OBJ_PROG;
0393     else if (strstr(buf, "bpf-link"))
0394         return BPF_OBJ_LINK;
0395 
0396     return BPF_OBJ_UNKNOWN;
0397 }
0398 
0399 char *get_fdinfo(int fd, const char *key)
0400 {
0401     char path[PATH_MAX];
0402     char *line = NULL;
0403     size_t line_n = 0;
0404     ssize_t n;
0405     FILE *fdi;
0406 
0407     snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", fd);
0408 
0409     fdi = fopen(path, "r");
0410     if (!fdi)
0411         return NULL;
0412 
0413     while ((n = getline(&line, &line_n, fdi)) > 0) {
0414         char *value;
0415         int len;
0416 
0417         if (!strstr(line, key))
0418             continue;
0419 
0420         fclose(fdi);
0421 
0422         value = strchr(line, '\t');
0423         if (!value || !value[1]) {
0424             free(line);
0425             return NULL;
0426         }
0427         value++;
0428 
0429         len = strlen(value);
0430         memmove(line, value, len);
0431         line[len - 1] = '\0';
0432 
0433         return line;
0434     }
0435 
0436     free(line);
0437     fclose(fdi);
0438     return NULL;
0439 }
0440 
0441 void print_data_json(uint8_t *data, size_t len)
0442 {
0443     unsigned int i;
0444 
0445     jsonw_start_array(json_wtr);
0446     for (i = 0; i < len; i++)
0447         jsonw_printf(json_wtr, "%d", data[i]);
0448     jsonw_end_array(json_wtr);
0449 }
0450 
0451 void print_hex_data_json(uint8_t *data, size_t len)
0452 {
0453     unsigned int i;
0454 
0455     jsonw_start_array(json_wtr);
0456     for (i = 0; i < len; i++)
0457         jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]);
0458     jsonw_end_array(json_wtr);
0459 }
0460 
0461 /* extra params for nftw cb */
0462 static struct hashmap *build_fn_table;
0463 static enum bpf_obj_type build_fn_type;
0464 
0465 static int do_build_table_cb(const char *fpath, const struct stat *sb,
0466                  int typeflag, struct FTW *ftwbuf)
0467 {
0468     struct bpf_prog_info pinned_info;
0469     __u32 len = sizeof(pinned_info);
0470     enum bpf_obj_type objtype;
0471     int fd, err = 0;
0472     char *path;
0473 
0474     if (typeflag != FTW_F)
0475         goto out_ret;
0476 
0477     fd = open_obj_pinned(fpath, true);
0478     if (fd < 0)
0479         goto out_ret;
0480 
0481     objtype = get_fd_type(fd);
0482     if (objtype != build_fn_type)
0483         goto out_close;
0484 
0485     memset(&pinned_info, 0, sizeof(pinned_info));
0486     if (bpf_obj_get_info_by_fd(fd, &pinned_info, &len))
0487         goto out_close;
0488 
0489     path = strdup(fpath);
0490     if (!path) {
0491         err = -1;
0492         goto out_close;
0493     }
0494 
0495     err = hashmap__append(build_fn_table, u32_as_hash_field(pinned_info.id), path);
0496     if (err) {
0497         p_err("failed to append entry to hashmap for ID %u, path '%s': %s",
0498               pinned_info.id, path, strerror(errno));
0499         goto out_close;
0500     }
0501 
0502 out_close:
0503     close(fd);
0504 out_ret:
0505     return err;
0506 }
0507 
0508 int build_pinned_obj_table(struct hashmap *tab,
0509                enum bpf_obj_type type)
0510 {
0511     struct mntent *mntent = NULL;
0512     FILE *mntfile = NULL;
0513     int flags = FTW_PHYS;
0514     int nopenfd = 16;
0515     int err = 0;
0516 
0517     mntfile = setmntent("/proc/mounts", "r");
0518     if (!mntfile)
0519         return -1;
0520 
0521     build_fn_table = tab;
0522     build_fn_type = type;
0523 
0524     while ((mntent = getmntent(mntfile))) {
0525         char *path = mntent->mnt_dir;
0526 
0527         if (strncmp(mntent->mnt_type, "bpf", 3) != 0)
0528             continue;
0529         err = nftw(path, do_build_table_cb, nopenfd, flags);
0530         if (err)
0531             break;
0532     }
0533     fclose(mntfile);
0534     return err;
0535 }
0536 
0537 void delete_pinned_obj_table(struct hashmap *map)
0538 {
0539     struct hashmap_entry *entry;
0540     size_t bkt;
0541 
0542     if (!map)
0543         return;
0544 
0545     hashmap__for_each_entry(map, entry, bkt)
0546         free(entry->value);
0547 
0548     hashmap__free(map);
0549 }
0550 
0551 unsigned int get_page_size(void)
0552 {
0553     static int result;
0554 
0555     if (!result)
0556         result = getpagesize();
0557     return result;
0558 }
0559 
0560 unsigned int get_possible_cpus(void)
0561 {
0562     int cpus = libbpf_num_possible_cpus();
0563 
0564     if (cpus < 0) {
0565         p_err("Can't get # of possible cpus: %s", strerror(-cpus));
0566         exit(-1);
0567     }
0568     return cpus;
0569 }
0570 
0571 static char *
0572 ifindex_to_name_ns(__u32 ifindex, __u32 ns_dev, __u32 ns_ino, char *buf)
0573 {
0574     struct stat st;
0575     int err;
0576 
0577     err = stat("/proc/self/ns/net", &st);
0578     if (err) {
0579         p_err("Can't stat /proc/self: %s", strerror(errno));
0580         return NULL;
0581     }
0582 
0583     if (st.st_dev != ns_dev || st.st_ino != ns_ino)
0584         return NULL;
0585 
0586     return if_indextoname(ifindex, buf);
0587 }
0588 
0589 static int read_sysfs_hex_int(char *path)
0590 {
0591     char vendor_id_buf[8];
0592     int len;
0593     int fd;
0594 
0595     fd = open(path, O_RDONLY);
0596     if (fd < 0) {
0597         p_err("Can't open %s: %s", path, strerror(errno));
0598         return -1;
0599     }
0600 
0601     len = read(fd, vendor_id_buf, sizeof(vendor_id_buf));
0602     close(fd);
0603     if (len < 0) {
0604         p_err("Can't read %s: %s", path, strerror(errno));
0605         return -1;
0606     }
0607     if (len >= (int)sizeof(vendor_id_buf)) {
0608         p_err("Value in %s too long", path);
0609         return -1;
0610     }
0611 
0612     vendor_id_buf[len] = 0;
0613 
0614     return strtol(vendor_id_buf, NULL, 0);
0615 }
0616 
0617 static int read_sysfs_netdev_hex_int(char *devname, const char *entry_name)
0618 {
0619     char full_path[64];
0620 
0621     snprintf(full_path, sizeof(full_path), "/sys/class/net/%s/device/%s",
0622          devname, entry_name);
0623 
0624     return read_sysfs_hex_int(full_path);
0625 }
0626 
0627 const char *
0628 ifindex_to_bfd_params(__u32 ifindex, __u64 ns_dev, __u64 ns_ino,
0629               const char **opt)
0630 {
0631     char devname[IF_NAMESIZE];
0632     int vendor_id;
0633     int device_id;
0634 
0635     if (!ifindex_to_name_ns(ifindex, ns_dev, ns_ino, devname)) {
0636         p_err("Can't get net device name for ifindex %d: %s", ifindex,
0637               strerror(errno));
0638         return NULL;
0639     }
0640 
0641     vendor_id = read_sysfs_netdev_hex_int(devname, "vendor");
0642     if (vendor_id < 0) {
0643         p_err("Can't get device vendor id for %s", devname);
0644         return NULL;
0645     }
0646 
0647     switch (vendor_id) {
0648     case 0x19ee:
0649         device_id = read_sysfs_netdev_hex_int(devname, "device");
0650         if (device_id != 0x4000 &&
0651             device_id != 0x6000 &&
0652             device_id != 0x6003)
0653             p_info("Unknown NFP device ID, assuming it is NFP-6xxx arch");
0654         *opt = "ctx4";
0655         return "NFP-6xxx";
0656     default:
0657         p_err("Can't get bfd arch name for device vendor id 0x%04x",
0658               vendor_id);
0659         return NULL;
0660     }
0661 }
0662 
0663 void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
0664 {
0665     char name[IF_NAMESIZE];
0666 
0667     if (!ifindex)
0668         return;
0669 
0670     printf("  offloaded_to ");
0671     if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
0672         printf("%s", name);
0673     else
0674         printf("ifindex %u ns_dev %llu ns_ino %llu",
0675                ifindex, ns_dev, ns_inode);
0676 }
0677 
0678 void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
0679 {
0680     char name[IF_NAMESIZE];
0681 
0682     if (!ifindex)
0683         return;
0684 
0685     jsonw_name(json_wtr, "dev");
0686     jsonw_start_object(json_wtr);
0687     jsonw_uint_field(json_wtr, "ifindex", ifindex);
0688     jsonw_uint_field(json_wtr, "ns_dev", ns_dev);
0689     jsonw_uint_field(json_wtr, "ns_inode", ns_inode);
0690     if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
0691         jsonw_string_field(json_wtr, "ifname", name);
0692     jsonw_end_object(json_wtr);
0693 }
0694 
0695 int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what)
0696 {
0697     char *endptr;
0698 
0699     NEXT_ARGP();
0700 
0701     if (*val) {
0702         p_err("%s already specified", what);
0703         return -1;
0704     }
0705 
0706     *val = strtoul(**argv, &endptr, 0);
0707     if (*endptr) {
0708         p_err("can't parse %s as %s", **argv, what);
0709         return -1;
0710     }
0711     NEXT_ARGP();
0712 
0713     return 0;
0714 }
0715 
0716 int __printf(2, 0)
0717 print_all_levels(__maybe_unused enum libbpf_print_level level,
0718          const char *format, va_list args)
0719 {
0720     return vfprintf(stderr, format, args);
0721 }
0722 
0723 static int prog_fd_by_nametag(void *nametag, int **fds, bool tag)
0724 {
0725     unsigned int id = 0;
0726     int fd, nb_fds = 0;
0727     void *tmp;
0728     int err;
0729 
0730     while (true) {
0731         struct bpf_prog_info info = {};
0732         __u32 len = sizeof(info);
0733 
0734         err = bpf_prog_get_next_id(id, &id);
0735         if (err) {
0736             if (errno != ENOENT) {
0737                 p_err("%s", strerror(errno));
0738                 goto err_close_fds;
0739             }
0740             return nb_fds;
0741         }
0742 
0743         fd = bpf_prog_get_fd_by_id(id);
0744         if (fd < 0) {
0745             p_err("can't get prog by id (%u): %s",
0746                   id, strerror(errno));
0747             goto err_close_fds;
0748         }
0749 
0750         err = bpf_obj_get_info_by_fd(fd, &info, &len);
0751         if (err) {
0752             p_err("can't get prog info (%u): %s",
0753                   id, strerror(errno));
0754             goto err_close_fd;
0755         }
0756 
0757         if ((tag && memcmp(nametag, info.tag, BPF_TAG_SIZE)) ||
0758             (!tag && strncmp(nametag, info.name, BPF_OBJ_NAME_LEN))) {
0759             close(fd);
0760             continue;
0761         }
0762 
0763         if (nb_fds > 0) {
0764             tmp = realloc(*fds, (nb_fds + 1) * sizeof(int));
0765             if (!tmp) {
0766                 p_err("failed to realloc");
0767                 goto err_close_fd;
0768             }
0769             *fds = tmp;
0770         }
0771         (*fds)[nb_fds++] = fd;
0772     }
0773 
0774 err_close_fd:
0775     close(fd);
0776 err_close_fds:
0777     while (--nb_fds >= 0)
0778         close((*fds)[nb_fds]);
0779     return -1;
0780 }
0781 
0782 int prog_parse_fds(int *argc, char ***argv, int **fds)
0783 {
0784     if (is_prefix(**argv, "id")) {
0785         unsigned int id;
0786         char *endptr;
0787 
0788         NEXT_ARGP();
0789 
0790         id = strtoul(**argv, &endptr, 0);
0791         if (*endptr) {
0792             p_err("can't parse %s as ID", **argv);
0793             return -1;
0794         }
0795         NEXT_ARGP();
0796 
0797         (*fds)[0] = bpf_prog_get_fd_by_id(id);
0798         if ((*fds)[0] < 0) {
0799             p_err("get by id (%u): %s", id, strerror(errno));
0800             return -1;
0801         }
0802         return 1;
0803     } else if (is_prefix(**argv, "tag")) {
0804         unsigned char tag[BPF_TAG_SIZE];
0805 
0806         NEXT_ARGP();
0807 
0808         if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2,
0809                tag + 3, tag + 4, tag + 5, tag + 6, tag + 7)
0810             != BPF_TAG_SIZE) {
0811             p_err("can't parse tag");
0812             return -1;
0813         }
0814         NEXT_ARGP();
0815 
0816         return prog_fd_by_nametag(tag, fds, true);
0817     } else if (is_prefix(**argv, "name")) {
0818         char *name;
0819 
0820         NEXT_ARGP();
0821 
0822         name = **argv;
0823         if (strlen(name) > BPF_OBJ_NAME_LEN - 1) {
0824             p_err("can't parse name");
0825             return -1;
0826         }
0827         NEXT_ARGP();
0828 
0829         return prog_fd_by_nametag(name, fds, false);
0830     } else if (is_prefix(**argv, "pinned")) {
0831         char *path;
0832 
0833         NEXT_ARGP();
0834 
0835         path = **argv;
0836         NEXT_ARGP();
0837 
0838         (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_PROG);
0839         if ((*fds)[0] < 0)
0840             return -1;
0841         return 1;
0842     }
0843 
0844     p_err("expected 'id', 'tag', 'name' or 'pinned', got: '%s'?", **argv);
0845     return -1;
0846 }
0847 
0848 int prog_parse_fd(int *argc, char ***argv)
0849 {
0850     int *fds = NULL;
0851     int nb_fds, fd;
0852 
0853     fds = malloc(sizeof(int));
0854     if (!fds) {
0855         p_err("mem alloc failed");
0856         return -1;
0857     }
0858     nb_fds = prog_parse_fds(argc, argv, &fds);
0859     if (nb_fds != 1) {
0860         if (nb_fds > 1) {
0861             p_err("several programs match this handle");
0862             while (nb_fds--)
0863                 close(fds[nb_fds]);
0864         }
0865         fd = -1;
0866         goto exit_free;
0867     }
0868 
0869     fd = fds[0];
0870 exit_free:
0871     free(fds);
0872     return fd;
0873 }
0874 
0875 static int map_fd_by_name(char *name, int **fds)
0876 {
0877     unsigned int id = 0;
0878     int fd, nb_fds = 0;
0879     void *tmp;
0880     int err;
0881 
0882     while (true) {
0883         struct bpf_map_info info = {};
0884         __u32 len = sizeof(info);
0885 
0886         err = bpf_map_get_next_id(id, &id);
0887         if (err) {
0888             if (errno != ENOENT) {
0889                 p_err("%s", strerror(errno));
0890                 goto err_close_fds;
0891             }
0892             return nb_fds;
0893         }
0894 
0895         fd = bpf_map_get_fd_by_id(id);
0896         if (fd < 0) {
0897             p_err("can't get map by id (%u): %s",
0898                   id, strerror(errno));
0899             goto err_close_fds;
0900         }
0901 
0902         err = bpf_obj_get_info_by_fd(fd, &info, &len);
0903         if (err) {
0904             p_err("can't get map info (%u): %s",
0905                   id, strerror(errno));
0906             goto err_close_fd;
0907         }
0908 
0909         if (strncmp(name, info.name, BPF_OBJ_NAME_LEN)) {
0910             close(fd);
0911             continue;
0912         }
0913 
0914         if (nb_fds > 0) {
0915             tmp = realloc(*fds, (nb_fds + 1) * sizeof(int));
0916             if (!tmp) {
0917                 p_err("failed to realloc");
0918                 goto err_close_fd;
0919             }
0920             *fds = tmp;
0921         }
0922         (*fds)[nb_fds++] = fd;
0923     }
0924 
0925 err_close_fd:
0926     close(fd);
0927 err_close_fds:
0928     while (--nb_fds >= 0)
0929         close((*fds)[nb_fds]);
0930     return -1;
0931 }
0932 
0933 int map_parse_fds(int *argc, char ***argv, int **fds)
0934 {
0935     if (is_prefix(**argv, "id")) {
0936         unsigned int id;
0937         char *endptr;
0938 
0939         NEXT_ARGP();
0940 
0941         id = strtoul(**argv, &endptr, 0);
0942         if (*endptr) {
0943             p_err("can't parse %s as ID", **argv);
0944             return -1;
0945         }
0946         NEXT_ARGP();
0947 
0948         (*fds)[0] = bpf_map_get_fd_by_id(id);
0949         if ((*fds)[0] < 0) {
0950             p_err("get map by id (%u): %s", id, strerror(errno));
0951             return -1;
0952         }
0953         return 1;
0954     } else if (is_prefix(**argv, "name")) {
0955         char *name;
0956 
0957         NEXT_ARGP();
0958 
0959         name = **argv;
0960         if (strlen(name) > BPF_OBJ_NAME_LEN - 1) {
0961             p_err("can't parse name");
0962             return -1;
0963         }
0964         NEXT_ARGP();
0965 
0966         return map_fd_by_name(name, fds);
0967     } else if (is_prefix(**argv, "pinned")) {
0968         char *path;
0969 
0970         NEXT_ARGP();
0971 
0972         path = **argv;
0973         NEXT_ARGP();
0974 
0975         (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_MAP);
0976         if ((*fds)[0] < 0)
0977             return -1;
0978         return 1;
0979     }
0980 
0981     p_err("expected 'id', 'name' or 'pinned', got: '%s'?", **argv);
0982     return -1;
0983 }
0984 
0985 int map_parse_fd(int *argc, char ***argv)
0986 {
0987     int *fds = NULL;
0988     int nb_fds, fd;
0989 
0990     fds = malloc(sizeof(int));
0991     if (!fds) {
0992         p_err("mem alloc failed");
0993         return -1;
0994     }
0995     nb_fds = map_parse_fds(argc, argv, &fds);
0996     if (nb_fds != 1) {
0997         if (nb_fds > 1) {
0998             p_err("several maps match this handle");
0999             while (nb_fds--)
1000                 close(fds[nb_fds]);
1001         }
1002         fd = -1;
1003         goto exit_free;
1004     }
1005 
1006     fd = fds[0];
1007 exit_free:
1008     free(fds);
1009     return fd;
1010 }
1011 
1012 int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len)
1013 {
1014     int err;
1015     int fd;
1016 
1017     fd = map_parse_fd(argc, argv);
1018     if (fd < 0)
1019         return -1;
1020 
1021     err = bpf_obj_get_info_by_fd(fd, info, info_len);
1022     if (err) {
1023         p_err("can't get map info: %s", strerror(errno));
1024         close(fd);
1025         return err;
1026     }
1027 
1028     return fd;
1029 }
1030 
1031 size_t hash_fn_for_key_as_id(const void *key, void *ctx)
1032 {
1033     return (size_t)key;
1034 }
1035 
1036 bool equal_fn_for_key_as_id(const void *k1, const void *k2, void *ctx)
1037 {
1038     return k1 == k2;
1039 }
1040 
1041 const char *bpf_attach_type_input_str(enum bpf_attach_type t)
1042 {
1043     switch (t) {
1044     case BPF_CGROUP_INET_INGRESS:       return "ingress";
1045     case BPF_CGROUP_INET_EGRESS:        return "egress";
1046     case BPF_CGROUP_INET_SOCK_CREATE:   return "sock_create";
1047     case BPF_CGROUP_INET_SOCK_RELEASE:  return "sock_release";
1048     case BPF_CGROUP_SOCK_OPS:       return "sock_ops";
1049     case BPF_CGROUP_DEVICE:         return "device";
1050     case BPF_CGROUP_INET4_BIND:     return "bind4";
1051     case BPF_CGROUP_INET6_BIND:     return "bind6";
1052     case BPF_CGROUP_INET4_CONNECT:      return "connect4";
1053     case BPF_CGROUP_INET6_CONNECT:      return "connect6";
1054     case BPF_CGROUP_INET4_POST_BIND:    return "post_bind4";
1055     case BPF_CGROUP_INET6_POST_BIND:    return "post_bind6";
1056     case BPF_CGROUP_INET4_GETPEERNAME:  return "getpeername4";
1057     case BPF_CGROUP_INET6_GETPEERNAME:  return "getpeername6";
1058     case BPF_CGROUP_INET4_GETSOCKNAME:  return "getsockname4";
1059     case BPF_CGROUP_INET6_GETSOCKNAME:  return "getsockname6";
1060     case BPF_CGROUP_UDP4_SENDMSG:       return "sendmsg4";
1061     case BPF_CGROUP_UDP6_SENDMSG:       return "sendmsg6";
1062     case BPF_CGROUP_SYSCTL:         return "sysctl";
1063     case BPF_CGROUP_UDP4_RECVMSG:       return "recvmsg4";
1064     case BPF_CGROUP_UDP6_RECVMSG:       return "recvmsg6";
1065     case BPF_CGROUP_GETSOCKOPT:     return "getsockopt";
1066     case BPF_CGROUP_SETSOCKOPT:     return "setsockopt";
1067     case BPF_TRACE_RAW_TP:          return "raw_tp";
1068     case BPF_TRACE_FENTRY:          return "fentry";
1069     case BPF_TRACE_FEXIT:           return "fexit";
1070     case BPF_MODIFY_RETURN:         return "mod_ret";
1071     case BPF_SK_REUSEPORT_SELECT:       return "sk_skb_reuseport_select";
1072     case BPF_SK_REUSEPORT_SELECT_OR_MIGRATE:    return "sk_skb_reuseport_select_or_migrate";
1073     default:    return libbpf_bpf_attach_type_str(t);
1074     }
1075 }