Generic XDPを使えばXDP動作環境がお手軽に構築できるようになった

XDP (eXpress Data Path) ネタ。

パフォーマンス上の理由から、XDPのパケット処理はskbが割り当てられる前にNICドライバから直接呼び出させる。そのため、XDPプログラムの動作にはNICドライバ側でのサポートが必要となる。

5月時点での対応ドライバリストは下記エントリ末尾に記載した。Linux kernel 4.12からixgbeでXDPがサポートされる予定とは言え、ハードウェアの準備も必要であり、従来XDPの動作を気軽に試すことは少し難しかった。

yunazuno.hatenablog.com

そこで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

*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

*3:通常のNIC-level XDPの場合は単に xdp と表示される