Skip to content

Commit cc0f4fb

Browse files
committed
advanced03: Add example code
Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
1 parent b941ce5 commit cc0f4fb

File tree

8 files changed

+769
-18
lines changed

8 files changed

+769
-18
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ xdp_stats
5959
xdp_pass_user
6060
xdp_load_and_stats
6161
xdp_prog_user
62+
af_xdp_user
6263

6364
# tracing userspace programs
6465
trace_load_and_stats

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
22

3-
LESSONS = $(wildcard basic*) $(wildcard packet*) $(wildcard tracing??-*)
3+
LESSONS = $(wildcard basic*) $(wildcard packet*) $(wildcard tracing??-*) advanced03-AF_XDP
44
LESSONS_CLEAN = $(addsuffix _clean,$(LESSONS))
55

66
.PHONY: clean $(LESSONS) $(LESSONS_CLEAN)

advanced03-AF_XDP/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
2+
3+
4+
XDP_TARGETS := af_xdp_kern
5+
USER_TARGETS := af_xdp_user
6+
7+
LIBBPF_DIR = ../libbpf/src/
8+
COMMON_DIR = ../common/
9+
10+
include $(COMMON_DIR)/common.mk
11+
LIBS += -lpthread

advanced03-AF_XDP/README.org

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,21 @@ default spread flows with RSS-hashing over all available RX-queues. Thus,
6666
traffic likely not hitting queue you expect.
6767

6868
In order to fix that problem, you *MUST* configure the NIC to steer flow to a specific RX-queue.
69-
This can be done via ethtool or TC HW offloading filter setup.
69+
This can be done via ethtool or TC HW offloading filter setup.
7070

7171
The following example shows how to configure the NIC to steer all UDP ipv4 traffic
72-
to /RX-queue id/ 42:
72+
to /RX-queue id/ 42:
7373

7474
#+begin_example sh
7575
ethtool -N <interface> flow-type udp4 action 42
7676
#+end_example
7777

7878
The parameter /action/ specifies the id of the target /RX-queue/.
7979

80-
In general, a flow rule is composed of a matching criteria and an action.
80+
In general, a flow rule is composed of a matching criteria and an action.
8181
L2, L3 and L4 header values can be used to specify the matching criteria.
82-
For a comprehensive documentation, please check out the man page of ethtool.
83-
It documents all available header values that can be used as part of the matching criteria.
82+
For a comprehensive documentation, please check out the man page of ethtool.
83+
It documents all available header values that can be used as part of the matching criteria.
8484

8585

8686
Alternative work-arounds:
@@ -108,17 +108,85 @@ XDP_REDIRECT and XDP_PASS, then "copy-mode" can be relevant. As in
108108
"zero-copy" mode doing XDP_PASS have a fairly high cost, which involves
109109
allocating memory and copying over the frame.
110110

111-
* Missing elements
111+
* Assignments
112+
The end goal of this lesson is to build an AF_XDP program that will send
113+
packets to userspace and if they are IPv6 ping packets reply.
112114

113-
*Lesson incomplete*
115+
We will do using the automatically installed XDP program, but one of the
116+
assignments it to implement this manually.
114117

115-
This lesson is missing some assignments, but first we need to provide an
116-
AF_XDP sample program. The sample should use the libbpf API for AF_XDP
117-
sockets. It also need to include an actual XDP bpf program that is more
118-
robust, and demonstrate how to read the RXQ-info about the RX-queue number,
119-
and only redirect if the RX-queue id match, and maybe give stats on redirect
120-
vs. XDP_PASS (not matching RX-queue id) packets.
118+
** Assignment 1: Run the example program to eat all packets
119+
First, you need to set up the test lab environment and start an infinite
120+
ping. You do this by running the following:
121+
#+begin_example sh
122+
$ eval $(../testenv/testenv.sh alias)
123+
$ t setup --name veth-adv03
124+
$ t ping
125+
#+end_example
126+
127+
Now you can start the af_xdp_user application and see all the pings being
128+
eaten by it:
129+
130+
#+begin_example sh
131+
$ sudo ./af_xdp_user -d veth-adv03
132+
[sudo] password for echaudro:
133+
AF_XDP RX: 2 pkts ( 1 pps) 0 Kbytes ( 0 Mbits/s) period:2.000185
134+
TX: 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000185
135+
136+
AF_XDP RX: 4 pkts ( 1 pps) 0 Kbytes ( 0 Mbits/s) period:2.000152
137+
TX: 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000152
138+
#+end_example
139+
140+
** Assignment 2: Write an XDP program to process every other packet
141+
For this exercise, you need to write an eBPF program that will count the
142+
packets received, and use this value to determine if the packet needs to be
143+
sent down the AF_XDP socket. We want every other packet to be sent to the
144+
AF_XDP socket.
145+
146+
This should result in every other ping packet being replied too. Here is the
147+
expected output from the ping command, notice the icmp_seq numbers:
148+
#+begin_example sh
149+
$ t ping
150+
Running ping from inside test environment:
151+
152+
PING fc00:dead:cafe:1::1(fc00:dead:cafe:1::1) 56 data bytes
153+
64 bytes from fc00:dead:cafe:1::1: icmp_seq=2 ttl=64 time=0.038 ms
154+
64 bytes from fc00:dead:cafe:1::1: icmp_seq=4 ttl=64 time=0.047 ms
155+
64 bytes from fc00:dead:cafe:1::1: icmp_seq=6 ttl=64 time=0.062 ms
156+
64 bytes from fc00:dead:cafe:1::1: icmp_seq=8 ttl=64 time=0.083 ms
157+
#+end_example
158+
159+
If you have your custom program ready you can bind it using the --filename
160+
option:
161+
162+
#+begin_example sh
163+
$ sudo ./af_xdp_user -d veth-adv03 --filename af_xdp_kern.o
164+
AF_XDP RX: 1 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000171
165+
TX: 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000171
166+
167+
AF_XDP RX: 2 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000133
168+
TX: 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000133
169+
#+end_example
170+
171+
Note that the full solution is included in the af_xdp_kern.c file.
172+
173+
174+
** Assignment 3: Write an userspace program to reply to IPv6 ping packets
175+
For the final exercise, you need to write some userspace code that will
176+
reply to the ping packets. This needs be done inside the process_packet()
177+
function.
178+
179+
Once you have done this all pings should receive a reply:
180+
#+begin_example sh
181+
$ sudo ./af_xdp_user -d veth-adv03
182+
AF_XDP RX: 2 pkts ( 1 pps) 0 Kbytes ( 0 Mbits/s) period:2.000175
183+
TX: 2 pkts ( 1 pps) 0 Kbytes ( 0 Mbits/s) period:2.000175
184+
185+
AF_XDP RX: 4 pkts ( 1 pps) 0 Kbytes ( 0 Mbits/s) period:2.000146
186+
TX: 4 pkts ( 1 pps) 0 Kbytes ( 0 Mbits/s) period:2.000146
187+
188+
AF_XDP RX: 6 pkts ( 1 pps) 0 Kbytes ( 0 Mbits/s) period:2.000118
189+
TX: 6 pkts ( 1 pps) 0 Kbytes ( 0 Mbits/s) period:2.000118
190+
#+end_example
121191

122-
Note: One of the reasons for providing a BPF C-code example is that the
123-
kernel removed =samples/bpf/xdpsock_kern.c=, when libbpf was extended with
124-
static BPF-instructions for AF_XDP redirect.
192+
Note that the full solution is present in the ad_xdp_user.c file.

advanced03-AF_XDP/af_xdp_kern.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
3+
#include <linux/bpf.h>
4+
5+
#include "bpf_helpers.h"
6+
7+
struct bpf_map_def SEC("maps") xsks_map = {
8+
.type = BPF_MAP_TYPE_XSKMAP,
9+
.key_size = sizeof(int),
10+
.value_size = sizeof(int),
11+
.max_entries = 64, /* Assume netdev has no more than 64 queues */
12+
};
13+
14+
struct bpf_map_def SEC("maps") xdp_stats_map = {
15+
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
16+
.key_size = sizeof(int),
17+
.value_size = sizeof(__u32),
18+
.max_entries = 64,
19+
};
20+
21+
SEC("xdp_sock")
22+
int xdp_sock_prog(struct xdp_md *ctx)
23+
{
24+
int index = ctx->rx_queue_index;
25+
__u32 *pkt_count;
26+
27+
pkt_count = bpf_map_lookup_elem(&xdp_stats_map, &index);
28+
if (pkt_count) {
29+
30+
/* We pass every other packet */
31+
if ((*pkt_count)++ & 1)
32+
return XDP_PASS;
33+
}
34+
35+
/* A set entry here means that the correspnding queue_id
36+
* has an active AF_XDP socket bound to it. */
37+
if (bpf_map_lookup_elem(&xsks_map, &index))
38+
return bpf_redirect_map(&xsks_map, index, 0);
39+
40+
return XDP_PASS;
41+
}
42+
43+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)