blob: 7dd1faf536f4db7c5b52656ef3ee7a273671399b [file] [log] [blame]
/* 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 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) {
bool prog_to_attach = !prog && cfg->section &&
!strcmp(get_bpf_program__section_name(p), cfg->section);
/* 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) {
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;
}