summaryrefslogtreecommitdiffstats
path: root/lib/bpf_libbpf.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bpf_libbpf.c')
-rw-r--r--lib/bpf_libbpf.c389
1 files changed, 389 insertions, 0 deletions
diff --git a/lib/bpf_libbpf.c b/lib/bpf_libbpf.c
new file mode 100644
index 0000000..08692d3
--- /dev/null
+++ b/lib/bpf_libbpf.c
@@ -0,0 +1,389 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * bpf_libbpf.c BPF code relay on libbpf
+ * Authors: Hangbin Liu <haliu@redhat.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include <libelf.h>
+#include <gelf.h>
+
+#include <bpf/libbpf.h>
+#include <bpf/bpf.h>
+
+#include "bpf_util.h"
+
+static int __attribute__((format(printf, 2, 0)))
+verbose_print(enum libbpf_print_level level, const char *format, va_list args)
+{
+ return vfprintf(stderr, format, args);
+}
+
+static int __attribute__((format(printf, 2, 0)))
+silent_print(enum libbpf_print_level level, const char *format, va_list args)
+{
+ if (level > LIBBPF_WARN)
+ return 0;
+
+ /* Skip warning from bpf_object__init_user_maps() for legacy maps */
+ if (strstr(format, "has unrecognized, non-zero options"))
+ return 0;
+
+ return vfprintf(stderr, format, args);
+}
+
+static const char *get_bpf_program__section_name(const struct bpf_program *prog)
+{
+#ifdef HAVE_LIBBPF_SECTION_NAME
+ return bpf_program__section_name(prog);
+#else
+ return bpf_program__title(prog, false);
+#endif
+}
+
+static int create_map(const char *name, struct bpf_elf_map *map,
+ __u32 ifindex, int inner_fd)
+{
+ union bpf_attr attr = {};
+
+ attr.map_type = map->type;
+ strlcpy(attr.map_name, name, sizeof(attr.map_name));
+ attr.map_flags = map->flags;
+ attr.key_size = map->size_key;
+ attr.value_size = map->size_value;
+ attr.max_entries = map->max_elem;
+ attr.map_ifindex = ifindex;
+ attr.inner_map_fd = inner_fd;
+
+ return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
+}
+
+static int create_map_in_map(struct bpf_object *obj, struct bpf_map *map,
+ struct bpf_elf_map *elf_map, int inner_fd,
+ bool *reuse_pin_map)
+{
+ char pathname[PATH_MAX];
+ const char *map_name;
+ bool pin_map = false;
+ int map_fd, ret = 0;
+
+ map_name = bpf_map__name(map);
+
+ if (iproute2_is_pin_map(map_name, pathname)) {
+ pin_map = true;
+
+ /* Check if there already has a pinned map */
+ map_fd = bpf_obj_get(pathname);
+ if (map_fd > 0) {
+ if (reuse_pin_map)
+ *reuse_pin_map = true;
+ close(map_fd);
+ return bpf_map__set_pin_path(map, pathname);
+ }
+ }
+
+ map_fd = create_map(map_name, elf_map, bpf_map__ifindex(map), inner_fd);
+ if (map_fd < 0) {
+ fprintf(stderr, "create map %s failed\n", map_name);
+ return map_fd;
+ }
+
+ ret = bpf_map__reuse_fd(map, map_fd);
+ if (ret < 0) {
+ fprintf(stderr, "map %s reuse fd failed\n", map_name);
+ goto err_out;
+ }
+
+ if (pin_map) {
+ ret = bpf_map__set_pin_path(map, pathname);
+ if (ret < 0)
+ goto err_out;
+ }
+
+ return 0;
+err_out:
+ close(map_fd);
+ return ret;
+}
+
+static int
+handle_legacy_map_in_map(struct bpf_object *obj, struct bpf_map *inner_map,
+ const char *inner_map_name)
+{
+ int inner_fd, outer_fd, inner_idx, ret = 0;
+ struct bpf_elf_map imap, omap;
+ struct bpf_map *outer_map;
+ /* What's the size limit of map name? */
+ char outer_map_name[128];
+ bool reuse_pin_map = false;
+
+ /* Deal with map-in-map */
+ if (iproute2_is_map_in_map(inner_map_name, &imap, &omap, outer_map_name)) {
+ ret = create_map_in_map(obj, inner_map, &imap, -1, NULL);
+ if (ret < 0)
+ return ret;
+
+ inner_fd = bpf_map__fd(inner_map);
+ outer_map = bpf_object__find_map_by_name(obj, outer_map_name);
+ ret = create_map_in_map(obj, outer_map, &omap, inner_fd, &reuse_pin_map);
+ if (ret < 0)
+ return ret;
+
+ if (!reuse_pin_map) {
+ inner_idx = imap.inner_idx;
+ outer_fd = bpf_map__fd(outer_map);
+ ret = bpf_map_update_elem(outer_fd, &inner_idx, &inner_fd, 0);
+ if (ret < 0)
+ fprintf(stderr, "Cannot update inner_idx into outer_map\n");
+ }
+ }
+
+ return ret;
+}
+
+static int find_legacy_tail_calls(struct bpf_program *prog, struct bpf_object *obj,
+ struct bpf_map **pmap)
+{
+ unsigned int map_id, key_id;
+ const char *sec_name;
+ struct bpf_map *map;
+ char map_name[128];
+ int ret;
+
+ /* Handle iproute2 tail call */
+ sec_name = get_bpf_program__section_name(prog);
+ ret = sscanf(sec_name, "%i/%i", &map_id, &key_id);
+ if (ret != 2)
+ return -1;
+
+ ret = iproute2_find_map_name_by_id(map_id, map_name);
+ if (ret < 0) {
+ fprintf(stderr, "unable to find map id %u for tail call\n", map_id);
+ return ret;
+ }
+
+ map = bpf_object__find_map_by_name(obj, map_name);
+ if (!map)
+ return -1;
+
+ if (pmap)
+ *pmap = map;
+
+ return 0;
+}
+
+static int update_legacy_tail_call_maps(struct bpf_object *obj)
+{
+ int prog_fd, map_fd, ret = 0;
+ unsigned int map_id, key_id;
+ struct bpf_program *prog;
+ const char *sec_name;
+ struct bpf_map *map;
+
+ bpf_object__for_each_program(prog, obj) {
+ /* load_bpf_object has already verified find_legacy_tail_calls
+ * succeeds when it should
+ */
+ if (find_legacy_tail_calls(prog, obj, &map) < 0)
+ continue;
+
+ prog_fd = bpf_program__fd(prog);
+ if (prog_fd < 0)
+ continue;
+
+ sec_name = get_bpf_program__section_name(prog);
+ ret = sscanf(sec_name, "%i/%i", &map_id, &key_id);
+ if (ret != 2)
+ continue;
+
+ map_fd = bpf_map__fd(map);
+ ret = bpf_map_update_elem(map_fd, &key_id, &prog_fd, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Cannot update map key for tail call!\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int handle_legacy_maps(struct bpf_object *obj)
+{
+ char pathname[PATH_MAX];
+ struct bpf_map *map;
+ const char *map_name;
+ int map_fd, ret = 0;
+
+ bpf_object__for_each_map(map, obj) {
+ map_name = bpf_map__name(map);
+
+ ret = handle_legacy_map_in_map(obj, map, map_name);
+ if (ret)
+ return ret;
+
+ /* If it is a iproute2 legacy pin maps, just set pin path
+ * and let bpf_object__load() to deal with the map creation.
+ * We need to ignore map-in-maps which have pinned maps manually
+ */
+ map_fd = bpf_map__fd(map);
+ if (map_fd < 0 && iproute2_is_pin_map(map_name, pathname)) {
+ ret = bpf_map__set_pin_path(map, pathname);
+ if (ret) {
+ fprintf(stderr, "map '%s': couldn't set pin path.\n", map_name);
+ break;
+ }
+ }
+
+ }
+
+ return ret;
+}
+
+static bool bpf_map_is_offload_neutral(const struct bpf_map *map)
+{
+ return bpf_map__type(map) == BPF_MAP_TYPE_PERF_EVENT_ARRAY;
+}
+
+static bool find_prog_to_attach(struct bpf_program *prog,
+ struct bpf_program *exist_prog,
+ const char *section, const char *prog_name)
+{
+ if (exist_prog)
+ return false;
+
+ /* We have default section name 'prog'. So do not check
+ * section name if there already has program name.
+ */
+ if (prog_name)
+ return !strcmp(bpf_program__name(prog), prog_name);
+ else
+ return !strcmp(get_bpf_program__section_name(prog), section);
+}
+
+static int load_bpf_object(struct bpf_cfg_in *cfg)
+{
+ struct bpf_program *p, *prog = NULL;
+ struct bpf_object *obj;
+ char root_path[PATH_MAX];
+ struct bpf_map *map;
+ int prog_fd, ret = 0;
+
+ ret = iproute2_get_root_path(root_path, PATH_MAX);
+ if (ret)
+ return ret;
+
+ DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
+ .relaxed_maps = true,
+ .pin_root_path = root_path,
+ );
+
+#if (LIBBPF_MAJOR_VERSION > 0) || (LIBBPF_MINOR_VERSION >= 7)
+ open_opts.kernel_log_level = 1;
+ if (cfg->verbose)
+ open_opts.kernel_log_level |= 2;
+#endif
+
+ obj = bpf_object__open_file(cfg->object, &open_opts);
+ if (libbpf_get_error(obj)) {
+ fprintf(stderr, "ERROR: opening BPF object file failed\n");
+ return -ENOENT;
+ }
+
+ bpf_object__for_each_program(p, obj) {
+ bool prog_to_attach = find_prog_to_attach(p, prog,
+ cfg->section,
+ cfg->prog_name);
+
+ /* Only load the programs that will either be subsequently
+ * attached or inserted into a tail call map */
+ if (find_legacy_tail_calls(p, obj, NULL) < 0 &&
+ !prog_to_attach) {
+ ret = bpf_program__set_autoload(p, false);
+ if (ret)
+ return -EINVAL;
+ continue;
+ }
+
+ bpf_program__set_type(p, cfg->type);
+ bpf_program__set_ifindex(p, cfg->ifindex);
+
+ if (prog_to_attach)
+ prog = p;
+ }
+
+ bpf_object__for_each_map(map, obj) {
+ if (!bpf_map_is_offload_neutral(map))
+ bpf_map__set_ifindex(map, cfg->ifindex);
+ }
+
+ if (!prog) {
+ if (cfg->prog_name)
+ fprintf(stderr, "object file doesn't contain prog %s\n", cfg->prog_name);
+ else
+ fprintf(stderr, "object file doesn't contain sec %s\n", cfg->section);
+ return -ENOENT;
+ }
+
+ /* Handle iproute2 legacy pin maps and map-in-maps */
+ ret = handle_legacy_maps(obj);
+ if (ret)
+ goto unload_obj;
+
+ ret = bpf_object__load(obj);
+ if (ret)
+ goto unload_obj;
+
+ ret = update_legacy_tail_call_maps(obj);
+ if (ret)
+ goto unload_obj;
+
+ prog_fd = fcntl(bpf_program__fd(prog), F_DUPFD_CLOEXEC, 1);
+ if (prog_fd < 0)
+ ret = -errno;
+ else
+ cfg->prog_fd = prog_fd;
+
+unload_obj:
+ /* Close obj as we don't need it */
+ bpf_object__close(obj);
+ return ret;
+}
+
+/* Load ebpf and return prog fd */
+int iproute2_load_libbpf(struct bpf_cfg_in *cfg)
+{
+ int ret = 0;
+
+ if (cfg->verbose)
+ libbpf_set_print(verbose_print);
+ else
+ libbpf_set_print(silent_print);
+
+ ret = iproute2_bpf_elf_ctx_init(cfg);
+ if (ret < 0) {
+ fprintf(stderr, "Cannot initialize ELF context!\n");
+ return ret;
+ }
+
+ ret = iproute2_bpf_fetch_ancillary();
+ if (ret < 0) {
+ fprintf(stderr, "Error fetching ELF ancillary data!\n");
+ return ret;
+ }
+
+ ret = load_bpf_object(cfg);
+ if (ret)
+ return ret;
+
+ return cfg->prog_fd;
+}