Linux の SRv6 H.Encaps は tunsrc を設定しないとどの程度性能が落ちるのか
※ この記事の文章は Claude に書かせ、筆者が確認・修正したものです。
最近、Linux カーネルに SRv6 Mobile User Plane(RFC 9433)の behavior を実装していて、既存の SRv6 behavior と比べて性能が見劣りしないかを、SRPerf を使って測っています。SRPerf は、Linux と VPP の SRv6 behavior を一通り測定した論文(SRPerf: A Performance Evaluation Framework for IPv6 Segment Routing)で使われている測定フレームワークです。公開されているものは古い環境向けなので、Ubuntu 24.04 と Python 3.12 で動くように直したフォークを使っています。
この論文には、H.Encaps ではトンネルの送信元アドレス(tunsrc)を設定しないと性能が落ちる、と書かれています。どの程度落ちるのかが気になったので、実際に測ってみました。
SRPerf の論文にある TUNSRC の記述
論文の実験環境の説明には、次のように書かれています。
We configured the TUNSRC for the SRv6 policy headend behaviors doing encapsulation. The latter allows to configure the IPv6 source address of the IPv6 outer header. The TUNSRC has to be configured otherwise the Linux kernel will try to get the address from the interface which will cause a performance drop in the performance of the encaps behavior.
論文の著者は tunsrc を設定して測っていますが、どの程度性能が落ちるのかは書かれていません。なお、公開されている SRPerf の forwarding-behaviour.cfg には tunsrc の設定が入っていないので、SRPerf で論文と同じ条件で測るときは tunsrc を足す必要があります。
tunsrc を設定しないと何が起きるのか
外側の IPv6 ヘッダの送信元アドレスは、net/ipv6/seg6_iptunnel.c の set_tun_src() で決まります(v7.1 のソース)。
if (route_tunsrc && !ipv6_addr_any(route_tunsrc)) {
memcpy(saddr, route_tunsrc, sizeof(struct in6_addr));
} else {
rcu_read_lock();
tun_src = rcu_dereference(sdata->tun_src);
if (!ipv6_addr_any(tun_src)) {
memcpy(saddr, tun_src, sizeof(struct in6_addr));
} else {
ipv6_dev_get_saddr(net, dev, daddr,
IPV6_PREFER_SRC_PUBLIC, saddr);
}
route 毎の tunsrc(Linux 7.1 から)、network namespace 毎の tunsrc(ip sr tunsrc set で設定するもの)の順に見て、どちらも :: なら ipv6_dev_get_saddr() で選びます。tunsrc の初期値は :: なので、何も設定していなければ、ホストに付いているアドレスを RFC 6724 の規則で比べる処理を毎パケット実行しています。論文にある「the Linux kernel will try to get the address from the interface」は、この処理のことです。
tunsrc の有無で比較してみた
測り方は論文に合わせ、SUT(測定対象のマシン)の 1 コアでパケットを処理し、PDR(Partial Drop Rate、パケットロス率 0.5% 以下で転送できる最大の送信レート)と MRR(Maximum Receive Rate、回線速度いっぱいで送ったときの受信レート)を 10 秒 × 10 回ずつ測っています。SUT の CPU は Xeon E5-2650 v3(2.30 GHz)で、T-Rex を動かす tester と Intel 82599ES(10GbE)で直結しています。IOMMU は passthrough(iommu=pt)にしています。
カーネルも設定も同じにして、tunsrc だけを :: と 12:2::2 で切り替えました。tunsrc に設定した 12:2::2 は、tunsrc が :: のときに選ばれるのと同じアドレスなので、2 回の測定で違うのは送信元アドレスの決め方だけです。比較のために、外側の IPv6 ヘッダを作らないので set_tun_src() を通らない plain IPv6 と H.Insert(inline)も同じ条件で測りました。
| tunsrc なし | tunsrc あり | 変化 | |
|---|---|---|---|
| plain IPv6 | 1196.0 | 1192.2 | -0.3% |
| H.Insert | 998.3 | 1001.3 | +0.3% |
| H.Encaps.V6 | 569.8 | 967.2 | +69.7% |
| H.Encaps.L2 | 445.2 | 656.2 | +47.4% |
(MRR、単位は kpps)
plain IPv6 と H.Insert はほとんど変わらず、encap する 2 つだけが伸びました。tunsrc なしだと、ありと比べて H.Encaps.V6 で 41%、H.Encaps.L2 で 32% 遅くなりました。H.Encaps.L2 には tunsrc とは別に遅くなる原因があり、この数字はその修正を入れる前のカーネルで測ったものです(次の記事で詳しく書いています)。
PDR から 1 パケットあたりのサイクル数(2.30 GHz ÷ PDR)を計算すると、差は H.Encaps.V6 でも H.Encaps.L2 でも 1660〜1670 サイクルで、送信元アドレスの選択にかかる分だけが減っています。この差は、ホストに付いているアドレスの数によって変わる可能性がありますが、そこまでは測っていません。
tunsrc を設定した H.Encaps.V6 の PDR は 976 kpps で、論文の約 978 kpps とほぼ同じです。
tunsrc の設定方法
iproute2 では次のように設定します。
# network namespace 毎
ip sr tunsrc set 2001:db8::1
ip sr tunsrc show
# route 毎(Linux 7.1、iproute2 7.1 から)
ip -6 route add 2001:db8:100::/64 encap seg6 mode encap tunsrc 2001:db8::1 segs fc00::1 dev eth0
FRR では、設定(frr.conf)に次のように書きます。
segment-routing
srv6
encapsulation
source-address 2001:db8::1
FRR は、この source-address を書かない限り tunsrc を設定しません。FRR を起動して確かめたところ、SRv6 の locator だけを設定した状態では ip sr tunsrc show は :: のままで、source-address を書くとそのアドレスになりました。bgpd の SRv6 L3VPN は route 毎の送信元アドレスも指定しないので、source-address を書いていなければ、性能が落ちます。
参考文献
- A. Abdelsalam, P. L. Ventre, C. Scarpitta, A. Mayer, S. Salsano, P. Camarillo, F. Clad, C. Filsfils, “SRPerf: A Performance Evaluation Framework for IPv6 Segment Routing”, IEEE Transactions on Network and Service Management, vol. 18, no. 2, pp. 2320-2333, 2021
- SRPerf(GitHub)とフォーク
- RFC 8986: Segment Routing over IPv6 (SRv6) Network Programming
- RFC 9433: Segment Routing over IPv6 for the Mobile User Plane
- RFC 6724: Default Address Selection for Internet Protocol Version 6 (IPv6)
- Linux v7.1 net/ipv6/seg6_iptunnel.c
- FRR User Guide: Zebra, SRv6 の source-address