Skip to content

Latest commit

 

History

History
225 lines (169 loc) · 8.25 KB

README.org

File metadata and controls

225 lines (169 loc) · 8.25 KB

Tutorial: Basic01 - loading your first BPF program

Welcome to the first step in this XDP tutorial.

The programming language for XDP is eBPF (Extended Berkeley Packet Filter) which we will just refer to as BPF. Thus, this tutorial will also be relevant for learning how to write other BPF programs; however, the main focus is on BPF programs that can be used in the XDP-hook. In this and the following couple of lessons we will be focusing on the basics to get up and running with BPF; the later lessons will then build on this to teach you how to do packet processing with XDP.

Since this is the first lesson, we will start out softly by not actually including any assignments. Instead, just read the text below and make sure you can load the program and that you understand what is going on.

Table of Contents

First step: setup dependencies

There are a number of setup dependencies, that are needed in order to compile the source code in this git repository. Please go read and complete the ../setup_dependencies.org guide if you haven’t already.

Then return here, and see if the next step compiles.

Compiling example code

If you completed the setup dependencies guide, then you should be able to simply run the make command, in this directory. (The Makefile and configure script will try to be nice and detect if you didn’t complete the setup steps).

Simple XDP code

The very simple XDP code used in this step is located in xdp_pass_kern.c, and displayed below:

SEC("xdp")
int  xdp_prog_simple(struct xdp_md *ctx)
{
        return XDP_PASS;
}

Compiling process

The LLVM+clang compiler turns this restricted-C code into BPF-byte-code and stores it in an ELF object file, named xdp_pass_kern.o.

Looking into the BPF-ELF object

You can inspect the contents of the xdp_pass_kern.o file with different tools like readelf or llvm-objdump. As the Makefile enables the debug option -g (LLVM version >= 4.0), the llvm-objdump tool can annotate assembler output with the original C code:

Run: llvm-objdump -S xdp_pass_kern.o

xdp_pass_kern.o:	file format ELF64-BPF

Disassembly of section xdp:
xdp_prog_simple:
; {
       0:	b7 00 00 00 02 00 00 00 	r0 = 2
; return XDP_PASS;
       1:	95 00 00 00 00 00 00 00 	exit

If you don’t want to see the raw BPF instructions add: --no-show-raw-insn. The define/enum XDP_PASS has a value of 2, as can be seen in the dump. The section name “xdp” was defined by SEC("xdp"), and the xdp_prog_simple: is our C-function name.

Loading and the XDP hook

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. It understands BPF Type Format (BTF) and implements 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 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.

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.

There are some alternatives to writing a new loader:

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

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:

$ sudo ip link set dev lo xdpgeneric obj xdp_pass_kern.o sec xdp

Listing the device via ip link show also shows the XDP info:

$ sudo 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 408 name xdp_prog_simple tag 3b185187f1855c4c jited

Should you run it without sudo, you would have less information:

$ 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 408

Removing the XDP program again from the device:

$ sudo ip link set dev lo xdpgeneric off

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:

$ sudo xdp-loader load -m skb lo xdp_pass_kern.o

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

$ sudo 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

Loading using xdp_pass_user

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

$ sudo ./xdp_pass_user --dev lo
Success: Loading XDP prog name:xdp_prog_simple(id:732) on device:lo(ifindex:1)

Loading the program again will add a second program instance to the XDP dispatcher on the interface.

$ sudo ./xdp_pass_user -d lo
Success: Loading XDP prog name:xdp_prog_simple(id:745) on device:lo(ifindex:1)

$ sudo xdp-loader status lo
CURRENT XDP PROGRAM STATUS:

Interface        Prio  Program name      Mode     ID   Tag               Chain actions
--------------------------------------------------------------------------------------
lo                     xdp_dispatcher    skb      738  94d5f00c20184d17
 =>              50     xdp_prog_simple           732  3b185187f1855c4c  XDP_PASS
 =>              50     xdp_prog_simple           745  3b185187f1855c4c  XDP_PASS

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

Unloading using xdp_pass_user

To unload the program using our own loader, use this command, with the id of the program to unload:

$ sudo ./xdp_pass_user --dev lo -U 745
Detaching XDP program with ID 745 from lo
Success: Unloading XDP prog name: xdp_prog_simple

You can also unload all programs from the XDP hook on hte device using this command:

$ sudo ./xdp_pass_user --dev lo --unload-all