Generic XDPを使えばXDP動作環境がお手軽に構築できるようになった
XDP (eXpress Data Path) ネタ。
パフォーマンス上の理由から、XDPのパケット処理はskb
が割り当てられる前にNICドライバから直接呼び出させる。そのため、XDPプログラムの動作にはNICドライバ側でのサポートが必要となる。
5月時点での対応ドライバリストは下記エントリ末尾に記載した。Linux kernel 4.12からixgbe
でXDPがサポートされる予定とは言え、ハードウェアの準備も必要であり、従来XDPの動作を気軽に試すことは少し難しかった。
そこでkernel 4.12から登場するのが Generic XDP と呼ばれる機能である。これはskb
が割り当てられた後にXDPのパケット処理を適用するものである*1。これによりNICドライバのサポートが無くともXDPプログラムを実行可能になった。パフォーマンスを犠牲にする代わりに、XDPプログラムの開発環境として使用したりXDP自体の間口を広げることを目的としている。
動作環境
前述した通り、Generic XDPはkernel 4.12でリリース予定である。4.12 rc3時点で既に最初のコミットに加えいくつかの微修正やサンプルコードの更新もマージされている。
プログラムのアタッチ時の動作
ネットワークインタフェースにXDPプログラムをアタッチする際*2、ドライバ側でのXDPサポート状況やフラグの値により、通常のNIC-level XDPを使用するか、あるいはGeneric XDPを使用するかが決定される。
- デフォルト: まずNIC-level XDPでのアタッチを試み、失敗したら (=NICドライバがサポート外) Generic XDPを使用する
XDP_FLAGS_SKB_MODE
を指定: Generic XDPのみを使用するXDP_FLAGS_DRV_MODE
を指定: NIC-level XDPでのアタッチのみを試みる。Generic XDPは使用しない
また、iproute2 (ip
コマンド) はバージョン4.10からXDPプログラムローダとしての機能を持っている。更に次回リリースではGeneric XDPに特化したロード (上記 XDP_FLAGS_SKB_MODE
に該当) もサポートされる予定である。
実際に試してみる
簡単なXDPプログラムを用意した上でそれをXDP非対応NICにアタッチし、Generic XDPが期待通り動作することを確認する。 また、XDPプログラムローダとして、過去のエントリで使用したiovisor/bccではなくiproute2を使用する。
- Kernel: 4.12 rc3 (Fedora rawhide 4.12.0-0.rc3.git0.2.fc27.x86_64)
- iproute2: 4.11+ (5a3ec4b)
- NIC: 8139cp (qemu-kvm RTL8139, ens4)
- NIC-level XDPは非サポート
まず、全ての受信パケットをドロップするXDPプログラムを用意し、コンパイルする:
#include <linux/bpf.h> #ifndef __section # define __section(NAME) \ __attribute__((section(NAME), used)) #endif __section("prog") int xdp_drop(struct xdp_md *ctx) { return XDP_DROP; } char __license[] __section("license") = "GPL";
$ clang -O2 -Wall -target bpf -c xdp_drop.c -o xdp_drop.o
続いて、これをip
コマンドでens4
にアタッチする:
# ip link set dev ens4 xdp obj xdp_drop.o
ip link show
の出力結果にxdpgeneric
という文字列が出現し、Generic XDPでXDPプログラムがアタッチされていることが確認できる*3。
# ip link show dev ens4 2: ens4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 xdpgeneric qdisc fq_codel state UP mode DEFAULT group default qlen 1000 link/ether 52:54:00:71:fc:9b brd ff:ff:ff:ff:ff:ff
デタッチは同様にip
コマンドで:
# ip link set dev ens4 xdp off
このとき別ノードからens4
宛にping
を打ってみると、XDPプログラムをアタッチしていた間、pingのレスポンスが無くなっていたことが確認できる。
$ ping 192.168.11.239 PING 192.168.11.239 (192.168.11.239) 56(84) bytes of data. 64 bytes from 192.168.11.239: icmp_seq=1 ttl=64 time=0.560 ms 64 bytes from 192.168.11.239: icmp_seq=2 ttl=64 time=0.318 ms 64 bytes from 192.168.11.239: icmp_seq=3 ttl=64 time=0.385 ms 64 bytes from 192.168.11.239: icmp_seq=8 ttl=64 time=0.594 ms 64 bytes from 192.168.11.239: icmp_seq=9 ttl=64 time=0.508 ms 64 bytes from 192.168.11.239: icmp_seq=10 ttl=64 time=0.624 ms ^C --- 192.168.11.239 ping statistics --- 10 packets transmitted, 6 received, 40% packet loss, time 9205ms rtt min/avg/max/mdev = 0.318/0.498/0.624/0.111 ms
References
- kernel/git/davem/net-next.git - David Miller's -next networking tree
- Generic XDPの最初のコミット。コミットログを読めば、おおよそ背景が理解できるはず。
- Eval Generic netstack XDP patch — Prototype Kernel 0.0.1 documentation
- Generic XDPのパフォーマンスに関する考察。
- BPF and XDP Reference Guide — Cilium v0.8 documentation
- eBPFとXDP全般に関するリファレンスガイド。iproute2をXDPプログラムローダとして使用する際のサンプルが豊富。
*1:具体的に言うとnet/core/dev.cのnetif_receive_skb_internal
*2:netlink経由で実行する; サンプル: http://elixir.free-electrons.com/linux/v4.12-rc3/source/samples/bpf/bpf_load.c#L674