/* SPDX-License-Identifier: GPL-2.0 */ /* * bpf_libbpf.c BPF code relay on libbpf * Authors: Hangbin Liu * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }