この記事は、Supershipグループ Advent Calendar 2021の7日目の記事になります。
先日、sshを使用したファイル転送が回線速度と比べて異常に遅いという現象に遭遇したので、その際に行った調査を再現しつつ原因や対策について書いてみたいと思います。
要約
- OpenSSHはデフォルトでinteractiveなセッションに
af21
、non-interactiveなセッションにcs1
をDSCP値としてIPヘッダに設定する - フレッツ網はIPヘッダのDSCP値を帯域優先サービスで使用しており、契約に応じて指定された優先度以外が設定されたパケットの転送は保証されない
- そのため、OpenSSHをデフォルト設定のままフレッツ網で使うと通信ができなかったり、速度低下などの悪影響を受ける可能性がある
- OpenSSHがDSCP値を設定しないようにするためには、
IPQoS none
を設定する- 可能であれば、ルーターでのDSCP値上書きも有効
きっかけ
リモートワークも定着し、むしろ出社したら仕事できるのかが不安な今日この頃、その日も元気に自宅で仕事をしていました。
さて、ちょっと大きいファイルを手元のPCから遠方のサーバーへ送りたくなりました。
インターネット越しのファイル転送なので、安全・簡単にファイルを送れるscpコマンドを使います。
(ただしプロトコルとしてのSCPはdeprecatedなため1、-s
オプションをつけて内部的にはSFTPプロトコルを使用します2)
tango@pc % scp -s big_data.bin server:
big_data.bin 1% 20MB 1.2MB/s 14:25 ETA
お、遅い...
調査
自宅に引いたネット回線やPCのスペックに少々自信のある私としてはショッキングなスピードです。
俺のscpがこんなに遅いわけがない!ということで原因を調べます。
検証環境
調査を始める前に私が使用した環境についてまとめておきます。
- OS: PC・サーバー共にLinux
- 回線: フレッツ光ネクスト ギガタイプ (に相当する光コラボ回線) + JPNE v6プラス 固定IPサービス
- ルーター: Yamaha RTX830
また、本記事中に出てくる通信速度の計測値については1回だけ測定したものとなります。
自宅回線の速度
まずは念の為、手元のPCのインターネットとの接続速度を確認します。
Googleで"speedtest"を検索し、検索結果画面で測定した結果は以下の通りです。
非常に高速です!
PC → サーバー間の通信速度
こちら側の回線が速くても、サーバー側の回線や間のネットワークが遅い場合はそこがボトルネックとなってしまいます。
そこで、PC→サーバーの転送速度をiperf3コマンドで計測してみましょう。
まず、PC・サーバー両方にiperf3を導入し、サーバー側ではTCPの5201番ポートをファイヤウォールで開放しておきます。
サーバー側で iperf3 -s
と実行してiperfをサーバーとして起動した後に、PC側で iperf3 -c <server addr>
と実行することで PC → サーバーの転送速度を測ることができます。
結果は...
tango@pc % iperf3 -c server
Connecting to host server, port 5201
[ 5] local X.X.X.X port 59012 connected to Y.Y.Y.Y port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 103 MBytes 867 Mbits/sec 1843 1.20 MBytes
[ 5] 1.00-2.00 sec 102 MBytes 860 Mbits/sec 2018 1.21 MBytes
[ 5] 2.00-3.00 sec 105 MBytes 881 Mbits/sec 1903 1.17 MBytes
[ 5] 3.00-4.00 sec 105 MBytes 881 Mbits/sec 1915 1.18 MBytes
[ 5] 4.00-5.00 sec 105 MBytes 881 Mbits/sec 1930 1.18 MBytes
[ 5] 5.00-6.00 sec 104 MBytes 870 Mbits/sec 1892 1.19 MBytes
[ 5] 6.00-7.00 sec 105 MBytes 881 Mbits/sec 1921 1.39 MBytes
[ 5] 7.00-8.00 sec 105 MBytes 881 Mbits/sec 1933 1.16 MBytes
[ 5] 8.00-9.00 sec 104 MBytes 870 Mbits/sec 1995 1.21 MBytes
[ 5] 9.00-10.00 sec 101 MBytes 849 Mbits/sec 2322 1.20 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 1.02 GBytes 872 Mbits/sec 19672 sender
[ 5] 0.00-10.01 sec 1.01 GBytes 869 Mbits/sec receiver
iperf Done.
scpよりずっと早い!
CPU負荷
さて、回線は十分速いことが確認できました。
そうすると、問題はscp特有のものだと思われます。
まず考えられるのは、sshによる暗号化の負荷が高くて転送速度が上がらないという可能性です。
(PC・サーバーともに十分な性能のものを使用しているはずなので、そうであって欲しくはないですが)
そこで、転送中にPC・サーバー双方にかかっている負荷をtopコマンドで測定してみます。
top - 09:08:44 up 1:00, 1 user, load average: 0.07, 0.03, 0.00
Tasks: 79 total, 1 running, 78 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.7/2.4 3[||| ]
%Cpu1 : 0.0/0.3 0[ ]
MiB Mem : 8.3/3930.6 [|||||| ]
MiB Swap: 0.0/0.0 [ ]
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
:
:
4420 tango 20 0 14356 5760 4544 S 2.7 0.1 0:06.44 `- sshd
4421 tango 20 0 6172 4620 4148 S 0.3 0.1 0:00.83 `- scp
:
:
top - 18:09:19 up 20:00, 1 user, load average: 0.00, 0.00, 0.00
Tasks: 154 total, 1 running, 153 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.3/0.0 0[ ]
%Cpu1 : 0.3/5.5 6[|||| ]
%Cpu2 : 0.0/0.0 0[ ]
%Cpu3 : 0.0/0.0 0[ ]
%Cpu4 : 0.0/0.0 0[ ]
%Cpu5 : 0.0/0.0 0[ ]
%Cpu6 : 0.0/0.0 0[ ]
%Cpu7 : 0.0/0.7 1[| ]
MiB Mem : 9.8/23703.3 [||||||| ]
MiB Swap: 0.0/11851.0 [ ]
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
:
:
13614 tango 20 0 10032 6408 4200 S 0.0 0.0 0:00.13 `- zsh
13781 tango 20 0 8740 4052 3656 S 0.3 0.0 0:01.02 `- scp
13782 tango 20 0 10896 6844 5188 S 1.0 0.0 0:03.18 `- ssh
:
:
予想はしていましたが、随分と暇そうです...
scpコマンド以外の転送手段
こうなると、scpコマンドの実装やプロトコルに問題がある可能性があります。
scp以外の転送方法を試してみましょう。
(流石にデータを暗号化せずにインターネット上に流すわけにはいかないので、まずはsshベースの方法から確認します)
sftp
scpコマンドの時点でSFTPプロトコルを使用しているので同じ結果になると思われますが、念の為確認します。
tango@pc % sftp server
Connected to server.
sftp> put big_data.bin
Uploading big_data.bin to /home/tango/big_data.bin
big_data.bin 1% 18MB 1.2MB/s 14:13 ETA
予想通り変わらず
pipe over ssh
シェルのパイプでファイルをsshに流し、サーバー側でddコマンドをにて書き込む速度をPC側のpvコマンドで計測します。
tango@pc % < big_data.bin pv | ssh server dd of=big_data.bin
13.2MiB 0:00:10 [1.10MiB/s] [> ] 1% ETA 0:12:43
変わらず
iperf3 over ssh tunnel
ファイル転送特有の問題があるのでしょうか?
sshトンネル越しにiperf3を実行し、sshそのものの転送速度を計測してみます。
tango@pc % ssh -L5201:localhost:5201 server
tango@server:~$ iperf3 -s
:
:
tango@pc % iperf3 -c localhost
Connecting to host localhost, port 5201
[ 5] local ::1 port 50744 connected to ::1 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 110 MBytes 923 Mbits/sec 0 895 KBytes
[ 5] 1.00-2.00 sec 102 MBytes 860 Mbits/sec 3 895 KBytes
[ 5] 2.00-3.00 sec 101 MBytes 849 Mbits/sec 1 895 KBytes
[ 5] 3.00-4.00 sec 101 MBytes 849 Mbits/sec 2 895 KBytes
[ 5] 4.00-5.00 sec 102 MBytes 860 Mbits/sec 2 895 KBytes
[ 5] 5.00-6.00 sec 101 MBytes 849 Mbits/sec 2 895 KBytes
[ 5] 6.00-7.00 sec 102 MBytes 860 Mbits/sec 1 895 KBytes
[ 5] 7.00-8.00 sec 100 MBytes 839 Mbits/sec 1 895 KBytes
[ 5] 8.00-9.00 sec 101 MBytes 849 Mbits/sec 0 895 KBytes
[ 5] 9.00-10.00 sec 102 MBytes 860 Mbits/sec 2 895 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 1.00 GBytes 860 Mbits/sec 14 sender
[ 5] 0.00-10.02 sec 1016 MBytes 850 Mbits/sec receiver
iperf Done.
おや、ほぼ生iperf3と同じ速度です。
nc over ssh tunnel
sshのトンネルだと速いことが分かったので、ncコマンドを利用してトンネルを通してファイルを転送してみましょう。
tango@server:~$ nc -l 5201 > big_data.bin
tango@pc % < big_data.bin pv | nc localhost 5201
1.00GiB 0:00:10 [ 102MiB/s] [=======================================================>] 100%
はい、無事に高速に転送できました。
めでたしめでたし...
scp over ssh tunnel
scpでの転送がssh tunnelでの転送より大幅に遅いことに納得できないので、本来無意味ですがsshトンネルを通してscpでファイルを転送してみます。
tango@pc % ssh -L8000:localhost:22 server
tango@pc % scp -s -P 8000 big_data.bin localhost:
big_data.bin 100% 1024MB 96.9MB/s 00:10
速い!?
無駄に2重に暗号・復号処理が発生しているはずなのに速いです!
原因
ssh v.s. ssh tunnel
ここまで確認できた現象をまとめてみます。
- ssh/scp/sftp でファイルを転送した場合、1.1MiB/s 程度
- iperf3/iperf3 over ssh tunnel で帯域を計測すると 850Mbps (≒ 101MiB/s) 以上
- nc over ssh tunnel/scp over ssh tunnel でファイルを転送した場合は 100MiB/s 程度
以上より、
- ssh tunnel では中に通すプロトコルによらず、PC → サーバー間の帯域とほぼ同等の速度で転送できる
- ssh/scp/sftp を直接使うと 1.1MiB/s 程度の速度しか出ない
ということが分かりました。
ssh tunnelとそれ以外のssh系ファイル転送手段の違いは何でしょうか?
QoS
ネットで調べてみると、フレッツ光回線でscpができないという話がヒットします。
曰く、sshはデフォルトでIPヘッダにQoSのためのビットを設定するが、この設定をされたパケットはフレッツ網の制約によりドロップされるため通信不能になるとのことです。
ssh_config(5)manを確認すると、QoS設定を行うための設定項目 IPQoS
について、以下のように説明されていました。
IPQoS
Specifies the IPv4 type-of-service or DSCP class for connections. Accepted values are af11, af12, af13, af21, af22, af23, af31, af32, af33, af41, af42, af43, cs0, cs1, cs2, cs3, cs4, cs5, cs6, cs7, ef, le, lowdelay, throughput, reliability, a numeric value, or none to use the operating system default. This option may take one or two arguments, separated by whitespace. If one argument is specified, it is used as the packet class unconditionally. If two values are specified, the first is automatically selected for interactive sessions and the second for non-interactive sessions. The default is af21 (Low-Latency Data) for interactive sessions and cs1 (Lower Effort) for non-interactive sessions.
どうやらデフォルトで、interactiveなセッションとnon-interactiveなセッションで異なる設定が使用されるようです。
scpやsshにパイプを組み合わせた転送はnon-interactiveなセッション、ssh tunnelはinteractiveなセッションに該当しそうなので、こいつが怪しいです。
試しにIPQoS
をnone
に設定してscpを動かしてみます。
tango@pc % scp -s -o IPQoS=none big_data.bin server:
big_data.bin 100% 1024MB 72.2MB/s 00:14
速い!
オマエノシワザダタノカ......
DSCP
IPQoS
で指定できる値は基本的にDSCPで定められたクラスの値です。
non-interactiveセッションのデフォルト、 cs1
は2進数で 001000
、10進数で 8
となります。
次にフレッツ網の仕様を確認してみます。
NTT東日本の技術参考資料のページより、IP通信網サービスのインタ フェース 第三分冊に以下のように記載されています。
2.4.4 帯域優先に関する仕様
契約帯域の範囲で優先クラスを利用したIPv6(IPoE)通信が可能となるサービスにて、当該サービス契約者の端末機器からIP通信網に送信されるIPv6パケットにおいて、RFC2474に規定される優先度の指定が可能です。当該サービス契約者の端末機器からIP通信網に送信されるIPv6パケット(IPoE方式)のトラヒッククラスフィールドの先頭6ビットに、DSCP値として8(001000)を指定することで、優先トラヒックとして転送します。
尚、指定された優先度以外が設定されたパケットの転送は保証しません。
「契約帯域の範囲で優先クラスを利用したIPv6(IPoE)通信が可能となるサービス」というのはおそらくフレッツ 光ネクスト プライオなどの法人向けサービスのことかと思われ、家庭用回線である私の家では当然そのようなサービスを契約しておりません。
そのような端末からDSCP値8
のパケットを送出したため、転送が保証されない = パケットドロップなどによる異常な速度低下が発生したと推測できます。
(あくまで推測で、実際のところ何が起こっているのかは公開されている資料からは読み取れませんでした。詳しい人いたら教えてください)
検索すると出てくるscpが全く使えないという事象と異なり、異常な速度低下という現象が発生した理由もよくわかりませんが、以下の辺りが原因かなと思っています。
- 最近フレッツ網の仕様が変わった
- v6プラス 固定IPサービスを使用していることによる影響
- ひかり電話契約の有無やHGWの有無による違い (当環境はひかり電話無し、HGW無し)
なお、sshはIPv4で使用しているのになぜフレッツ網へ送るIPv6パケットにDSCP値が設定されるのかについては、おそらくルーター(Yamaha RTX)がipipトンネルへ送るためにIPv4パケットをカプセルする際に、IPv4ヘッダーのDSCP値を外側のIPv6ヘッダーへコピーしているためと思われます。
(こちらも推測で、実際にルータから出ているパケットをキャプチャして調べてはいません)
対処
アプリケーション・クライアント毎に設定するのは面倒なため、ルーター側でフレッツ網へ送るパケットのDSCP値を書き換えるような処理を行うのが理想です。
私が使用しているRTX830は、IPv4パケットについてはIP パケットの TOS フィールドの書き換え機能により、以下のような設定でDSCP値を書き換えることができます。
ip tos supersede 10000 normal precedence=0 999000
# 10000は任意の識別番号、999000はこの機能を適用する静的フィルタの番号
実際に上記設定を行った後、IPQoS
設定を削除しても転送が遅くならないことを確認しました。
ただし、IPv6パケットについてはTraffic classフィールドを書き換える機能が見当たらず、現状RTXシリーズでは対応できなさそうです。
(2023年1月7日 追記)
RTX830については2022年12月26日にリリース(ただし現時点で公開停止中)されたファームウェア Rev.15.02.273 にて、IPパケットのDSCP値書き換え機能及びIPv6パケットのDSCP値書き換え機能が追加されたようです。
現在RTX830を所有していないため未検証ですが、参考情報として記載します。
ip dscp supersede 10000 default 999000
ipv6 dscp supersede 10000 default 999000
# 10000は任意の識別番号、999000はこの機能を適用する静的フィルタの番号
(追記ここまで)
そこで、sshの設定ファイルでも対処することにします。
Host *
IPQoS none
システムグローバルで適用するのであれば、/etc/ssh/ssh_config
での設定も可能です。
IPQoS none
これで、sshはDSCP値を設定しないようになり、フレッツ網でも問題なく使えるようになりました。
サーバー側設定について
今回はクライアント側がフレッツ光回線である場合の設定ですが、サーバー側がフレッツ光回線である場合には/etc/ssh/sshd_config
でのIPQoS
設定(もしくはルーターでの対応)が必要となります。
これは、ssh接続時に認証が完了した後、サーバー側から開始されるセッション(シェルなど)にはサーバー側のIPQoS
設定が使用されるためです。
(sshの挙動について理解し切れていませんが、パケットキャプチャで実際にそのような挙動を確認しています)
設定自体はssh_configと同様です。
IPQoS none
おわりに
本記事では最近遭遇した不思議な現象について、Advent Calendarに乗じてまとめてみました。
フレッツでsshが繋がらないという情報は調べてみるとちらほら見つかるのですが、遅くなるという情報はあまりなく、IPQoSへ辿り着くまでに時間がかかりました。
同じ現象に遭遇している人がこの記事を見つけて役に立ててもらえたら嬉しいです。
宣伝
リモートワークを推奨しているSupershipグループではプロダクト開発やサービス開発に関わる人を絶賛募集しております。
ご興味がある方は以下リンクよりご確認ください。
Supershipグループ 採用サイト
是非ともよろしくお願いします。
-
OpenSSH 8.0 Release noteより、The scp protocol is outdated, inflexible and not readily fixed. We recommend the use of more modern protocols like sftp and rsync for file transfer instead. ↩
-
OpenSSH 8.7 Release noteより、SFTP support may be enabled via a temporary scp -s flag. It is intended for SFTP to become the default transfer mode in the near future, at which time the -s flag will be removed. ↩
-
http://www.rtpro.yamaha.co.jp/RT/docs/relnote/Rev.15.02/relnote_15_02_27.html ↩