Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion headers/xdp/libxdp.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ bool xdp_multiprog__xdp_dev_bound(const struct xdp_multiprog *mp);
* @fd
* Load from BPF program with fd @fd
*
* @insns, @insns_cnt, @prog_name
* Create from a raw BPF instruction stream of @insns_cnt instructions.
* libxdp synthesises the BTF that the kernel requires to load the program
* as a dispatcher component, so callers that generate bytecode directly
* do not need a BTF-emitting toolchain.
*
* @prog_name is required.
*
* When one of these combinations is set, all other members of the opts struct
* must be zeroed out.
*/
Expand All @@ -163,9 +171,11 @@ struct xdp_program_opts {
const char *pin_path;
__u32 id;
int fd;
const struct bpf_insn *insns;
size_t insns_cnt;
size_t :0;
};
#define xdp_program_opts__last_field fd
#define xdp_program_opts__last_field insns_cnt

#define DECLARE_LIBXDP_OPTS DECLARE_LIBBPF_OPTS

Expand Down
2 changes: 1 addition & 1 deletion lib/libxdp/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ include $(LIB_DIR)/defines.mk
OBJDIR ?= .
SHARED_OBJDIR := $(OBJDIR)/sharedobjs
STATIC_OBJDIR := $(OBJDIR)/staticobjs
OBJS := libxdp.o xsk.o
OBJS := libxdp.o xsk.o xdp_elfgen.o
XDP_OBJS := xdp-dispatcher.o xsk_def_xdp_prog.o xsk_def_xdp_prog_5.3.o
EMBEDDED_XDP_OBJS := $(addsuffix .embed.o,$(basename $(XDP_OBJS)))
SHARED_OBJS := $(addprefix $(SHARED_OBJDIR)/,$(OBJS))
Expand Down
25 changes: 25 additions & 0 deletions lib/libxdp/README.org
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,31 @@ defaults to =/usr/lib/bpf= (alternatively =/usr/lib64/bpf= or =/usr/local/lib/bp
depending on which =PREFIX= libxdp was compiled with). This is convenient
for applications shipping pre-compiled eBPF object files.

A program can also be created from a raw BPF instruction stream, by calling
=xdp_program__create()= with the =insns= and =insns_cnt= members of =struct
xdp_program_opts= set; =prog_name= is required in this case:

#+begin_src C
struct bpf_insn insns[] = {
BPF_MOV64_IMM(BPF_REG_0, XDP_PASS),
BPF_EXIT_INSN(),
};
DECLARE_LIBXDP_OPTS(xdp_program_opts, opts,
.prog_name = "my_program",
.insns = insns,
.insns_cnt = 2);

prog = xdp_program__create(&opts);
#+end_src

This is for applications that generate bytecode at runtime instead of compiling
it ahead of time. The kernel will only accept a dispatcher component as a
=BPF_PROG_TYPE_EXT= program carrying BTF that describes its own signature, and
BTF cannot be added to a program once it has been loaded. Since every XDP
component has the same =int (struct xdp_md *)= signature, libxdp synthesises
the required BTF before loading, so callers do not need a BTF-emitting
toolchain of their own.

When libxdp is compiled with the DEBUG flag, it will additionally look
for BPF object files in the current directory, before checking the
system-wide directory. This can be convenient during development to avoid
Expand Down
27 changes: 27 additions & 0 deletions lib/libxdp/libxdp.3
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,33 @@ defaults to \fI/usr/lib/bpf\fP (alternatively \fI/usr/lib64/bpf\fP or \fI/usr/lo
depending on which \fIPREFIX\fP libxdp was compiled with). This is convenient
for applications shipping pre-compiled eBPF object files.
.PP
A program can also be created from a raw BPF instruction stream, by calling
\fIxdp_program__create()\fP with the \fIinsns\fP and \fIinsns_cnt\fP members of \fIstruct
xdp_program_opts\fP set; \fIprog_name\fP is required in this case:
.RS
.nf
\fCstruct bpf_insn insns[] = {
BPF_MOV64_IMM(BPF_REG_0, XDP_PASS),
BPF_EXIT_INSN(),
};
DECLARE_LIBXDP_OPTS(xdp_program_opts, opts,
.prog_name = "my_program",
.insns = insns,
.insns_cnt = 2);

prog = xdp_program__create(&opts);
\fP
.fi
.RE
.PP
This is for applications that generate bytecode at runtime instead of compiling
it ahead of time. The kernel will only accept a dispatcher component as a
\fIBPF_PROG_TYPE_EXT\fP program carrying BTF that describes its own signature, and
BTF cannot be added to a program once it has been loaded. Since every XDP
component has the same \fIint (struct xdp_md *)\fP signature, libxdp synthesises
the required BTF before loading, so callers do not need a BTF-emitting
toolchain of their own.
.PP
When libxdp is compiled with the DEBUG flag, it will additionally look
for BPF object files in the current directory, before checking the
system-wide directory. This can be convenient during development to avoid
Expand Down
80 changes: 74 additions & 6 deletions lib/libxdp/libxdp.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ struct xdp_program {
__u64 load_time;
bool from_external_obj;
bool is_frags;

/* synthesised ELF image backing bpf_obj, if any; libelf references it
* rather than copying, so it has to outlive the bpf_object
*/
void *synth_elf;
unsigned int run_prio;
unsigned int chain_call_actions; /* bitmap */

Expand Down Expand Up @@ -1155,6 +1160,9 @@ void xdp_program__close(struct xdp_program *xdp_prog)
btf__free(xdp_prog->btf);
}

/* must come after bpf_object__close(), which still reads the image */
free(xdp_prog->synth_elf);

free(xdp_prog);
}

Expand Down Expand Up @@ -1265,6 +1273,56 @@ static struct xdp_program *__xdp_program__open_file(const char *filename,
return xdp_prog;
}

/* Build a program from a raw instruction stream. The kernel will only accept
* a dispatcher component as BPF_PROG_TYPE_EXT if it carries BTF describing its
* own signature, and BTF cannot be added to a program after it is loaded, so
* synthesise the BTF here and wrap everything in an ELF image that libbpf can
* open like any other object.
*/
static struct xdp_program *xdp_program__from_insns(const struct bpf_insn *insns,
size_t insns_cnt,
const char *prog_name)
{
struct xdp_program *xdp_prog;
struct bpf_object *obj;
size_t elf_size;
void *elf;
int err;

if (!insns || !insns_cnt || !prog_name)
return ERR_PTR(-EINVAL);

elf = xdp_synthesise_elf(insns, insns_cnt, prog_name, &elf_size);
if (!elf)
return ERR_PTR(-errno);

obj = bpf_object__open_mem(elf, elf_size, NULL);
if (!obj) {
err = -errno;
pr_warn("Couldn't open synthesised object for program %s: %s\n",
prog_name, strerror(-err));
free(elf);
return ERR_PTR(err);
}

xdp_prog = xdp_program__create_from_obj(obj, NULL, prog_name, false);
if (IS_ERR(xdp_prog)) {
bpf_object__close(obj);
free(elf);
return xdp_prog;
}

/* libelf references the image rather than copying it, so hand
* ownership to the xdp_program and free it in xdp_program__close()
*/
xdp_prog->synth_elf = elf;

pr_debug("Created program %s from %zu synthesised instructions\n",
prog_name, insns_cnt);

return xdp_prog;
}

struct xdp_program *xdp_program__open_file(const char *filename,
const char *section_name,
struct bpf_object_open_opts *opts)
Expand Down Expand Up @@ -1481,8 +1539,10 @@ struct xdp_program *xdp_program__create(struct xdp_program_opts *opts)
{
const char *pin_path, *prog_name, *find_filename, *open_filename;
struct bpf_object_open_opts *obj_opts;
const struct bpf_insn *insns;
struct xdp_program *prog;
struct bpf_object *obj;
size_t insns_cnt;
__u32 id;
int fd;

Expand All @@ -1497,31 +1557,39 @@ struct xdp_program *xdp_program__create(struct xdp_program_opts *opts)
pin_path = OPTS_GET(opts, pin_path, NULL);
id = OPTS_GET(opts, id, 0);
fd = OPTS_GET(opts, fd, 0);
insns = OPTS_GET(opts, insns, NULL);
insns_cnt = OPTS_GET(opts, insns_cnt, 0);

if (obj) { /* prog_name is optional */
if (obj_opts || find_filename || open_filename || pin_path || id || fd)
if (obj_opts || find_filename || open_filename || pin_path || id || fd || insns)
goto err;
prog = xdp_program__create_from_obj(obj, NULL, prog_name, true);
} else if (find_filename) { /* prog_name, obj_opts is optional */
if (obj || open_filename || pin_path || id || fd)
if (obj || open_filename || pin_path || id || fd || insns)
goto err;
prog = __xdp_program__find_file(find_filename, NULL, prog_name, obj_opts);
} else if (open_filename) { /* prog_name, obj_opts is optional */
if (obj || find_filename || pin_path || id || fd)
if (obj || find_filename || pin_path || id || fd || insns)
goto err;
prog = __xdp_program__open_file(open_filename, NULL, prog_name, obj_opts);
} else if (pin_path) {
if (obj || obj_opts || prog_name || find_filename || open_filename || id || fd)
if (obj || obj_opts || prog_name || find_filename || open_filename || id || fd || insns)
goto err;
prog = xdp_program__from_pin(pin_path);
} else if (id) {
if (obj || obj_opts || prog_name || find_filename || open_filename || pin_path || fd)
if (obj || obj_opts || prog_name || find_filename || open_filename || pin_path || fd || insns)
goto err;
prog = xdp_program__from_id(id);
} else if (fd) {
if (obj || obj_opts || prog_name || find_filename || open_filename || pin_path || id)
if (obj || obj_opts || prog_name || find_filename || open_filename || pin_path || id || insns)
goto err;
prog = xdp_program__from_fd(fd);
} else if (insns) { /* prog_name and insns_cnt are required */
if (obj || obj_opts || find_filename || open_filename || pin_path || id || fd)
goto err;
if (!prog_name || !insns_cnt)
goto err;
prog = xdp_program__from_insns(insns, insns_cnt, prog_name);
} else {
goto err;
}
Expand Down
4 changes: 4 additions & 0 deletions lib/libxdp/libxdp_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ static inline void *libxdp_err_ptr(int err, bool ret_null)
return ERR_PTR(err);
}

LIBXDP_HIDE_SYMBOL void *xdp_synthesise_elf(const struct bpf_insn *insns,
size_t insn_cnt,
const char *prog_name,
size_t *out_size);
LIBXDP_HIDE_SYMBOL int xdp_lock_acquire(void);
LIBXDP_HIDE_SYMBOL int xdp_lock_release(int lock_fd);
LIBXDP_HIDE_SYMBOL int xdp_attach_fd(int prog_fd, int old_fd, int ifindex,
Expand Down
1 change: 1 addition & 0 deletions lib/libxdp/tests/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ test_xsk_non_privileged
test_link_detach
test_xsk_umem_flags
test_xdp_devbound
test_synth_insns
4 changes: 2 additions & 2 deletions lib/libxdp/tests/Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)

USER_TARGETS := test_xsk_refcnt test_xdp_frags test_link_detach test_xsk_umem_flags test_xdp_devbound test_xsk_map_leak
USER_TARGETS := test_xsk_refcnt test_xdp_frags test_link_detach test_xsk_umem_flags test_xdp_devbound test_xsk_map_leak test_synth_insns
STATIC_LINK_TARGETS := check_kern_compat test_dispatcher_versions
BPF_TARGETS := xdp_dispatcher_v1 xdp_dispatcher_v2 xdp_pass
USER_LIBS := -lpthread
USER_EXTRA_C := test_utils.c

EXTRA_DEPS += xdp_dispatcher.h
EXTRA_DEPS += xdp_dispatcher.h ../bpf_instr.h
EXTRA_USER_DEPS += test_utils.h $(USER_EXTRA_C)

TEST_FILE := ./test-libxdp.sh
Expand Down
14 changes: 13 additions & 1 deletion lib/libxdp/tests/test-libxdp.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)

ALL_TESTS="test_link_so test_link_a test_old_dispatcher test_xdp_devbound test_xdp_frags test_xsk_prog_refcnt_bpffs test_xsk_prog_refcnt_legacy test_xsk_non_privileged test_link_detach test_xsk_umem_flags test_xsk_map_leak"
ALL_TESTS="test_link_so test_link_a test_old_dispatcher test_xdp_devbound test_xdp_frags test_xsk_prog_refcnt_bpffs test_xsk_prog_refcnt_legacy test_xsk_non_privileged test_link_detach test_xsk_umem_flags test_xsk_map_leak test_synth_insns"

TESTS_DIR=$(dirname "${BASH_SOURCE[0]}")

Expand Down Expand Up @@ -141,6 +141,18 @@ test_link_detach()
ip link delete xdp_veth0
}

test_synth_insns()
{
if test ! -f $TESTS_DIR/test_synth_insns; then
exit "$SKIPPED_TEST"
fi
export LIBXDP_OBJECT_PATH=$TESTS_DIR

ip link add xdp_veth0 type veth peer name xdp_veth1
check_run $TESTS_DIR/test_synth_insns xdp_veth0
ip link delete xdp_veth0
}

test_xsk_umem_flags()
{
ip link add xdp_veth0 type veth peer name xdp_veth1
Expand Down
Loading