Skip to content

Commit

Permalink
Update basic01 to be clean libxdp example
Browse files Browse the repository at this point in the history
Signed-off-by: Donald Hunter <donald.hunter@gmail.com>
  • Loading branch information
donaldh committed Mar 3, 2023
1 parent 1366836 commit 1ad462e
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 39 deletions.
1 change: 1 addition & 0 deletions basic01-xdp-pass/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ CLANG ?= clang
CC := gcc

COMMON_DIR = ../common
COMMON_OBJS += $(COMMON_DIR)/common_user_bpf_xdp.o

include $(COMMON_DIR)/common.mk
66 changes: 52 additions & 14 deletions basic01-xdp-pass/README.org
Original file line number Diff line number Diff line change
Expand Up @@ -87,29 +87,38 @@ is our C-function name.

As you should understand by now, the BPF byte code is stored in an ELF file.
To load this into the kernel, user space needs an ELF loader to read the
file and pass it into the kernel in the right format. The *libbpf* library
provides both an ELF loader and several BPF helper functions. In this
tutorial you will learn how to write C code using this library, which is
where our libelf-devel dependency comes from.
file and pass it into the kernel in the right format.

The *libbpf* library provides both an ELF loader and several BPF helper
functions. It understands BPF Type Format (BTF) and implements [[https://nakryiko.com/posts/bpf-core-reference-guide/][CO-RE]]
relocation as part of ELF loading, which is where our libelf-devel
dependency comes from.

The *libxdp* library provides helper functions for loading and installing
XDP programs using the XDP multi-dispatch protocol and helper functions for
using AF_XDP sockets. The *libxdp* library uses *libbpf* and adds extra
features on top. In this tutorial you will learn how to write C code using
the *libxdp* and *libbpf* libraries.

The C code in [[file:xdp_pass_user.c]] (which gets compiled to the program
=xdp_pass_user=) shows how to write a BPF loader specifically for our
=xdp_pass_kern.o= ELF file. This loader attaches the program in the ELF file
to an XDP hook on a network device.

** Loading via iproute2 ip

It does seem overkill to write a C program to simply load and attach a
specific BPF-program. However, we still include this in the tutorial
since it will help you integrate BPF into other Open Source projects.

As an alternative to writing a new loader, the standard iproute2 tool also
contains a BPF ELF loader. However, this loader is not based on libbpf,
which unfortunately makes it incompatible when starting to use BPF maps.
There are some alternatives to writing a new loader:

- The standard iproute2 tool
- The xdp-loader from xdp-tools

The iproute2 loader can be used with the standard =ip= tool; so in this case
you can actually load our ELF-file =xdp_pass_kern.o= (where we named our
ELF section "xdp") like this:
** Loading via iproute2 ip

Iproute2 provides libbpf based BPF loading capability that can be used with
the standard =ip= tool; so in this case you can actually load our ELF-file
=xdp_pass_kern.o= (where we named our ELF section "xdp") like this:

#+begin_example sh
ip link set dev lo xdpgeneric obj xdp_pass_kern.o sec xdp
Expand All @@ -121,20 +130,48 @@ Listing the device via =ip link show= also shows the XDP info:
$ ip link show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 xdpgeneric qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
prog/xdp id 220 tag 3b185187f1855c4c jited
prog/xdp id name xdp_prog_simple 220 tag 3b185187f1855c4c jited
#+end_example

Removing the XDP program again from the device:
#+begin_example
ip link set dev lo xdpgeneric off
#+end_example

It is important to note that the =ip= tool from iproute2 does not implement
the XDP multi-dispatch protocol. When we use this tool, our program gets
attached directly to the =lo= interface.

** Loading using xdp-loader

The xdp-tools project provides the =xdp-loader= tool which has commands for
loading, unloading and showing the status of loaded XDP programs.

We can load our =xdp_pass_kern.o= program and attach it using the XDP
multi-dispatch protocol like this:

#+begin_example sh
xdp-loader load -m skb lo xdp_pass_kern.o
#+end_example

We can show the status of the XDP programs attached to the device:

#+begin_example sh
$ xdp-loader status lo
CURRENT XDP PROGRAM STATUS:

Interface Prio Program name Mode ID Tag Chain actions
--------------------------------------------------------------------------------------
lo xdp_dispatcher skb 486 94d5f00c20184d17
=> 50 xdp_prog_simple 493 3b185187f1855c4c XDP_PASS
#+end_example

** Loading using xdp_pass_user

To load the program using our own loader, simply issue this command:

#+begin_example sh
$ sudo ./xdp_pass_user --dev lo --skb-mode
$ sudo ./xdp_pass_user --dev lo
Success: Loading XDP prog name:xdp_prog_simple(id:225) on device:lo(ifindex:1)
#+end_example

Expand All @@ -160,3 +197,4 @@ You can list XDP programs on the device using different commands, and verify
that the program ID is the same:
- =ip link list dev lo=
- =bpftool net list dev lo=
- =xdp-loader status lo=
61 changes: 36 additions & 25 deletions basic01-xdp-pass/xdp_pass_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ static const char *__doc__ = "Simple XDP prog doing XDP_PASS\n";
#include <linux/if_link.h> /* depend on kernel-headers installed */

#include "../common/common_params.h"
#include "../common/common_user_bpf_xdp.h"

static const struct option_wrapper long_options[] = {
{{"help", no_argument, NULL, 'h' },
Expand All @@ -33,32 +34,38 @@ static const struct option_wrapper long_options[] = {
{{"auto-mode", no_argument, NULL, 'A' },
"Auto-detect SKB or native mode"},

{{"force", no_argument, NULL, 'F' },
"Force install, replacing existing program on interface"},

{{"unload", required_argument, NULL, 'U' },
{{"unload", required_argument, NULL, 'U' },
"Unload XDP program <id> instead of loading", "<id>"},

{{"unload-all", no_argument, NULL, 4 },
"Unload all XDP programs on device"},

{{0, 0, NULL, 0 }, NULL, false}
};


int main(int argc, char **argv)
{
DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
DECLARE_LIBXDP_OPTS(xdp_program_opts, xdp_opts, 0);
struct bpf_prog_info info = {};
__u32 info_len = sizeof(info);
char filename[] = "xdp_pass_kern.o";
char progname[] = "xdp_prog_simple";
struct xdp_program *prog;
char errmsg[1024];
int err = EXIT_SUCCESS;
struct xdp_program *p;
int prog_fd, err; // = EXIT_SUCCESS;

struct config cfg = {
.xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE,
.attach_mode = XDP_MODE_UNSPEC,
.ifindex = -1,
.do_unload = false,
};

DECLARE_LIBBPF_OPTS(bpf_object_open_opts, bpf_opts);
DECLARE_LIBXDP_OPTS(xdp_program_opts, xdp_opts,
.open_filename = filename,
.prog_name = progname,
.opts = &bpf_opts);

parse_cmdline_args(argc, argv, long_options, &cfg, __doc__);
/* Required option */
if (cfg.ifindex == -1) {
Expand All @@ -67,39 +74,43 @@ int main(int argc, char **argv)
return EXIT_FAIL_OPTION;
}

if (cfg.do_unload) {
xdp_opts.id = cfg.prog_id;
} else {
xdp_opts.open_filename = filename;
xdp_opts.prog_name = progname;
xdp_opts.opts = &opts;
/* Unload a program by prog_id, or
* unload all programs on net device
*/
if (cfg.do_unload || cfg.unload_all) {
err = do_unload(&cfg);
if (err) {
libxdp_strerror(err, errmsg, sizeof(errmsg));
fprintf(stderr, "Couldn't unload XDP program %s: %s\n",
progname, errmsg);
return err;
}

printf("Success: Unloading XDP prog name: %s\n", progname);
return EXIT_OK;
}

p = xdp_program__create(&xdp_opts);
err = libxdp_get_error(p);
/* Create an xdp_program froma a BPF ELF object file */
prog = xdp_program__create(&xdp_opts);
err = libxdp_get_error(prog);
if (err) {
libxdp_strerror(err, errmsg, sizeof(errmsg));
fprintf(stderr, "Couldn't get XDP program %s: %s\n",
progname, errmsg);
return err;
}

if (cfg.do_unload)
return xdp_program__detach(p, cfg.ifindex, cfg.attach_mode, 0);

err = xdp_program__attach(p, cfg.ifindex, cfg.attach_mode, 0);
/* Attach the xdp_program to the net device XDP hook */
err = xdp_program__attach(prog, cfg.ifindex, cfg.attach_mode, 0);
if (err) {
libxdp_strerror(err, errmsg, sizeof(errmsg));
fprintf(stderr, "Couldn't attach XDP program on iface '%s' : %s (%d)\n",
cfg.ifname, errmsg, err);
return err;
}

struct bpf_prog_info info = {};
__u32 info_len = sizeof(info);
int prog_fd = xdp_program__fd(p);

/* This step is not really needed , BPF-info via bpf-syscall */
prog_fd = xdp_program__fd(prog);
err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
if (err) {
fprintf(stderr, "ERR: can't get prog info - %s\n",
Expand Down
3 changes: 3 additions & 0 deletions common/common_params.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ void parse_cmdline_args(int argc, char **argv,
cfg->xsk_bind_flags &= ~XDP_COPY;
cfg->xsk_bind_flags |= XDP_ZEROCOPY;
break;
case 4: /* --unload-all */
cfg->unload_all = true;
break;
case 'h':
full_help = true;
/* fall-through */
Expand Down

0 comments on commit 1ad462e

Please sign in to comment.