xdpcapを使ってみる

タイトル通り。XDPを利用するとLinuxの通常のネットワークスタック前に処理されます。 そのため意図しない XDP_ABORTED XDP_DROP などはそもそもtcpdumpで観察できません。 そこでcloudflare/xdpcapを利用します。 これは pin したマップにXDPプログラムの返却時にパケットとステータス(XDP_DROP, XDP_PASS, …)を保存してツールから扱えるようにしてくれます。

準備

xdp-tutorialのbasic02をもとに実験しました。 使うにあたって変更したファイルはXDPプログラム, ローダー, Makefileの3つです。

自動で保存されるわけではないのでXDPプログラムをパケットを保存するよう変更しないとなりません。保存先となるマップをpinするためにローダーを変更しました。これはbpftoolなど外部ツールを使えば必要ではありません。 最後にXDPプログラムでxdpcapのヘッダを読み込むためにMakefileで BPF_CFLAGS にパスを追加しました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
diff --git a/basic02-prog-by-name/xdp_prog_kern.c b/basic02-prog-by-name/xdp_prog_kern.c
index 641a684..7d2ba88 100644
--- a/basic02-prog-by-name/xdp_prog_kern.c
+++ b/basic02-prog-by-name/xdp_prog_kern.c
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #include <linux/bpf.h>
 #include <bpf/bpf_helpers.h>
+#include "hook.h"

 /* Notice how this XDP/BPF-program contains several programs in the same source
  * file. These will each get their own section in the ELF file, and via libbpf
@@ -14,16 +15,26 @@
  * C-function names (below the SEC define).
  */

+
+// https://github.com/cloudflare/xdpcap
+struct bpf_map_def SEC("maps") xdpcap_hook = XDPCAP_HOOK();
+
 SEC("xdp_pass")
 int  xdp_pass_func(struct xdp_md *ctx)
 {
-       return XDP_PASS;
+       return xdpcap_exit(ctx, &xdpcap_hook, XDP_PASS);
 }

 SEC("xdp_drop")
 int  xdp_drop_func(struct xdp_md *ctx)
 {
-       return XDP_DROP;
+       return xdpcap_exit(ctx, &xdpcap_hook, XDP_DROP);
+}
+
+SEC("xdp_aborted")
+int  xdp_aborted_func(struct xdp_md *ctx)
+{
+       return xdpcap_exit(ctx, &xdpcap_hook, XDP_ABORTED);
 }

 /* Assignment#2: Add new XDP program section that use XDP_ABORTED */

作ったmapを読み取るためにpinしておく。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
diff --git a/basic02-prog-by-name/xdp_loader.c b/basic02-prog-by-name/xdp_loader.c
index e18d97f..d28657d 100644
--- a/basic02-prog-by-name/xdp_loader.c
+++ b/basic02-prog-by-name/xdp_loader.c
@@ -125,6 +125,8 @@ struct bpf_object *__load_bpf_and_xdp_attach(struct config *cfg)
                exit(EXIT_FAIL_BPF);
        }

+       bpf_object__pin(bpf_obj, "/sys/fs/bpf/tuto2");
+
        prog_fd = bpf_program__fd(bpf_prog);
        if (prog_fd <= 0) {
                fprintf(stderr, "ERR: bpf_program__fd failed\n");
1
2
3
4
5
6
7
8
9
diff --git a/basic02-prog-by-name/Makefile b/basic02-prog-by-name/Makefile
index c56f433..714a7f4 100644
--- a/basic02-prog-by-name/Makefile
+++ b/basic02-prog-by-name/Makefile
@@ -8,3 +8,4 @@ COMMON_DIR = ../common/

 include $(COMMON_DIR)/common.mk

+BPF_CFLAGS += -I$(HOME)/dev/src/github.com/cloudflare/xdpcap/

使ってみる

今回の修正ではローダーが後処理をちゃんとやっていないため、事前にpinしたマップを消してます。

1
2
3
4
5
6
# 別のXDPプログラムがある場合には消しておきます
sudo ip link set dev veth-basic03 xdp off
# 前回のアタッチ時にpinされしまったオブジェクトを消しておく
sudo rm -rf /sys/fs/bpf/tuto2
# 適当なネットワークデバイスにアタッチします
sudo ./xdp_loader --dev veth-basic03 --force --progsec xdp_drop

下のように xcpcap でpcapの保存場所を指定すると標準出力には返り値の統計が表示される。dump.pcapは通常のpcapファイルなのでtsharkなどで使えます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ sudo $(which xdpcap) /sys/fs/bpf/tuto2/xdpcap_hook dump.pcap
aborted: 0/0    drop: 1/1       pass: 0/0       tx: 0/0 redirect: 0/0   (received/matched packets)
aborted: 0/0    drop: 2/2       pass: 0/0       tx: 0/0 redirect: 0/0   (received/matched packets)
aborted: 0/0    drop: 3/3       pass: 0/0       tx: 0/0 redirect: 0/0   (received/matched packets)
aborted: 0/0    drop: 4/4       pass: 0/0       tx: 0/0 redirect: 0/0   (received/matched packets)
aborted: 0/0    drop: 5/5       pass: 0/0       tx: 0/0 redirect: 0/0   (received/matched packets)
aborted: 0/0    drop: 6/6       pass: 0/0       tx: 0/0 redirect: 0/0   (received/matched packets)
aborted: 0/0    drop: 7/7       pass: 0/0       tx: 0/0 redirect: 0/0   (received/matched packets)
aborted: 0/0    drop: 8/8       pass: 0/0       tx: 0/0 redirect: 0/0   (received/matched packets)
aborted: 0/0    drop: 9/9       pass: 0/0       tx: 0/0 redirect: 0/0   (received/matched packets)

stdoutにpcapの出力先を指定するとXDPプログラムの返り値は表示されません。したみたいにtcpdumpなどにつないで使います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ sudo $(which xdpcap) /sys/fs/bpf/tuto2/xdpcap_hook - | sudo tcpdump -r -
reading from file -, link-type EN10MB (Ethernet)
06:43:27.452099 IP6 fc00:dead:cafe:2::2 > dev-xdp: ICMP6, echo request, seq 170, length 64
06:43:28.476193 IP6 fc00:dead:cafe:2::2 > dev-xdp: ICMP6, echo request, seq 171, length 64
06:43:29.500133 IP6 fc00:dead:cafe:2::2 > dev-xdp: ICMP6, echo request, seq 172, length 64
06:43:30.523693 IP6 fc00:dead:cafe:2::2 > dev-xdp: ICMP6, echo request, seq 173, length 64
06:43:31.548052 IP6 fc00:dead:cafe:2::2 > dev-xdp: ICMP6, echo request, seq 174, length 64
06:43:32.571707 IP6 fc00:dead:cafe:2::2 > dev-xdp: ICMP6, echo request, seq 175, length 64
06:43:33.596387 IP6 fc00:dead:cafe:2::2 > dev-xdp: ICMP6, echo request, seq 176, length 64
^Ctcpdump: pcap_loop: error reading dump file: Interrupted system call

これから

XDPプログラムの中でパケットに変更を加えていたときにどうなるのか気になっています。 関数から抜けるタイミングで記録を取っているはずなので変更後のパケットが保存されるはずと思っています。 宛先アドレスを変更してDROPするなどして確認しておきたいです。

comments powered by Disqus