Blame lib/bpf_libbpf.c

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
}