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