SoftEtherの「UDP高速化機能」を深堀りする

はじめに

アリエではテレワーク環境として、社員宅から社内イントラに接続するために  SoftEther VPN を導入しています。

利用開放後、社員から「自宅からVPNにアクセスできない」という声が上がり調査したところ、SoftEther VPNクライアントの設定で「UDP高速化機能を無効」にすると速度は遅くなるものの無事にアクセスできることが分かりました。

社員しか使わずトラブルシューティングも面倒なのでしばらくそのままにしていましたが、最近になって対策法が見えてきました。

この記事では、 SoftEtherのUDP高速化オプション の動作と NATルーター越え の条件をさぐり、UDP高速化オプションが使えるようになるまでの顛末をまとめようと思います。

たまにSEという言葉を使いますが、SoftEtherの略として使います。

SoftEtherのUDP高速化オプションとは

SEのサーバは初期設定では443/tcpや5555/tcpなどのTCPポートで接続を待ち受けますが、サーバ側のUDPポートへの通信が可能であればUDP高速化オプションが自動的に有効になるはずです。

事前調査ではTCPポートに加えサーバ側の40000-44999/udpにアクセス可能であればUDP高速化が有効になるはずですが、「はじめに」で語ったようにそれだけではありませんでした。

UDP高速化オプションの動作を観察する

まずは以下のようなUDP高速化機能がすんなり動きそうな検証環境を準備し、NATトラバーサルを有効・無効した時のパケットキャプチャを観察してみます。

●NATトラバーサル有効(デフォルト)

NAT-T有効の場合、53/udpやICMPのパケットが利用者→SE向けに発信されますが、応答パケットが返っていく様子は観測されませんでした。

SEの仕様に記載のある「VPN over DNS」や「VPN over ICMP」のためのパケットと思われますが、今回はどのような条件でそれらが有効になるかは深堀りしていません。

UPD高速化で利用していると思われる「利用者:54154(エフェメラルポート)/udp ⇔ SEサーバ:40000/udp」の通信が双方で交わされており、SEクライアントの状態も「UDP高速化機能を使用中:はい」となっています。

//not tcpでフィルタ
21:24:54.071853 IP 192.168.10.39 > 192.168.10.41: ICMP echo reply, id 731, seq 44943, length 291
21:24:54.071867 IP 192.168.10.39 > 192.168.10.41: ICMP echo reply, id 731, seq 44943, length 296
21:24:54.071870 IP 192.168.10.39 > 192.168.10.41: ICMP echo reply, id 731, seq 44943, length 269
21:24:54.071872 IP 192.168.10.39 > 192.168.10.41: ICMP echo reply, id 731, seq 44943, length 116
21:24:54.071873 IP 192.168.10.39 > 192.168.10.41: ICMP echo reply, id 731, seq 44943, length 187
21:24:54.071913 IP 192.168.10.39 > 192.168.10.41: ICMP echo request, id 731, seq 44943, length 101
21:24:54.071928 IP 192.168.10.41 > 192.168.10.39: ICMP echo reply, id 731, seq 44943, length 101
21:24:54.071934 IP 192.168.10.39 > 192.168.10.41: ICMP echo reply, id 731, seq 44943, length 67
21:24:54.071936 IP 192.168.10.39 > 192.168.10.41: ICMP information request, length 67  ←これ以降ICMPのやりとりが途切れる
21:24:54.072252 IP 192.168.10.39.42964 > 192.168.10.41.53: 56895+ [1au] DNSKEY? 09f82272. (325)
21:24:54.072259 IP 192.168.10.39.42964 > 192.168.10.41.53: 5914+ [1au] DNSKEY? ad979abb. (254)
21:24:54.072260 IP 192.168.10.39.42964 > 192.168.10.41.53: 44477+ [1au] DNSKEY? 724680b7. (237)
21:24:54.072263 IP 192.168.10.39.42964 > 192.168.10.41.53: 53500+ [1au] DNSKEY? 99a52869. (265)
21:24:54.072264 IP 192.168.10.39.42964 > 192.168.10.41.53: 50832+ [1au] DNSKEY? e21325bd. (221)
21:24:54.072266 IP 192.168.10.39.42964 > 192.168.10.41.53: 36768+ [1au] DNSKEY? a2d4c576. (76) ←これ以降53/udpでのやりとりが途切れる
21:24:54.338588 IP 192.168.10.41.40000 > 192.168.10.39.54154: UDP, length 66
21:24:54.338848 IP 192.168.10.39.54154 > 192.168.10.41.40000: UDP, length 54
21:24:54.371771 IP 192.168.10.39.54154 > 192.168.10.41.40000: UDP, length 82
21:24:54.371782 IP 192.168.10.39.54154 > 192.168.10.41.60660: UDP, length 82 ←UDP高速化で使うポート番号で通信している
・
・
・

●NATトラバーサル無効

NAT-Tを無効にした場合は53/udpやICMPのパケットが発信されず、利用者→SEサーバのTCPリッスンポートへの通信と、「利用者:(エフェメラルポート)/udp ⇔ SEサーバ:40000/udp」のみが交わされる状態となります。

SEクライアントの状態も「UDP高速化機能を使用中:はい」となっています。

//not tcpでフィルタ
17:04:30.985593 IP 192.168.10.41.40000 > 192.168.10.39.29873: UDP, length 82
17:04:31.306424 IP 192.168.10.39.50565 > 192.168.10.41.40000: UDP, length 81
17:04:31.339324 IP 192.168.10.39.50565 > 192.168.10.41.40000: UDP, length 57
17:04:31.339335 IP 192.168.10.39.58584 > 192.168.10.41.60166: UDP, length 57
17:04:31.603497 IP 192.168.10.41.40000 > 192.168.10.39.50565: UDP, length 61
17:04:31.603509 IP 192.168.10.41.40000 > 192.168.10.39.63082: UDP, length 61
.
.
.

「利用者:(エフェメラルポート)/udp ⇔ SEサーバ:40000/udp」の通信が上手くいかないとUDP高速化機能が使えず接続が失敗するのでは・・・ということで、どのようなネットワーク構成の時にこの通信が失敗するのかを推測していきます。

UDPパケットがNATを越えられないケースを想像してみる

こんな感じのネットワークで、パケットがどのようにNATルーターを越えているのかを考えます。

利用者PC→会社方向に着目すると、利用者宅のルーターをパケットが越えるのは内側から外に向かうパケットなので問題ないでしょう。

会社のNATルータを越えてSEサーバに届くには手動で ポートフォワード を設定を設定するか、既にUDP高速化のポート番号での通信が確立していてNATテーブルが生成されている必要があります。

SEサーバからクライアントの方向に着目すると、仮にSEサーバ側から先にパケットを発信する仕組みの場合は、利用者宅ルータにNATテーブルが存在していないのでSEクライアントにパケットが到達することが出来ません。

会社側ルータの「手動のポートフォワード設定」、会社側ルータと利用者側ルータの「NATテーブルの話」がポイントになってきそうです。

UDPホールパンチング

アプリケーションを実装する際、ユーザがNATルータのポート開放をせずとも外→内の通信を受け入れることが出来る「UDPホールパンチング」という技術があります。

NATルータがソースポート番号を変換しない(利用者側端末のソースポートとNATルータのグローバルIPのソースポート番号が一致している)ことを前提としており、 Symmetric-NAT のようにソースポート番号も変換される場合は「UDPホールパンチング」による通信は到達できません。

SEのパケットキャプチャを眺めると、SEサーバからはソースポート40000-44999/udpからクライアントのエフェメラルポートへ向けて、クライアントからはエフェメラルポートからSEサーバ40000-44999/udpへ向けたUDPパケットが同時に何回か発信されたのちにUDP高速化状態が確立されており、UDPホールパンチングのため動作だろうと推測できます。

以下は「SoftEtherはこんな感じでNATパンチングしているのでは」という想像図です。相手側のルータのNATテーブルが存在せずとも【UDP高速化の通信を始める前に、相手がUDP高速化のために使うソースポート番号が分かれば】NATを外から内に越えることが出来ます。

検証

検証パターンの検討

「UDP高速化機能を有効にすると接続が失敗するのはどちらかのNATルータのせいでUDPホールパンチングに失敗しているため」という仮説を立て、以下のパラメータを組み合わせて検証パターンを準備しました。

●利用者側SEクライアントの「NAT-T無効」「NAT-T有効」の切り替え

●利用者側ルータの「Cone-NAT」「Symmetric-NAT」の設定

●会社側ルータの「Cone-NAT」「Symmetric-NAT」の設定

●会社側ルータにて「UDP Port40000-44999」をSEサーバにポートフォワード

検証の構成

NATルータとして FreeBSD 13.2 + pfを、SEサーバ/クライアントとしてFreeBSD13.2 + SoftEther 4.42 (Build 9798 rtm)を準備しました。 Cone-NATの場合は”nat”のオプションに「static-port」を指定しソースポート番号が変換されないように、Symmmetric-natの場合はstatic-port設定を削除しソースポート番号が変換されるようにしました。 ポートフォワードは”pass in”と”rdr”を設定しています。

検証の結果

# SEのNAT-T 利用者宅ルータ 会社ルータ 結果
1-1
有効
1-2
有効
×
1-3
有効
×
1-4
有効
Cone + ポートフォワードあり
1-5
有効
Symmetric + ポートフォワードあり
2-1
無効
2-2
無効
×
2-3
無効
×
2-4
無効
Cone + ポートフォワードあり
2-5
無効
Symmetric + ポートフォワードあり

考察

検証結果および検証時のパケットキャプチャを眺めると、SEクライアントのNAT-T有効/無効に関わらずUDPホールパンチングは行われ、違いは53/udpおよびICMPの試行が行われるか否かのみでした。条件2-1からも、「NATトラバーサル」を無効としているにも関わらずUDPホールパンチングが成功していることが分かります。

つまり「UDP高速化機能が有効になっている(デフォルト)場合、」

条件1-2、2-2、1-3、2-3のNATテーブルおよびパケットキャプチャを参照すると、SEサーバが発信するUDPパケットの宛先ポートとSEクライアントの発信ポートが不一致であることが分かります。

つまり、SEプロトコル上でネゴシエートしたソースポート番号がNAT変換されてしまいUDPホールパンチングに失敗するものと思われます。 


### SEクライアント
#NATテーブル(pfctl -sa抜粋)
all udp 192.168.10.39:50190 (10.0.0.1:60256) -> 192.168.10.41:40000       SINGLE:NO_TRAFFIC
→ソースポート番号が"60256"から"50190"に変換されている

#キャプチャからUDPの部分を抜粋
12:04:50.346369 IP 192.168.10.39.50190 > 192.168.10.41.40000: UDP, length 66
12:04:50.346716 IP 192.168.10.41.40000 > 192.168.10.39.60256: UDP, length 81
12:04:50.359685 IP 192.168.10.41.40000 > 192.168.10.39.60256: UDP, length 63
→SEクライアント側のネットワークからは変換されたソースポート50190で送信しているが、SEサーバからは変換前の"60256"に向けて通信している

### SEサーバ
#NATテーブル(pfctl -sa抜粋)
all udp 192.168.10.41:40000 (10.0.0.1:40000) -> 192.168.10.39:60256       SINGLE:NO_TRAFFIC
→SEクライアントのソースポート"60256"に向けて通信しようとしている

#キャプチャからUDPの部分を抜粋
12:04:50.346436 IP 192.168.10.39.50190 > 192.168.10.41.40000: UDP, length 66
12:04:50.346649 IP 192.168.10.41.40000 > 192.168.10.39.60256: UDP, length 81
12:04:50.359621 IP 192.168.10.41.40000 > 192.168.10.39.60256: UDP, length 63
→SEサーバからは変換前の"60256"に向けて通信している

 

経路上にSymmetric-NATがあるにも関わらず接続が成功するのは、条件1-4、1-5、2-4、2-5の40000-44999/udpがSEサーバにポートフォワーされている場合です。

パケットキャプチャを眺めると、最初のうちはSEサーバが発信するUDPパケットの宛先ポートとSEクライアントの発信ポートが不一致なパケットを交わしていますが、パンチングに失敗した場合は実際に受け取ったパケットのポート番号に向けて応答することが分かります。

### SEクライアント
#NATテーブル(pfctl -sa抜粋)
all udp 192.168.10.41:40000 <- 10.0.0.1:43661 MULTIPLE:MULTIPLE all udp 192.168.10.39:53911 (10.0.0.1:43661) -> 192.168.10.41:40000       MULTIPLE:MULTIPLE
→ソースポート番号が"43661"から"53911"に変換されている

#キャプチャからUDPの部分を抜粋
02:09:11.599327 IP 192.168.10.41.40000 > 192.168.10.39.43661: UDP, length 62
02:09:11.599494 IP 192.168.10.39.53911 > 192.168.10.41.40000: UDP, length 56
02:09:11.871587 IP 192.168.10.39.53911 > 192.168.10.41.40000: UDP, length 51
02:09:11.871770 IP 192.168.10.41.40000 > 192.168.10.39.53911: UDP, length 69
→SEサーバがポート変換前の"43661"に向けてパケットを送出してきている


### SEサーバ
#NATテーブル(pfctl -sa抜粋)
all udp 192.168.10.39:43661 <- 10.0.0.1:40000 NO_TRAFFIC:SINGLE all udp 192.168.10.41:40000 (10.0.0.1:40000) -> 192.168.10.39:43661       SINGLE:NO_TRAFFIC
all udp 10.0.0.1:40000 (192.168.10.41:40000) <- 192.168.10.39:53911 MULTIPLE:MULTIPLE
→ポート変換前の"43661"と、思ってたのと違うポート番号"53911"の両方に通信しようとしていた痕跡  SEクライアント側ルータのNATテーブルを見ると、NAT変換前のソースポートが"43361"であり先ずはそこに向けて通信しようとしたことが分かる

#キャプチャからUDPの部分を抜粋
02:09:11.599235 IP 192.168.10.41.40000 > 192.168.10.39.43661: UDP, length 62 02:09:11.599565 IP 192.168.10.39.53911 > 192.168.10.41.40000: UDP, length 56 02:09:11.871650 IP 192.168.10.39.53911 > 192.168.10.41.40000: UDP, length 51 02:09:11.871718 IP 192.168.10.41.40000 > 192.168.10.39.53911: UDP, length 69 →ポート変換前の"43661"に向けてパケット送出後、思ってたのと違う"53911"からUDPが届き、以降はこのポート番号宛に通信している

SoftEtherの「NAT-T有効・無効」で何が変わるか

  • 一般的な用語としての「NATトラバーサル」という言葉はUDPホールパンチングを含む
  • SoftEtherの「NAT-T無効」設定で無効になるのは「VPN over DNS」「VPN over ICMP」のみで、UDP高速化オプション有効時はUDPホールパンチングは常に試行される
  • UDPホールパンチング出来ない環境でもSEサーバが40000-44999/udpを受け取れればUDP高速化通信は出来る

UDP高速化接続が上手くいくためのネットワーク要件

SEクライアントの「NAT-T無効」設定は関係なく以下のいずれかであることが分かりました。

  • 経路上のルータが全てCone-NATであること
  • SEサーバがグローバルIPアドレスを付与されたサーバで直接稼働している
  • SEサーバの40000-44999/udpに通信可能なこと、およびSEサーバ発信のソースポート40000-44999/udpが経路上で変換されないこと

    一つ目と二つ目は、UDPホールパンチングが成功するために必要な条件です。

    三つ目のとおり、UDPホールパンチングが出来なくても、SEサーバが40000-44999/udp宛を受け取れればUDP高速化自体は利用可能です。ただしこれはここは機器仕様によるところで、SEサーバ側が先にUDPパケットを発信すると動的に生成されたSymmetric-NATのエントリを通ってしまい接続に失敗するケースがあります。(このパターンで接続失敗することがあるが再現性が微妙・・・)

    共通しているのは「SEサーバが発信する40000-44999/udpのソースポート番号が変換されてしまうとダメ」というところです。

    どうしてもUDP高速化オプション有効だと接続できない場合

    UDP高速化をあきらめる、新たにIPv4のグローバルアドレスを確保しSEサーバにすべての通信を1対1NAT(pfでいうbinat)で転送する、IPv4グローバルアドレスを付与してくれるVPSサーバでSEサーバを動かす等を検討したほうがいいかもしれません。

    なお、SEサーバとして開発版の「softether5-5.02.5180.404,2」を使っているとネットワーク構成をどのように変更してもUDP高速化オプションによる通信を確立してくれませんでした。素直に安定版を使いましょう。

    IPv6はいいぞ

    IPv6でSEサーバを構築する場合はv4のようなNAT事情が無い分楽になります。SEサーバ用に新しくグローバルIPv6アドレスを固定で確保し、TCPリスニングポートと40000-44999/udpのフィルタを許可すればよいでしょう。

    ユニークローカルアドレスのネットワーク(fd::/8)でSEサーバが動いている場合も、グローバルIPアドレスを新たに確保し1対1NATで全ての通信をリダイレクトしてしまえばCone-NATやSymmetric-NATに悩まされることはありません。

    数個のグローバルアドレスを大事に使わなければいけないv4と違い、/64のprefixでグローバルアドレスを与えられて残りの64bitを好き放題使えるIPv6ならではですね。

    SEクライアントの接続先としてIPv6を設定、VPN仮想NICにアサインされるアドレスがIPv4というような構成であってもきちんと動きます。

    まとめ

    みんなIPv6使おう。

    宣伝

    アリエでは「テレワーク環境を導入したい」「全国の拠点LAN同士をVPNで安価に接続したい」「AWSのインフラ周りの設定がよくわからないから手伝ってほしい」などのお悩みがある方からのお仕事を募集しています。

    参考リンク