Merge pull request #45 from xdp-project/vestas08_cleanups

Cleanups for AF_XDP-interaction code and btf_unit_test
This commit is contained in:
Jesper Dangaard Brouer
2022-04-27 09:23:19 +02:00
committed by GitHub
5 changed files with 361 additions and 61 deletions
+2
View File
@@ -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
+1 -8
View File
@@ -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);
+218
View File
@@ -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;
}
+73
View File
@@ -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 -53
View File
@@ -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;
}