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 flags;
|
||||||
__u32 id;
|
__u32 id;
|
||||||
__u32 pinning;
|
__u32 pinning;
|
||||||
|
__u32 inner_id;
|
||||||
|
__u32 inner_idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* __BPF_ELF__ */
|
#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,
|
static int bpf_map_create(enum bpf_map_type type, uint32_t size_key,
|
||||||
uint32_t size_value, uint32_t max_elem,
|
uint32_t size_value, uint32_t max_elem,
|
||||||
uint32_t flags)
|
uint32_t flags, int inner_fd)
|
||||||
{
|
{
|
||||||
union bpf_attr attr = {};
|
union bpf_attr attr = {};
|
||||||
|
|
||||||
attr.map_type = type;
|
attr.map_type = type;
|
||||||
attr.key_size = size_key;
|
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.max_entries = max_elem;
|
||||||
attr.map_flags = flags;
|
attr.map_flags = flags;
|
||||||
|
attr.inner_map_fd = inner_fd;
|
||||||
|
|
||||||
return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||||
}
|
}
|
||||||
|
|
@ -1343,7 +1344,7 @@ retry:
|
||||||
|
|
||||||
static void bpf_map_report(int fd, const char *name,
|
static void bpf_map_report(int fd, const char *name,
|
||||||
const struct bpf_elf_map *map,
|
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,
|
fprintf(stderr, "Map object \'%s\' %s%s (%d)!\n", name,
|
||||||
fd < 0 ? "rejected: " : "loaded",
|
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, " - Identifier: %u\n", map->id);
|
||||||
fprintf(stderr, " - Pinning: %u\n", map->pinning);
|
fprintf(stderr, " - Pinning: %u\n", map->pinning);
|
||||||
fprintf(stderr, " - Size key: %u\n", map->size_key);
|
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, " - Max elems: %u\n", map->max_elem);
|
||||||
fprintf(stderr, " - Flags: %#x\n\n", map->flags);
|
fprintf(stderr, " - Flags: %#x\n\n", map->flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bpf_map_attach(const char *name, const struct bpf_elf_map *map,
|
static int bpf_find_map_id(const struct bpf_elf_ctx *ctx, uint32_t id)
|
||||||
struct bpf_elf_ctx *ctx)
|
|
||||||
{
|
{
|
||||||
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);
|
fd = bpf_probe_pinned(name, ctx, map->pinning);
|
||||||
if (fd > 0) {
|
if (fd > 0) {
|
||||||
|
|
@ -1381,11 +1458,29 @@ static int bpf_map_attach(const char *name, const struct bpf_elf_map *map,
|
||||||
return fd;
|
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;
|
errno = 0;
|
||||||
fd = bpf_map_create(map->type, map->size_key, map->size_value,
|
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) {
|
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)
|
if (fd < 0)
|
||||||
return fd;
|
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)
|
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;
|
const char *map_name;
|
||||||
int i, fd;
|
|
||||||
|
|
||||||
for (i = 0; i < ctx->map_num; i++) {
|
for (i = 0; i < ctx->map_num; i++) {
|
||||||
map_name = bpf_map_fetch_name(ctx, i);
|
map_name = bpf_map_fetch_name(ctx, i);
|
||||||
if (!map_name)
|
if (!map_name)
|
||||||
return -EIO;
|
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)
|
if (fd < 0)
|
||||||
return fd;
|
return fd;
|
||||||
|
|
||||||
ctx->map_fds[i] = 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue