| /* 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 verbose_print(enum libbpf_print_level level, const char *format, va_list args) |
| { |
| return vfprintf(stderr, format, args); |
| } |
| |
| static int 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) |
| { |
| struct bpf_create_map_attr map_attr = {}; |
| |
| map_attr.name = name; |
| map_attr.map_type = map->type; |
| map_attr.map_flags = map->flags; |
| map_attr.key_size = map->size_key; |
| map_attr.value_size = map->size_value; |
| map_attr.max_entries = map->max_elem; |
| map_attr.map_ifindex = ifindex; |
| map_attr.inner_map_fd = inner_fd; |
| |
| return bpf_create_map_xattr(&map_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) |
| { |
| 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; |
| |
| /* Save the map here for later updating */ |
| bpf_program__set_priv(prog, map, NULL); |
| |
| 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) { |
| map = bpf_program__priv(prog); |
| if (!map) |
| 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 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, |
| ); |
| |
| 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) { |
| /* Only load the programs that will either be subsequently |
| * attached or inserted into a tail call map */ |
| if (find_legacy_tail_calls(p, obj) < 0 && cfg->section && |
| strcmp(get_bpf_program__section_name(p), cfg->section)) { |
| 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) |
| 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) { |
| 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; |
| } |