© steve latif
Aya Rust Tutorial Part 4: XDP Hello World
Welcome to part 4. So far we have installed the prerequisite in part 2,
built eBPF code that loads into the kernel and passes the
verifier. Let's continue on by building another XDP
program that will print a message every time it receives a packet
on an interface. As in part 3 we will use the loopback interface.
This will show how to print a message from the kernel. This is analogous
to using 'bpf_printk' in the eBPF programs in C, see [here](https://github.com/libbpf/libbpf-bootstrap/blob/master/examples/c/kprobe.bpf.c_ul
This will involve only a few more lines of code and
will follow the same build and deployment process as in the previous chapter.
Generating the code
As we did in part 3
generate the code using cargo generate
At the prompt select hello-world as the project name
Using the template, generate the code in directory hello-world\
, select the xdp option.
$ cargo generate https://github.com/aya-rs/aya-template
⚠️ Favorite `https://github.com/aya-rs/aya-template` not found in config, using it as a git repository: https://github.com/aya-rs/aya-template
🤷 Project Name: hello-world
🔧 Destination: /home/steve/articles/learning_ebpf_with_rust/xdp-tutorial/basic01-hello-world/hello-world ...
🔧 project-name: hello-world ...
🔧 Generating template ...
? 🤷 Which type of eBPF program? ›
cgroup_skb
cgroup_sockopt
cgroup_sysctl
classifier
fentry
fexit
kprobe
kretprobe
lsm
perf_event
raw_tracepoint
sk_msg
sock_ops
socket_filter
tp_btf
tracepoint
uprobe
uretprobe
❯ xdp
The generated code will if unaltered behave as a hello world program. In the
first part of this note we will modify the generated code, but come back
to it later
Modify the generated code in the file hello-world/hello-world-ebpf/src/main.rs
so that it looks like:
#![no_std]
#![no_main]
use aya_ebpf::{bindings::xdp_action, macros::xdp, programs::XdpContext};
use aya_ebpf::bpf_printk;
#[xdp]
pub fn hello_world(_ctx: XdpContext) -> u32 {
unsafe {
bpf_printk!(b"packet received!");
}
xdp_action::XDP_PASS
}
This code uses the unsafe macro bpf_printk
to print out a message every time a packet is received on the interface.
It returns XDP\_PASS\
bpf_printk is a useful tool for debugging. It is globally shared in the kernel
so other programs using it may disrupt its output.
Compile the code
cargo xtask build-ebpf
cargo build
Looking into the BPF-ELF object
As we did in the previous section, lets look at the generated eBPF byte code
$ llvm-readelf --sections target/bpfel-unknown-none/debug/hello-world
There are 7 section headers, starting at offset 0x2e0:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .strtab STRTAB 0000000000000000 000238 0000a2 00 0 0 1
[ 2] .text PROGBITS 0000000000000000 000040 000098 00 AX 0 0 8
[ 3] xdp PROGBITS 0000000000000000 0000d8 000030 00 AX 0 0 8
[ 4] .relxdp REL 0000000000000000 000228 000010 10 I 6 3 8
[ 5] .rodata PROGBITS 0000000000000000 000108 000013 00 A 0 0 1
[ 6] .symtab SYMTAB 0000000000000000 000120 000108 18 1 8 8
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
R (retain), p (processor specific)
As before we have an xdp section, lets disassemble that:
$ llvm-objdump --no-show-raw-insn --section=xdp -S target/bpfel-unknown-none/debug/hello-world
target/bpfel-unknown-none/debug/hello-world: file format elf64-bpf
Disassembly of section xdp:
0000000000000000 <hello_world>:
0: r1 = 0 ll
2: r2 = 19
3: call 6
4: r0 = 2
5: exit
Recall that the registers for eBPF:
- r0: Stores a return value of a function, and exit value for an eBPF program
- r1 - R5: Stores function arguments
- r6 - R9: For general purpose usage
- r10: Stores an address for stack frame
line 0 zeroes out the r1 register
line 2 sets r2 to 19 - the length of the output string
line 3 makes a system call, the mysterious 6 is the index of
bpf_helpers found in bpf.h
line 4 sets the exit value to 2 which corresponds to XDP_PASS
To run this let's use cargo
$ cargo xtask build-ebpf
$ cargo build
$ cargo xtask run -- i lo
To see output, open another terminal enable tracing:
echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_on
Then to see output
sudo cat /sys/kernel/debug/tracing/trace_pipe
From another terminal, ping the loopback interface
ping 127.0.0.1
You should see output being logged in the terminal where you ran the trace_pipe
command
$ sudo cat /sys/kernel/debug/tracing/trace_pipe
ping-75348 [000] ..s21 47214.233803: bpf_trace_printk: packet received!
ping-75348 [000] ..s21 47214.233815: bpf_trace_printk: packet received!
ping-75348 [007] ..s21 47215.236704: bpf_trace_printk: packet received!
ping-75348 [007] ..s21 47215.236737: bpf_trace_printk: packet received!
Let's return to the previous step where we generated the code. Run the
cargo generate
leave the generated code and don't change it
#![no_std]
#![no_main]
use aya_ebpf::{bindings::xdp_action, macros::xdp, programs::XdpContext};
use aya_log_ebpf::info;
#[xdp]
pub fn hello_world(ctx: XdpContext) -> u32 {
match try_hello_world(ctx) {
Ok(ret) => ret,
Err(_) => xdp_action::XDP_ABORTED,
}
}
fn try_hello_world(ctx: XdpContext) -> Result<u32, u32> {
info!(&ctx, "received a packet");
Ok(xdp_action::XDP_PASS)
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
unsafe { core::hint::unreachable_unchecked() }
}
Build and running it:
cargo xtask build-ebpf
cargo build
RUST_LOG=info cargo xtask run -- -i lo
Then running ping in another terminal:
ping 127.0.0.1
You should see this output in the window where you ran cargo xtask run
[2024-06-08T04:24:17Z INFO hello_world] Waiting for Ctrl-C...
[2024-06-08T04:24:21Z INFO hello_world] received a packet
[2024-06-08T04:24:21Z INFO hello_world] received a packet
[2024-06-08T04:24:22Z INFO hello_world] received a packet
...
This programs functions in the same way as the first one, but
there are significant differences in the code.
It looks more like idiomatic rust with only one unsafe block in
the panic handler.
However looking at a dump of the byte code:
$ llvm-objdump --section=xdp -S target/bpfel-unknown-none/debug/hello-world
target/bpfel-unknown-none/debug/hello-world: file format elf64-bpf
Disassembly of section xdp:
0000000000000000 <hello_world>:
0: r6 = r1
1: r7 = 0
2: *(u32 *)(r10 - 4) = r7
3: r2 = r10
4: r2 += -4
5: r1 = 0 ll
7: call 1
8: if r0 == 0 goto +166 <LBB0_2>
9: *(u8 *)(r0 + 2) = r7
10: r2 = 11
11: *(u8 *)(r0 + 1) = r2
12: r1 = 1
13: *(u8 *)(r0 + 0) = r1
14: r3 = r0
15: r3 += 3
16: r4 = 0 ll
18: r5 = *(u8 *)(r4 + 0)
19: *(u8 *)(r3 + 0) = r5
20: r5 = *(u8 *)(r4 + 1)
21: *(u8 *)(r3 + 1) = r5
22: r5 = *(u8 *)(r4 + 2)
23: *(u8 *)(r3 + 2) = r5
24: r5 = *(u8 *)(r4 + 3)
25: *(u8 *)(r3 + 3) = r5
26: r5 = *(u8 *)(r4 + 4)
27: *(u8 *)(r3 + 4) = r5
28: r5 = *(u8 *)(r4 + 5)
29: *(u8 *)(r3 + 5) = r5
30: r5 = *(u8 *)(r4 + 6)
31: *(u8 *)(r3 + 6) = r5
32: r5 = *(u8 *)(r4 + 7)
33: *(u8 *)(r3 + 7) = r5
34: r5 = *(u8 *)(r4 + 8)
35: *(u8 *)(r3 + 8) = r5
36: r5 = *(u8 *)(r4 + 9)
37: *(u8 *)(r3 + 9) = r5
38: r5 = *(u8 *)(r4 + 10)
39: *(u8 *)(r3 + 10) = r5
40: r3 = 3
41: *(u8 *)(r0 + 18) = r3
42: *(u8 *)(r0 + 17) = r3
43: r3 = 2
44: *(u8 *)(r0 + 14) = r3
45: *(u8 *)(r0 + 20) = r7
46: *(u8 *)(r0 + 19) = r2
47: *(u8 *)(r0 + 16) = r7
48: *(u8 *)(r0 + 15) = r1
49: r3 = r0
50: r3 += 21
51: r5 = *(u8 *)(r4 + 0)
52: *(u8 *)(r3 + 0) = r5
53: r5 = *(u8 *)(r4 + 1)
54: *(u8 *)(r3 + 1) = r5
55: r5 = *(u8 *)(r4 + 2)
56: *(u8 *)(r3 + 2) = r5
57: r5 = *(u8 *)(r4 + 3)
58: *(u8 *)(r3 + 3) = r5
59: r5 = *(u8 *)(r4 + 4)
60: *(u8 *)(r3 + 4) = r5
61: r5 = *(u8 *)(r4 + 5)
62: *(u8 *)(r3 + 5) = r5
63: r5 = *(u8 *)(r4 + 6)
64: *(u8 *)(r3 + 6) = r5
65: r5 = *(u8 *)(r4 + 7)
66: *(u8 *)(r3 + 7) = r5
67: r5 = *(u8 *)(r4 + 8)
68: *(u8 *)(r3 + 8) = r5
69: r5 = *(u8 *)(r4 + 9)
70: *(u8 *)(r3 + 9) = r5
71: r5 = *(u8 *)(r4 + 10)
72: *(u8 *)(r3 + 10) = r5
73: *(u8 *)(r0 + 33) = r2
74: *(u8 *)(r0 + 34) = r7
75: r2 = 4
76: *(u8 *)(r0 + 32) = r2
77: r3 = r0
78: r3 += 35
79: r4 = 11 ll
81: r5 = *(u8 *)(r4 + 0)
82: *(u8 *)(r3 + 0) = r5
83: r5 = *(u8 *)(r4 + 1)
84: *(u8 *)(r3 + 1) = r5
85: r5 = *(u8 *)(r4 + 2)
86: *(u8 *)(r3 + 2) = r5
87: r5 = *(u8 *)(r4 + 3)
88: *(u8 *)(r3 + 3) = r5
89: r5 = *(u8 *)(r4 + 4)
90: *(u8 *)(r3 + 4) = r5
91: r5 = *(u8 *)(r4 + 5)
92: *(u8 *)(r3 + 5) = r5
93: r5 = *(u8 *)(r4 + 6)
94: *(u8 *)(r3 + 6) = r5
95: r5 = *(u8 *)(r4 + 7)
96: *(u8 *)(r3 + 7) = r5
97: r5 = *(u8 *)(r4 + 8)
98: *(u8 *)(r3 + 8) = r5
99: r5 = *(u8 *)(r4 + 9)
100: *(u8 *)(r3 + 9) = r5
101: r5 = *(u8 *)(r4 + 10)
102: *(u8 *)(r3 + 10) = r5
103: *(u8 *)(r0 + 56) = r1
104: r1 = 8
105: *(u8 *)(r0 + 54) = r1
106: r1 = 16
107: *(u8 *)(r0 + 49) = r1
108: *(u8 *)(r0 + 66) = r7
109: *(u8 *)(r0 + 63) = r7
110: *(u8 *)(r0 + 62) = r7
111: *(u8 *)(r0 + 61) = r7
112: *(u8 *)(r0 + 60) = r7
113: *(u8 *)(r0 + 59) = r7
114: *(u8 *)(r0 + 58) = r7
115: *(u8 *)(r0 + 57) = r7
116: *(u8 *)(r0 + 55) = r7
117: *(u8 *)(r0 + 52) = r7
118: *(u8 *)(r0 + 51) = r7
119: *(u8 *)(r0 + 50) = r7
120: *(u8 *)(r0 + 48) = r7
121: *(u8 *)(r0 + 47) = r2
122: r1 = 17
123: *(u8 *)(r0 + 65) = r1
124: *(u8 *)(r0 + 64) = r1
125: r1 = 6
126: *(u8 *)(r0 + 53) = r1
127: r1 = 5
128: *(u8 *)(r0 + 46) = r1
129: r1 = r0
130: r1 += 67
131: r2 = 22 ll
133: r3 = *(u8 *)(r2 + 0)
134: *(u8 *)(r1 + 0) = r3
135: r3 = *(u8 *)(r2 + 1)
136: *(u8 *)(r1 + 1) = r3
137: r3 = *(u8 *)(r2 + 2)
138: *(u8 *)(r1 + 2) = r3
139: r3 = *(u8 *)(r2 + 3)
140: *(u8 *)(r1 + 3) = r3
141: r3 = *(u8 *)(r2 + 4)
142: *(u8 *)(r1 + 4) = r3
143: r3 = *(u8 *)(r2 + 5)
144: *(u8 *)(r1 + 5) = r3
145: r3 = *(u8 *)(r2 + 6)
146: *(u8 *)(r1 + 6) = r3
147: r3 = *(u8 *)(r2 + 7)
148: *(u8 *)(r1 + 7) = r3
149: r3 = *(u8 *)(r2 + 8)
150: *(u8 *)(r1 + 8) = r3
151: r3 = *(u8 *)(r2 + 9)
152: *(u8 *)(r1 + 9) = r3
153: r3 = *(u8 *)(r2 + 10)
154: *(u8 *)(r1 + 10) = r3
155: r3 = *(u8 *)(r2 + 11)
156: *(u8 *)(r1 + 11) = r3
157: r3 = *(u8 *)(r2 + 12)
158: *(u8 *)(r1 + 12) = r3
159: r3 = *(u8 *)(r2 + 13)
160: *(u8 *)(r1 + 13) = r3
161: r3 = *(u8 *)(r2 + 14)
162: *(u8 *)(r1 + 14) = r3
163: r3 = *(u8 *)(r2 + 15)
164: *(u8 *)(r1 + 15) = r3
165: r3 = *(u8 *)(r2 + 16)
166: *(u8 *)(r1 + 16) = r3
167: r1 = r6
168: r2 = 0 ll
170: r3 = 4294967295 ll
172: r4 = r0
173: r5 = 84
174: call 25
0000000000000578 <LBB0_2>:
175: r0 = 2
176: exit
So there's a lot here, we will defer a full explanation till later, note that there are now
two system calls on line 7 and line 174:
7: call 1
...
174: call 25
call 1
corresponds to map_lookup_elem
in bpf.h
call 25
corresponding to `perf_event_output
in bpf.h
Much of the rest of the byte code is setting up the stack to pass arguments.
Summary
- Seen how to set up and deploy a basic hello world program
- print out a message when a packet is received
- compared two different hello world programs
Top comments (0)