bpf: support loading map in map from obj
Add support for map in map in the loader and add a small example program. The outer map uses inner_id to reference a bpf_elf_map with a given ID as the inner type. Loading maps is done in three passes, i) all non-map in map maps are loaded, ii) all map in map maps are loaded based on the inner_id map spec of a non-map in map with corresponding id, and iii) related inner maps are attached to the map in map with given inner_idx key. Pinned objetcs are assumed to be managed externally, so they are only retrieved from BPF fs. Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
parent
23b2ed2d64
commit
612ff099a1
|
|
@ -0,0 +1,56 @@
|
|||
#include "../../include/bpf_api.h"
|
||||
|
||||
#define MAP_INNER_ID 42
|
||||
|
||||
struct bpf_elf_map __section_maps map_inner = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.size_key = sizeof(uint32_t),
|
||||
.size_value = sizeof(uint32_t),
|
||||
.id = MAP_INNER_ID,
|
||||
.inner_idx = 0,
|
||||
.pinning = PIN_GLOBAL_NS,
|
||||
.max_elem = 1,
|
||||
};
|
||||
|
||||
struct bpf_elf_map __section_maps map_outer = {
|
||||
.type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
|
||||
.size_key = sizeof(uint32_t),
|
||||
.size_value = sizeof(uint32_t),
|
||||
.inner_id = MAP_INNER_ID,
|
||||
.pinning = PIN_GLOBAL_NS,
|
||||
.max_elem = 1,
|
||||
};
|
||||
|
||||
__section("egress")
|
||||
int emain(struct __sk_buff *skb)
|
||||
{
|
||||
struct bpf_elf_map *map_inner;
|
||||
int key = 0, *val;
|
||||
|
||||
map_inner = map_lookup_elem(&map_outer, &key);
|
||||
if (map_inner) {
|
||||
val = map_lookup_elem(map_inner, &key);
|
||||
if (val)
|
||||
lock_xadd(val, 1);
|
||||
}
|
||||
|
||||
return BPF_H_DEFAULT;
|
||||
}
|
||||
|
||||
__section("ingress")
|
||||
int imain(struct __sk_buff *skb)
|
||||
{
|
||||
struct bpf_elf_map *map_inner;
|
||||
int key = 0, *val;
|
||||
|
||||
map_inner = map_lookup_elem(&map_outer, &key);
|
||||
if (map_inner) {
|
||||
val = map_lookup_elem(map_inner, &key);
|
||||
if (val)
|
||||
printt("map val: %d\n", *val);
|
||||
}
|
||||
|
||||
return BPF_H_DEFAULT;
|
||||
}
|
||||
|
||||
BPF_LICENSE("GPL");
|
||||
|
|
@ -36,6 +36,8 @@ struct bpf_elf_map {
|
|||
__u32 flags;
|
||||
__u32 id;
|
||||
__u32 pinning;
|
||||
__u32 inner_id;
|
||||
__u32 inner_idx;
|
||||
};
|
||||
|
||||
#endif /* __BPF_ELF__ */
|
||||
|
|
|
|||
159
lib/bpf.c
159
lib/bpf.c
|
|
@ -1023,15 +1023,16 @@ static int bpf_log_realloc(struct bpf_elf_ctx *ctx)
|
|||
|
||||
static int bpf_map_create(enum bpf_map_type type, uint32_t size_key,
|
||||
uint32_t size_value, uint32_t max_elem,
|
||||
uint32_t flags)
|
||||
uint32_t flags, int inner_fd)
|
||||
{
|
||||
union bpf_attr attr = {};
|
||||
|
||||
attr.map_type = type;
|
||||
attr.key_size = size_key;
|
||||
attr.value_size = size_value;
|
||||
attr.value_size = inner_fd ? sizeof(int) : size_value;
|
||||
attr.max_entries = max_elem;
|
||||
attr.map_flags = flags;
|
||||
attr.inner_map_fd = inner_fd;
|
||||
|
||||
return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
}
|
||||
|
|
@ -1343,7 +1344,7 @@ retry:
|
|||
|
||||
static void bpf_map_report(int fd, const char *name,
|
||||
const struct bpf_elf_map *map,
|
||||
struct bpf_elf_ctx *ctx)
|
||||
struct bpf_elf_ctx *ctx, int inner_fd)
|
||||
{
|
||||
fprintf(stderr, "Map object \'%s\' %s%s (%d)!\n", name,
|
||||
fd < 0 ? "rejected: " : "loaded",
|
||||
|
|
@ -1354,15 +1355,91 @@ static void bpf_map_report(int fd, const char *name,
|
|||
fprintf(stderr, " - Identifier: %u\n", map->id);
|
||||
fprintf(stderr, " - Pinning: %u\n", map->pinning);
|
||||
fprintf(stderr, " - Size key: %u\n", map->size_key);
|
||||
fprintf(stderr, " - Size value: %u\n", map->size_value);
|
||||
fprintf(stderr, " - Size value: %u\n",
|
||||
inner_fd ? (int)sizeof(int) : map->size_value);
|
||||
fprintf(stderr, " - Max elems: %u\n", map->max_elem);
|
||||
fprintf(stderr, " - Flags: %#x\n\n", map->flags);
|
||||
}
|
||||
|
||||
static int bpf_map_attach(const char *name, const struct bpf_elf_map *map,
|
||||
struct bpf_elf_ctx *ctx)
|
||||
static int bpf_find_map_id(const struct bpf_elf_ctx *ctx, uint32_t id)
|
||||
{
|
||||
int fd, ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ctx->map_num; i++) {
|
||||
if (ctx->maps[i].id != id)
|
||||
continue;
|
||||
if (ctx->map_fds[i] < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ctx->map_fds[i];
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int bpf_derive_elf_map_from_fdinfo(int fd, struct bpf_elf_map *map)
|
||||
{
|
||||
char file[PATH_MAX], buff[4096];
|
||||
unsigned int val;
|
||||
FILE *fp;
|
||||
|
||||
snprintf(file, sizeof(file), "/proc/%d/fdinfo/%d", getpid(), fd);
|
||||
|
||||
memset(map, 0, sizeof(*map));
|
||||
|
||||
fp = fopen(file, "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "No procfs support?!\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
while (fgets(buff, sizeof(buff), fp)) {
|
||||
if (sscanf(buff, "map_type:\t%u", &val) == 1)
|
||||
map->type = val;
|
||||
else if (sscanf(buff, "key_size:\t%u", &val) == 1)
|
||||
map->size_key = val;
|
||||
else if (sscanf(buff, "value_size:\t%u", &val) == 1)
|
||||
map->size_value = val;
|
||||
else if (sscanf(buff, "max_entries:\t%u", &val) == 1)
|
||||
map->max_elem = val;
|
||||
else if (sscanf(buff, "map_flags:\t%i", &val) == 1)
|
||||
map->flags = val;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bpf_report_map_in_map(int outer_fd, int inner_fd, uint32_t idx)
|
||||
{
|
||||
struct bpf_elf_map outer_map;
|
||||
int ret;
|
||||
|
||||
fprintf(stderr, "Cannot insert map into map! ");
|
||||
|
||||
ret = bpf_derive_elf_map_from_fdinfo(outer_fd, &outer_map);
|
||||
if (!ret) {
|
||||
if (idx >= outer_map.max_elem &&
|
||||
outer_map.type == BPF_MAP_TYPE_ARRAY_OF_MAPS) {
|
||||
fprintf(stderr, "Outer map has %u elements, index %u is invalid!\n",
|
||||
outer_map.max_elem, idx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Different map specs used for outer and inner map?\n");
|
||||
}
|
||||
|
||||
static bool bpf_is_map_in_map_type(const struct bpf_elf_map *map)
|
||||
{
|
||||
return map->type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
|
||||
map->type == BPF_MAP_TYPE_HASH_OF_MAPS;
|
||||
}
|
||||
|
||||
static int bpf_map_attach(const char *name, const struct bpf_elf_map *map,
|
||||
struct bpf_elf_ctx *ctx, int *have_map_in_map)
|
||||
{
|
||||
int fd, ret, map_inner_fd = 0;
|
||||
|
||||
fd = bpf_probe_pinned(name, ctx, map->pinning);
|
||||
if (fd > 0) {
|
||||
|
|
@ -1381,11 +1458,29 @@ static int bpf_map_attach(const char *name, const struct bpf_elf_map *map,
|
|||
return fd;
|
||||
}
|
||||
|
||||
if (have_map_in_map && bpf_is_map_in_map_type(map)) {
|
||||
(*have_map_in_map)++;
|
||||
if (map->inner_id)
|
||||
return 0;
|
||||
fprintf(stderr, "Map \'%s\' cannot be created since no inner map ID defined!\n",
|
||||
name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!have_map_in_map && bpf_is_map_in_map_type(map)) {
|
||||
map_inner_fd = bpf_find_map_id(ctx, map->inner_id);
|
||||
if (map_inner_fd < 0) {
|
||||
fprintf(stderr, "Map \'%s\' cannot be loaded. Inner map with ID %u not found!\n",
|
||||
name, map->inner_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
fd = bpf_map_create(map->type, map->size_key, map->size_value,
|
||||
map->max_elem, map->flags);
|
||||
map->max_elem, map->flags, map_inner_fd);
|
||||
if (fd < 0 || ctx->verbose) {
|
||||
bpf_map_report(fd, name, map, ctx);
|
||||
bpf_map_report(fd, name, map, ctx, map_inner_fd);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
}
|
||||
|
|
@ -1430,21 +1525,63 @@ static const char *bpf_map_fetch_name(struct bpf_elf_ctx *ctx, int which)
|
|||
|
||||
static int bpf_maps_attach_all(struct bpf_elf_ctx *ctx)
|
||||
{
|
||||
int i, j, ret, fd, inner_fd, inner_idx, have_map_in_map = 0;
|
||||
const char *map_name;
|
||||
int i, fd;
|
||||
|
||||
for (i = 0; i < ctx->map_num; i++) {
|
||||
map_name = bpf_map_fetch_name(ctx, i);
|
||||
if (!map_name)
|
||||
return -EIO;
|
||||
|
||||
fd = bpf_map_attach(map_name, &ctx->maps[i], ctx);
|
||||
fd = bpf_map_attach(map_name, &ctx->maps[i], ctx,
|
||||
&have_map_in_map);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
ctx->map_fds[i] = !fd ? -1 : fd;
|
||||
}
|
||||
|
||||
for (i = 0; have_map_in_map && i < ctx->map_num; i++) {
|
||||
if (ctx->map_fds[i] >= 0)
|
||||
continue;
|
||||
|
||||
map_name = bpf_map_fetch_name(ctx, i);
|
||||
if (!map_name)
|
||||
return -EIO;
|
||||
|
||||
fd = bpf_map_attach(map_name, &ctx->maps[i], ctx,
|
||||
NULL);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
ctx->map_fds[i] = fd;
|
||||
}
|
||||
|
||||
for (i = 0; have_map_in_map && i < ctx->map_num; i++) {
|
||||
if (!ctx->maps[i].id ||
|
||||
ctx->maps[i].inner_id ||
|
||||
ctx->maps[i].inner_idx == -1)
|
||||
continue;
|
||||
|
||||
inner_fd = ctx->map_fds[i];
|
||||
inner_idx = ctx->maps[i].inner_idx;
|
||||
|
||||
for (j = 0; j < ctx->map_num; j++) {
|
||||
if (!bpf_is_map_in_map_type(&ctx->maps[j]))
|
||||
continue;
|
||||
if (ctx->maps[j].inner_id != ctx->maps[i].id)
|
||||
continue;
|
||||
|
||||
ret = bpf_map_update(ctx->map_fds[j], &inner_idx,
|
||||
&inner_fd, BPF_ANY);
|
||||
if (ret < 0) {
|
||||
bpf_report_map_in_map(ctx->map_fds[j],
|
||||
inner_fd, inner_idx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue