前の所属のラボの TrueNAS を、 LXC コンテナ→ WireGuard → NAT で NAS につなぐ形で NFS マウントして使い始めた。手元のノートPCで転送速度を確認したら 25MB/s あたりで完全に頭打ちになっていた。最初は「ルータの WireGuard の暗号化が遅いんだろう」と決めつけていたが、順番に切り分けていくと、真犯人は自分のノートPCの Wi-Fi の規制ドメイン(regulatory domain)が 00(未設定)だったであり、全く別だったという記録。
環境
ノートPC(Arch, Intel AX200) --[WiFi]--> ルータ(ASUS AX6000)--[WireGuard]--> NAS内LXC --[NAT]--> NAS
$ mount | grep server41
10.105.100.41:/mnt/data/share on /home/ken/Mount/server41 type nfs4 (rw,...,rsize=1048576,wsize=1048576,...,proto=tcp,...)
rsize/wsize は 1MB でチューニング済み。マウント自体は問題なし。
切り分け1: NFS越しの実測
まず実際に測る。キャッシュに騙されないよう、書き込みは conv=fdatasync、読み込みは iflag=direct を付ける。
$ cd ~/Mount/server41/.bench_ken
# 書き込み
$ dd if=/dev/zero of=testfile bs=1M count=1024 conv=fdatasync
1073741824 bytes (1.1 GB) copied, 57.5 s, 18.7 MB/s
# 読み込み(キャッシュヒット → 無意味)
$ dd if=testfile of=/dev/null bs=1M
1073741824 bytes (1.1 GB) copied, 0.099 s, 10.8 GB/s ← ローカルのページキャッシュ
# 読み込み(direct でキャッシュ回避)
$ dd if=testfile of=/dev/null bs=1M iflag=direct
1073741824 bytes (1.1 GB) copied, 46.6 s, 23.0 MB/s
Write 18.7 / Read 23.0 MB/s。だいたい 200Mbps 相当。LAN にしては遅すぎ!
切り分け2: 単一ストリームの問題か?
TCP 単一ストリームで詰まっているだけなら、並列化すれば合計は伸びるはず。4本並列で direct read してみる。
$ for i in 1 2 3 4; do dd if=testfile of=/dev/null bs=4M iflag=direct 2>/tmp/dd$i & done; wait
dd1: ... 6.4 MB/s
dd2: ... 6.4 MB/s
dd3: ... 6.4 MB/s
dd4: ... 6.4 MB/s → 合計 25.6 MB/s
単一(24.6MB/s)とほぼ同じ。並列化しても伸びない = TCPストリーム数の問題ではなく、経路のどこかに ~25MB/s の固い上限がある。レイテンシは ping で avg 3.5ms・ロス0% と良好なので、遅延ではなく帯域律速。
切り分け3: ネットワークか、それとも自分か
ここで同じ LAN 内の有線の端末から NAS へ iperf3 すると、
$ iperf3 -c 10.105.100.41
Connecting to host 10.105.100.41, port 5201
[ 5] local 10.105.7.246 port 56570 connected to 10.105.100.41 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 83.6 MBytes 701 Mbits/sec 115 1.38 MBytes
[ 5] 1.00-2.00 sec 83.4 MBytes 699 Mbits/sec 0 1.50 MBytes
[ 5] 2.00-3.00 sec 81.1 MBytes 680 Mbits/sec 0 1.59 MBytes
[ 5] 3.00-4.00 sec 83.9 MBytes 704 Mbits/sec 3 1.19 MBytes
...
700Mbpsくらい出る。つまり手元の LAN も NAS 側のネットワークも WireGuard もほぼ問題なし。遅いのは 自分のノートPCから先の経路だけと判明。
犯人発見: Wi-Fiのリンクレートが低すぎる
$ iw dev wlp165s0 link
signal: -38 dBm ← 電波は最高レベル
rx bitrate: 286.7 MBit/s HE-MCS 11 HE-NSS 2 ← なのに286Mbpsしか出ていない
tx bitrate: 286.7 MBit/s HE-MCS 11 HE-NSS 2
$ iw dev wlp165s0 info
channel 120 (5600 MHz), width: 20 MHz ← チャンネル幅が20MHz
電波は -38dBm と目の前レベルなのに、リンクが 286.7Mbps しか出ていない。これは Wi-Fi 6・2ストリームでも チャンネル幅が 20MHz のときの値。TCP 実効はPHYの6〜7割なので、286 × 0.65 ≒ 186Mbps ≒ 23MB/s。実測の 25MB/s とピッタリ一致する。
つまり犯人は WireGuard ではなく、Wi-Fi が 20MHz 幅でしか繋がっていないことだった。
なぜ20MHzに落ちるのか → 規制ドメインが00
ここで「じゃあルータの設定が 20MHz なのか?」と思ったが、同じ SSID にスマホで繋ぐと 800Mbps 出る。AP は 80MHz 以上で飛ばせているのに、自分のノートPCだけが 20MHz にネゴっている。
原因は規制ドメインだった。
$ iw reg get
country 00: DFS-UNSET ← JPではなく「00」(ワールド/未設定)になっている
$ iw list | grep -A1 5600
* 5600.0 MHz [120] (22.0 dBm) (no IR, radar detection)
- 現在の 5GHz チャンネル ch120 は DFS 帯(レーダーと共用する帯域)。
- 規制ドメインが
00(未設定)だと、DFS チャンネルはno IR(No Initiate Radiation = 自分から送信開始してはいけない) 扱いになり、安全側に倒して 20MHz に制限される。 - スマホは正しく
JPドメインで動くので、同じ ch120 でも 80MHz を張れて 800Mbps 出ていた。
カードは Intel AX200 (Wi-Fi 6) で 80/160MHz にフル対応。ハードは何も悪くなく、規制ドメインが JP になっていなかっただけだった。
対策
1. 規制ドメインを JP にする(即効・一時的)
$ sudo iw reg set JP
$ iw reg get | grep country # → country JP
$ iw dev wlp165s0 info | grep width # → width: 80 MHz
$ iw dev wlp165s0 link | grep bitrate # → 800Mbps台に上がる
2. 永続化
再起動でも維持されるよう、カーネルモジュールのオプションで固定する。
# /etc/modprobe.d/cfg80211.conf
options cfg80211 ieee80211_regdom=JP
wireless-regdb が入っていること。
$ pacman -Q wireless-regdb || sudo pacman -S wireless-regdb
3. (保険) AP側を非DFSの80MHz固定にする
regdomain を JP にすれば ch120 の 80MHz でも動くが、DFS 帯はレーダー検出で瞬断・幅低下のリスクが残る。確実にしたいなら AP の 5GHz を 非DFS(ch36 or ch149)+ 80MHz 固定にすると、DFS 絡みの不安定さを完全に回避できる。
なぜAPによって発症したりしなかったりするのか
この問題のいやらしいところは、同じノートPCでも AP によって出たり出なかったりすること。「別のAPだと平気」「スマホは平気」「昨日は速かった」とブレるので、余計に原因にたどり着きにくい。理由は、規制ドメインの決まり方と、引っかかる条件がどちらも AP 依存だから。
まず規制ドメインの決まり方には優先順があり、ざっくり以下の順で決まる。
- 明示設定(
iw reg set/cfg80211.conf/wireless-regdbの設定) - カードのEEPROMに焼かれた値
- AP がビーコンで流す国コード(802.11d の Country IE)
Ubuntu などはインストール時のロケール/国選択でここを設定してくれるので 00 に落ちないが、Arch は何も設定しないので 1・2 が無ければ 00(ワールド=一番保守的)のままになる。「ちゃんと設定しろ」と案内されないのに踏むのはこれが理由。
その上で、00 のまま繋いでも 2つの条件が噛み合ったときだけ発症する。
条件① APが 802.11d の Country IE を流しているか
Intel の iwlwifi には LAR(Location Aware Regulatory) という機能があり、繋いだ AP が「私は JP です」とビーコンで宣言していれば、クライアントが 00 → JP に自動追従して制限が解ける。
- Country IE を流す AP →
00でも勝手に直る(発症しない) - 流さない AP(消費者向けは流さない設定も多い)→
00のまま張り付く
条件② APが DFS チャンネルを使っているか
00 が痛いのは DFS帯(ch52–64, 100–144) に対してだけ。非DFS(ch36–48 / ch149–165、2.4GHzのch1–11)なら 00 でもだいたい普通の幅で通る。
- AP が非DFS(ch36等)→
00でも 80MHz 出る(発症しない) - AP が DFS(今回の ch120)→
no IRで 20MHz に制限(発症する)
今回はこの両方が同時に成立していた。
AP が Country IE を流さない × AP が自動選択で DFS の ch120 を掴んだ
↓
クライアントは 00 のまま × DFS帯は no IR で 20MHz 制限
↓
25MB/s 頭打ち
自分の AP が Country IE を流しているかは、スキャン結果に Country: 行が出るかで分かる。
$ sudo iw dev wlp165s0 scan | grep -iA1 -E 'SSID: C1-105-wifi|Country'
出れば 802.11d を流している AP(LAR で自動追従する)、出なければ流していない AP(00 に張り付く)。だからこそ regdomain は AP 任せ(LAR頼み)にせず、ieee80211_regdom=JP で明示固定しておくのが確実。そうすれば AP が Country IE を流そうが、DFS を掴もうが、常に JP の制限で動く。
教訓
- 「遅い = 一番それっぽいやつ(WireGuard)が悪い」と決めつけない。端から順に切り分ける。
iperf3で「自分より先は速い」を確認できたのが決定打だった。切り分けは どこまでが速くてどこから遅いか の境界を探す作業。- Wi-Fi が遅いときは電波強度(
signal)だけ見てはダメ。iw dev ... linkの bitrate とiw dev ... infoの width、そしてiw reg getの country を見る。電波が良いのに遅いなら、たいてい幅か規制ドメイン。 - 規制ドメインが
00のまま運用されているケースは意外と多い。DFS チャンネルを掴んだ瞬間に地味に足枷が付く。