mirror of
https://github.com/xdp-project/bpf-examples.git
synced 2024-05-06 15:54:53 +00:00
Merge pull request #45 from xdp-project/vestas08_cleanups
Cleanups for AF_XDP-interaction code and btf_unit_test
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
|
||||
USER_TARGETS := af_xdp_user
|
||||
USER_TARGETS += btf_unit_test
|
||||
BPF_TARGETS := af_xdp_kern
|
||||
BPF_TARGETS += btf_unit_test_bpf
|
||||
|
||||
# Define C-code objects USER_TARGETS needs
|
||||
USER_TARGETS_OBJS := common_params.o common_user_bpf_xdp.o
|
||||
|
||||
@@ -143,18 +143,11 @@ struct xsk_btf_info *setup_btf_info(struct btf *btf,
|
||||
|
||||
err = xsk_btf__init_xdp_hint(btf, struct_name, &xbi);
|
||||
if (err) {
|
||||
fprintf(stderr, "WARN(%d): Cannot BTF find struct:%s\n",
|
||||
fprintf(stderr, "WARN(%d): Cannot BTF locate valid struct:%s\n",
|
||||
err, struct_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!xsk_btf__has_field("btf_id", xbi)) {
|
||||
fprintf(stderr, "ERR: %s doesn't contain member btf_id\n",
|
||||
struct_name);
|
||||
xsk_btf__free_xdp_hint(xbi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (debug_meta)
|
||||
printf("Setup BTF based XDP hints for struct: %s\n",
|
||||
struct_name);
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
/* Code exercising BTF userspace decoding */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
#include <bpf/btf.h> /* provided by libbpf */
|
||||
|
||||
#include "lib_xsk_extend.h"
|
||||
|
||||
static int verbose = 1;
|
||||
|
||||
/* Exit return codes - can be used by scripts looking at exit code */
|
||||
#define EXIT_OK 0 /* == EXIT_SUCCESS (stdlib.h) man exit(3) */
|
||||
#define EXIT_FAIL 1 /* == EXIT_FAILURE (stdlib.h) man exit(3) */
|
||||
#define EXIT_FAIL_OPTION 2
|
||||
#define EXIT_FAIL_XDP 3
|
||||
#define EXIT_FAIL_BPF 4
|
||||
#define EXIT_FAIL_BTF 5
|
||||
|
||||
struct bpf_object *load_bpf_object(const char *filename) {
|
||||
struct bpf_object *obj;
|
||||
char buf[100];
|
||||
int err;
|
||||
|
||||
obj = bpf_object__open_file(filename, NULL);
|
||||
err = libbpf_get_error(obj);
|
||||
if (err) {
|
||||
libbpf_strerror(err, buf, sizeof(buf));
|
||||
printf("Error opening file: %s\n", buf);
|
||||
return NULL;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* BTF setup XDP-hints
|
||||
* -------------------
|
||||
* Setup the data structures for accessing the XDP-hints provided by
|
||||
* kernel side BPF-prog via decoding BTF-info provided in BPF
|
||||
* ELF-object file.
|
||||
*/
|
||||
|
||||
/* This struct BTF mirrors kernel-side struct xdp_hints_rx_time */
|
||||
struct xdp_hints_rx_time {
|
||||
__u32 btf_type_id; /* cached xsk_btf__btf_type_id(xbi) */
|
||||
struct xsk_btf_info *xbi;
|
||||
struct xsk_btf_member rx_ktime;
|
||||
struct xsk_btf_member xdp_rx_cpu;
|
||||
} xdp_hints_rx_time = { 0 };
|
||||
|
||||
/* This struct BTF mirrors kernel-side struct xdp_hints_mark */
|
||||
struct xdp_hints_mark {
|
||||
__u32 btf_type_id; /* cached xsk_btf__btf_type_id(xbi) */
|
||||
struct xsk_btf_info *xbi;
|
||||
struct xsk_btf_member mark;
|
||||
} xdp_hints_mark = { 0 };
|
||||
|
||||
struct xsk_btf_info *setup_btf_info(struct btf *btf,
|
||||
const char *struct_name,
|
||||
int *errval)
|
||||
{
|
||||
struct xsk_btf_info *xbi = NULL;
|
||||
int err;
|
||||
|
||||
err = xsk_btf__init_xdp_hint(btf, struct_name, &xbi);
|
||||
if (err) {
|
||||
if (errval) {
|
||||
/* Expect caller to handle err detection */
|
||||
*errval = err;
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"WARN(%d): Cannot BTF locate valid struct:%s\n",
|
||||
err, struct_name);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
int btf_id = xsk_btf__btf_type_id(xbi);
|
||||
printf("Setup BTF based XDP hints for (btf_id:%d) struct: %s\n",
|
||||
btf_id, struct_name);
|
||||
}
|
||||
|
||||
return xbi;
|
||||
}
|
||||
|
||||
int init_btf_info_via_bpf_object(struct bpf_object *bpf_obj)
|
||||
{
|
||||
struct btf *btf = bpf_object__btf(bpf_obj);
|
||||
struct xsk_btf_info *xbi;
|
||||
|
||||
xbi = setup_btf_info(btf, "xdp_hints_rx_time", NULL);
|
||||
if (xbi) {
|
||||
/* Lookup info on required member "rx_ktime" */
|
||||
if (!xsk_btf__field_member("rx_ktime", xbi,
|
||||
&xdp_hints_rx_time.rx_ktime))
|
||||
return -EBADSLT;
|
||||
if (!xsk_btf__field_member("xdp_rx_cpu", xbi,
|
||||
&xdp_hints_rx_time.xdp_rx_cpu))
|
||||
return -EBADSLT;
|
||||
xdp_hints_rx_time.btf_type_id = xsk_btf__btf_type_id(xbi);
|
||||
xdp_hints_rx_time.xbi = xbi;
|
||||
}
|
||||
/* Remember to cleanup later: xsk_btf__free_xdp_hint(xbi); */
|
||||
|
||||
xbi = setup_btf_info(btf, "xdp_hints_mark", NULL);
|
||||
if (xbi) {
|
||||
if (!xsk_btf__field_member("mark", xbi, &xdp_hints_mark.mark))
|
||||
return -EBADSLT;
|
||||
xdp_hints_mark.btf_type_id = xsk_btf__btf_type_id(xbi);
|
||||
xdp_hints_mark.xbi = xbi;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test01_normal()
|
||||
{
|
||||
struct bpf_object *bpf_obj;
|
||||
int err = 0;
|
||||
|
||||
bpf_obj = load_bpf_object("af_xdp_kern.o");
|
||||
if (!bpf_obj)
|
||||
return EXIT_FAIL_BPF;
|
||||
|
||||
err = init_btf_info_via_bpf_object(bpf_obj);
|
||||
if (err) {
|
||||
if (verbose)
|
||||
printf("ERR(%d): Failed loading BTF info", err);
|
||||
bpf_object__close(bpf_obj);
|
||||
return EXIT_FAIL_BTF;
|
||||
}
|
||||
/* Teardown structs and memory again */
|
||||
xsk_btf__free_xdp_hint(xdp_hints_rx_time.xbi);
|
||||
xsk_btf__free_xdp_hint(xdp_hints_mark.xbi);
|
||||
bpf_object__close(bpf_obj);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
int helper_expect_invalid_btf_id(struct btf *btf,
|
||||
const char *xdp_hint_name)
|
||||
{
|
||||
struct xsk_btf_info *xbi;
|
||||
int ret = EXIT_OK;
|
||||
int errval = 0;
|
||||
|
||||
xbi = setup_btf_info(btf, xdp_hint_name, &errval);
|
||||
if (xbi) {
|
||||
/* Unexpected success - as hints layout should be invalid */
|
||||
printf(" - Unexpected success in test that should fail\n");
|
||||
xsk_btf__free_xdp_hint(xbi);
|
||||
ret = EXIT_FAIL_BPF;
|
||||
goto out;
|
||||
}
|
||||
if (errval != -EOVERFLOW) {
|
||||
/* Expecting failure with EOVERFLOW as btf_id not last member */
|
||||
printf("Unexpect FAIL - with errno:%d\n", errval);
|
||||
ret = EXIT_FAIL_BTF;
|
||||
goto out;
|
||||
}
|
||||
if (verbose) {
|
||||
printf("SUCCESS - "
|
||||
"detect btf_id not last member in struct %s\n",
|
||||
xdp_hint_name);
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int test02_should_fail()
|
||||
{
|
||||
const char *xdp_hint01 = "xdp_hints_fail001";
|
||||
const char *xdp_hint02 = "xdp_hints_fail002";
|
||||
struct bpf_object *bpf_obj;
|
||||
int ret = EXIT_OK;
|
||||
struct btf *btf;
|
||||
|
||||
bpf_obj = load_bpf_object("btf_unit_test_bpf.o");
|
||||
if (!bpf_obj)
|
||||
return EXIT_FAIL_BPF;
|
||||
|
||||
btf = bpf_object__btf(bpf_obj);
|
||||
|
||||
ret = helper_expect_invalid_btf_id(btf, xdp_hint01);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = helper_expect_invalid_btf_id(btf, xdp_hint02);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
bpf_object__close(bpf_obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = test01_normal();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = test02_should_fail();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
#include <bpf/bpf_core_read.h> /* bpf_core_type_id_local */
|
||||
|
||||
/*
|
||||
* The xdp_hints_xxx struct's are stored in the XDP 'data_meta' area,
|
||||
* which is located just in-front-of the raw packet payload data.
|
||||
*
|
||||
* Explaining the struct attribute's:
|
||||
* ----------------------------------
|
||||
* The struct must be 4 byte aligned (kernel requirement), which here
|
||||
* is enforced by the struct __attribute__((aligned(4))).
|
||||
*
|
||||
* To avoid any C-struct padding attribute "packed" is used.
|
||||
*
|
||||
* NOTICE: Do NOT define __attribute__((preserve_access_index)) here,
|
||||
* as libbpf will try to find a matching kernel data-structure,
|
||||
* e.g. it will cause BPF-prog loading step to fail (with invalid func
|
||||
* unknown#195896080 which is 0xbad2310 in hex for "bad relo").
|
||||
*/
|
||||
|
||||
struct xdp_hints_fail001 {
|
||||
__u64 hash64;
|
||||
__u32 btf_id;
|
||||
__u32 pad; /* Pad that breaks btf_id as last member */
|
||||
} __attribute__((aligned(4))) __attribute__((packed));
|
||||
|
||||
/* Notice struct is without attribute "packed", thus (64-bit) C-compiler will
|
||||
* add padding. This will cause btf_id to NOT be the last member (which is a
|
||||
* requirement).
|
||||
*/
|
||||
struct xdp_hints_fail002 {
|
||||
__u64 hash64;
|
||||
__u32 btf_id;
|
||||
} __attribute__((aligned(4))) /* not packed */;
|
||||
|
||||
|
||||
SEC("xdp")
|
||||
int xdp_prog_fail001(struct xdp_md *ctx)
|
||||
{
|
||||
struct xdp_hints_fail001 *meta;
|
||||
void *data;
|
||||
int err;
|
||||
|
||||
err = bpf_xdp_adjust_meta(ctx, -(int)sizeof(*meta));
|
||||
if (err)
|
||||
return XDP_ABORTED;
|
||||
|
||||
data = (void *)(unsigned long)ctx->data;
|
||||
meta = (void *)(unsigned long)ctx->data_meta;
|
||||
if (meta + 1 > data) /* Verify meta area is accessible */
|
||||
return XDP_ABORTED;
|
||||
|
||||
meta->btf_id = bpf_core_type_id_local(struct xdp_hints_fail001);
|
||||
meta->hash64 = 0x4142434445464748;
|
||||
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
SEC("xdp")
|
||||
int xdp_prog_fail002(struct xdp_md *ctx)
|
||||
{
|
||||
struct xdp_hints_fail002 f002;
|
||||
f002.btf_id = bpf_core_type_id_local(struct xdp_hints_fail002);
|
||||
if (f002.btf_id == 0)
|
||||
return XDP_ABORTED;
|
||||
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -67,59 +67,6 @@ static bool __xsk_equal_fn(const void *k1, const void *k2, void *ctx)
|
||||
return k1 == k2;
|
||||
}
|
||||
|
||||
int xsk_btf__init_xdp_hint(struct btf *btf_obj,
|
||||
const char *xdp_hints_name,
|
||||
struct xsk_btf_info **xbi)
|
||||
{
|
||||
const struct btf_member *m;
|
||||
const struct btf_type *t;
|
||||
unsigned short vlen;
|
||||
int i, id, ret = 0;
|
||||
|
||||
if (!xbi)
|
||||
return -EINVAL;
|
||||
|
||||
/* Require XDP-hints are defined as a struct */
|
||||
id = btf__find_by_name_kind(btf_obj, xdp_hints_name, BTF_KIND_STRUCT);
|
||||
if (id < 0) {
|
||||
ret = id;
|
||||
goto error_btf;
|
||||
}
|
||||
|
||||
t = btf__type_by_id(btf_obj, id);
|
||||
|
||||
*xbi = malloc(sizeof(**xbi));
|
||||
if (!*xbi) {
|
||||
ret = -ENOMEM;
|
||||
goto error_btf;
|
||||
}
|
||||
|
||||
hashmap__init(&(*xbi)->map, __xsk_hash_fn, __xsk_equal_fn, NULL);
|
||||
|
||||
/* Validate no BTF field is a bitfield */
|
||||
m = btf_members(t);
|
||||
vlen = BTF_INFO_VLEN(t->info);
|
||||
for (i = 0; i < vlen; i++, m++) {
|
||||
if (BTF_MEMBER_BITFIELD_SIZE(m->offset)) {
|
||||
ret = -ENOTSUP;
|
||||
goto error_entry;
|
||||
}
|
||||
}
|
||||
|
||||
(*xbi)->btf = btf_obj;
|
||||
(*xbi)->type = t;
|
||||
(*xbi)->btf_type_id = id;
|
||||
|
||||
return ret;
|
||||
|
||||
error_entry:
|
||||
__xsk_btf_free_hash(*xbi);
|
||||
free(*xbi);
|
||||
|
||||
error_btf:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __xsk_btf_field_entry(struct xsk_btf_info *xbi, const char *field,
|
||||
struct xsk_btf_member *entry)
|
||||
{
|
||||
@@ -219,3 +166,70 @@ int xsk_btf__read_field(void **dest, size_t size, const char *field,
|
||||
xsk_btf__read(dest, size, entry, xbi,addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xsk_btf__init_xdp_hint(struct btf *btf_obj,
|
||||
const char *xdp_hints_name,
|
||||
struct xsk_btf_info **xbi)
|
||||
{
|
||||
struct xsk_btf_member btf_id_member;
|
||||
const struct btf_member *m;
|
||||
const struct btf_type *t;
|
||||
unsigned short vlen;
|
||||
__u32 member_end;
|
||||
int i, id, err = 0;
|
||||
|
||||
if (!xbi)
|
||||
return -EINVAL;
|
||||
|
||||
/* Require XDP-hints are defined as a struct */
|
||||
id = btf__find_by_name_kind(btf_obj, xdp_hints_name, BTF_KIND_STRUCT);
|
||||
if (id < 0) {
|
||||
err = id;
|
||||
goto error_btf;
|
||||
}
|
||||
|
||||
t = btf__type_by_id(btf_obj, id);
|
||||
|
||||
*xbi = malloc(sizeof(**xbi));
|
||||
if (!*xbi) {
|
||||
err = -ENOMEM;
|
||||
goto error_btf;
|
||||
}
|
||||
|
||||
hashmap__init(&(*xbi)->map, __xsk_hash_fn, __xsk_equal_fn, NULL);
|
||||
|
||||
/* Validate no BTF field is a bitfield */
|
||||
m = btf_members(t);
|
||||
vlen = BTF_INFO_VLEN(t->info);
|
||||
for (i = 0; i < vlen; i++, m++) {
|
||||
if (BTF_MEMBER_BITFIELD_SIZE(m->offset)) {
|
||||
err = -ENOTSUP;
|
||||
goto error_entry;
|
||||
}
|
||||
}
|
||||
|
||||
(*xbi)->btf = btf_obj;
|
||||
(*xbi)->type = t;
|
||||
(*xbi)->btf_type_id = id;
|
||||
|
||||
/* Validate 'btf_id' member MUST exist */
|
||||
err = __xsk_btf_field_entry((*xbi), "btf_id", &btf_id_member);
|
||||
if (err)
|
||||
goto error_entry;
|
||||
|
||||
/* Validate 'btf_id' is last member */
|
||||
member_end = btf_id_member.offset + btf_id_member.size;
|
||||
if (t->size != member_end) {
|
||||
/* Situation can happen if compiler adds struct padding */
|
||||
err = -EOVERFLOW;
|
||||
goto error_entry;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_entry:
|
||||
__xsk_btf_free_hash(*xbi);
|
||||
free(*xbi);
|
||||
error_btf:
|
||||
return err;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user