aboutsummaryrefslogtreecommitdiffstats
path: root/src/bpfswitch/usrc/libbpf_helpers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bpfswitch/usrc/libbpf_helpers.c')
-rw-r--r--src/bpfswitch/usrc/libbpf_helpers.c309
1 files changed, 309 insertions, 0 deletions
diff --git a/src/bpfswitch/usrc/libbpf_helpers.c b/src/bpfswitch/usrc/libbpf_helpers.c
new file mode 100644
index 00000000..522f5586
--- /dev/null
+++ b/src/bpfswitch/usrc/libbpf_helpers.c
@@ -0,0 +1,309 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * convenience wrappers around libbpf functions
+ *
+ * Copyright (c) 2019-2021 David Ahern <dsahern@gmail.com>
+ */
+
+#include <linux/if_link.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <unistd.h>
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+#include <errno.h>
+
+#include "libbpf_helpers.h"
+
+enum bpf_obj_type {
+ BPF_OBJ_UNKNOWN,
+ BPF_OBJ_PROG,
+ BPF_OBJ_MAP,
+ BPF_OBJ_LINK,
+ BPF_OBJ_BTF,
+};
+
+int load_obj_file(struct bpf_prog_load_attr *attr,
+ struct bpf_object **obj,
+ const char *objfile, bool user_set)
+{
+ static char *expected_paths[] = {
+ "bin",
+ "ksrc/obj", /* path in git tree */
+ "bpf-obj",
+ ".", /* cwd */
+ NULL,
+ };
+ char path[PATH_MAX];
+ int prog_fd, i = 0;
+
+ if (user_set) {
+ attr->file = objfile;
+ return bpf_prog_load_xattr(attr, obj, &prog_fd);
+ }
+
+ attr->file = path;
+ while (expected_paths[i]) {
+ struct stat sbuf;
+
+ snprintf(path, sizeof(path), "%s/%s",
+ expected_paths[i], objfile);
+
+ if (stat(path, &sbuf) == 0) {
+ if (!bpf_prog_load_xattr(attr, obj, &prog_fd))
+ return 0;
+
+ if (errno != ENOENT)
+ break;
+ }
+ ++i;
+ }
+
+ fprintf(stderr, "Failed to find object file; nothing to load\n");
+ return 1;
+}
+
+int bpf_map_get_fd_by_name(const char *name)
+{
+ struct bpf_map_info info = {};
+ __u32 len = sizeof(info);
+ __u32 id = 0;
+ int err, fd;
+
+ while (1) {
+ err = bpf_map_get_next_id(id, &id);
+ if (err)
+ break;
+
+ fd = bpf_map_get_fd_by_id(id);
+ if (fd < 0)
+ continue;
+
+ err = bpf_obj_get_info_by_fd(fd, &info, &len);
+ if (!err && strcmp(info.name, name) == 0)
+ return fd;
+
+ close(fd);
+ }
+
+ return -1;
+}
+
+/* from bpftool */
+static int get_fd_type(int fd)
+{
+ char path[PATH_MAX];
+ char buf[512];
+ ssize_t n;
+
+ snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
+
+ n = readlink(path, buf, sizeof(buf));
+ if (n < 0) {
+ fprintf(stderr, "can't read link type: %s\n", strerror(errno));
+ return -1;
+ }
+ if (n == sizeof(path)) {
+ fprintf(stderr, "can't read link type: path too long!\n");
+ return -1;
+ }
+
+ if (strstr(buf, "bpf-map"))
+ return BPF_OBJ_MAP;
+
+ if (strstr(buf, "bpf-prog"))
+ return BPF_OBJ_PROG;
+
+ if (strstr(buf, "bpf-link"))
+ return BPF_OBJ_LINK;
+
+ return BPF_OBJ_UNKNOWN;
+}
+
+int bpf_map_get_fd_by_path(const char *path)
+{
+ enum bpf_obj_type objtype;
+ int fd;
+
+ fd = bpf_obj_get(path);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to get bpf object (%s): %s\n",
+ path, strerror(errno));
+ return -1;
+ }
+
+ objtype = get_fd_type(fd);
+ if (objtype != BPF_OBJ_MAP) {
+ fprintf(stderr, "Path is not to a BPF map\n");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int bpf_map_get_fd(__u32 id, const char *path, const char *name,
+ const char *desc)
+{
+ int fd = -1;
+
+ if (id) {
+ fd = bpf_map_get_fd_by_id(id);
+ if (fd < 0 && errno != ENOENT) {
+ fprintf(stderr,
+ "Failed to get fd for %s by id: %s: %d\n",
+ desc, strerror(errno), errno);
+ return -1;
+ }
+ } else if (path) {
+ fd = bpf_map_get_fd_by_path(path);
+ if (fd < 0) {
+ fprintf(stderr,
+ "Failed to get fd for %s by path: %s: %d\n",
+ desc, strerror(errno), errno);
+ return -1;
+ }
+ } else if (name) {
+ fd = bpf_map_get_fd_by_name(name);
+ if (fd < 0 && errno != ENOENT) {
+ fprintf(stderr,
+ "Failed to get fd for %s by expected name: %s: %d\n",
+ desc, strerror(errno), errno);
+ return -1;
+ }
+ }
+
+ return fd;
+}
+
+int bpf_prog_get_fd_by_path(const char *path)
+{
+ enum bpf_obj_type objtype;
+ int fd;
+
+ fd = bpf_obj_get(path);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to get bpf object (%s): %s\n",
+ path, strerror(errno));
+ return -1;
+ }
+
+ objtype = get_fd_type(fd);
+ if (objtype != BPF_OBJ_PROG) {
+ fprintf(stderr, "Path is not to a BPF program\n");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int bpf_prog_get_fd_by_name(const char *name)
+{
+ struct bpf_prog_info info = {};
+ __u32 len = sizeof(info);
+ __u32 id = 0;
+ int err, fd;
+
+ while (1) {
+ err = bpf_prog_get_next_id(id, &id);
+ if (err)
+ break;
+
+ fd = bpf_prog_get_fd_by_id(id);
+ if (fd < 0)
+ continue;
+
+ err = bpf_obj_get_info_by_fd(fd, &info, &len);
+ if (!err && strcmp(info.name, name) == 0)
+ return fd;
+
+ close(fd);
+ }
+
+ return -1;
+}
+
+int bpf_prog_get_fd(__u32 id, const char *path, const char *name,
+ const char *desc)
+{
+ int fd = -1;
+
+ if (id) {
+ fd = bpf_prog_get_fd_by_id(id);
+ if (fd < 0 && errno != ENOENT) {
+ fprintf(stderr,
+ "Failed to get fd for %s by id: %s: %d\n",
+ desc, strerror(errno), errno);
+ return -1;
+ }
+ } else if (path) {
+ fd = bpf_prog_get_fd_by_path(path);
+ if (fd < 0) {
+ fprintf(stderr,
+ "Failed to get fd for %s by path: %s: %d\n",
+ desc, strerror(errno), errno);
+ return -1;
+ }
+ } else if (name) {
+ fd = bpf_prog_get_fd_by_name(name);
+ if (fd < 0 && errno != ENOENT) {
+ fprintf(stderr,
+ "Failed to get fd for %s by expected name: %s: %d\n",
+ desc, strerror(errno), errno);
+ return -1;
+ }
+ }
+
+ return fd;
+}
+
+int attach_to_dev_generic(int idx, int prog_fd, const char *dev)
+{
+ int err;
+
+ err = bpf_set_link_xdp_fd(idx, prog_fd, XDP_FLAGS_SKB_MODE);
+ if (err < 0) {
+ printf("ERROR: failed to attach program to %s\n", dev);
+ return err;
+ }
+
+ return 0;
+}
+
+int detach_from_dev_generic(int idx, const char *dev)
+{
+ int err;
+
+ err = bpf_set_link_xdp_fd(idx, -1, XDP_FLAGS_SKB_MODE);
+ if (err < 0)
+ printf("ERROR: failed to detach program from %s\n", dev);
+
+ return 0;
+}
+
+
+int attach_to_dev(int idx, int prog_fd, const char *dev)
+{
+ int err;
+
+ err = bpf_set_link_xdp_fd(idx, prog_fd, XDP_FLAGS_DRV_MODE);
+ if (err < 0) {
+ printf("ERROR: failed to attach program to %s\n", dev);
+ return err;
+ }
+
+ return 0;
+}
+
+int detach_from_dev(int idx, const char *dev)
+{
+ int err;
+
+ err = bpf_set_link_xdp_fd(idx, -1, 0);
+ if (err < 0)
+ printf("ERROR: failed to detach program from %s\n", dev);
+
+ return 0;
+}