cloudflare tunnelをマルチホーミングで使う注意点

短いまとめ

  • マルチホーミング構成のフェールバック(バックアップ→主)で、cloudflare tunnelの通信が主回線側に戻らなかった。
  • 原因はcloudflare tunnelのquic通信が非対称ルーティングを許容するからだった。

はじめに

前回、RTX1300でマルチホーム構成したらギガ死した件という記事を書きました。

メイン回線に復旧した後もcloudflare tunnelの通信だけがモバイル回線に残り続け、意図せずバックアップ側の通信量が大きくなりました。

その後、詳しく調査してみたところ、記事での推論と異なる状況が見えてきました。

訂正を兼ねて顛末をまとめます。

前回のおさらい

構成は前回と同じです。

普段はフレッツ光クロス(v6プラス)のIPIPトンネルを使い、疎通が途切れたらモバイル回線のlan3に切り替わります。

ip route default gateway tunnel 1 keepalive 1 gateway dhcp lan3 weight 0
ip keepalive 1 icmp-echo 3 3 1.1.1.1 log=on downwait=20

モバイル回線(バックアップ)からメイン回線に復帰してもcloudflare zero trustのコネクタページに出るorigin IPがモバイル回線のままで、通信量も減りませんでした。

これをルーティングキャッシュが残っているせいだと考えて、復帰を検知したらモバイル回線側のインタフェースをリセットするLuaスクリプトを書いた、というのが前回までの話です。

実測できる環境を用意した

前回はshow ip connection lan3の表示とzero trustのコンソールだけを見て原因を推測していました。

今回はエコーサーバやパケットキャプチャ環境を用意して詳しく調査してみました。

  • AWS EC2サーバーを立て、受信したパケットの送信元IPとポートを返させる。
  • 観測クライアントはローカルから始点ポートを固定(NAPTセッションを維持)して1秒間隔で叩く。

これでRTX1300のルーティングが実際にどうなっているかを確認できます。

さらに、

  • RTX1300とモバイル回線の上位ゲートウェイのあいだにリピーターハブを挟みパケットキャプチャ。

これでモバイル回線側の通信がフォールバック時にどうなるのかを確認できます。

ルーティングは追従

主回線(LAN2)をルータから抜いてバックアップ回線(モバイル)にフェールオーバーさせ、しばらくしたらつなぎ直してフェールバックさせました。

エコーサーバのレスポンスは経路変更とほぼ同時に切り替わりました。時刻はそれぞれ異なる時計で合わせているので参考です。

事象 経路変更(syslog) プローブ(EC2で観測)
フェールオーバー 20:12:42.570 20:12:42にモバイル側へ
フェールバック 20:13:19.570 20:13:19にFTTH側へ

期待通り、RTX1300が主回線の疎通状況に沿ってdefault gatewayを変更してくれているのが分かりました。

モバイル回線は下りが残る

一方、パケットキャプチャは面白いデータが得られました。主回線側にフェールバックしても下りパケットだけ流れ続けていました。

時刻帯 下り Packets 上り Packets 備考
20:13:00 161 160
20:13:10 156 151
20:13:20 170 0 20:13:19.570 に経路変更
20:13:30 183 0
20:13:40 161 0
20:14:10 380 0
20:14:20 30 0 20:14:18 にNATクリア

パケットを詳しくみていくと、問題となっているcloudflare tunnelの4本のコネクションはフェールオーバー中に張り直されたもの、つまりモバイル回線上で生まれたストリームでした。ですが、主回線側にフェールバックしても受信だけ残り続けるという現象が観測されました。

lan3のNATディスクリプタにエントリが生きているので、届いたパケットは内部のcloudflaredへ正しく変換されて配送されます。

送信は主回線、受信はバックアップ回線という非対称経路が成立していたと思われます。

QUICだから切れない

普通のTCPなら、こんな状態は長続きしません。src/dstのIP, portでコネクションを管理するので、フェールオーバー/フェールバックに伴ってglobal IPが変化するとコネクションが切れたと認識されます。

ところが、QUICはコネクションIDでコネクションを識別するので、送信元アドレスが途中で変わっても切れないようです。

Cloudflare tunnel自身は通信できていると認識しているので自分から張り直す理由がなく、Cloudflare側に登録されたorigin IPもモバイル回線側のまま残ります。

しかもこの状態は自然には終わりません。

QUICの下り通信が続くとNATバインディングが更新されるので、下り通信はネットワーク側によって止められることなく続きます。

ギガ死するまで止まらなかったのは、そういうことでした。

対策

受信を止めればよいので、残っているNATテーブル上のセッションを削除すれば解決しました。

clear nat descriptor interface dynamic lan3

55.4.15 インタフェースの NAT アドレステーブルのクリア

前回の記事ではインターフェースをリセットしていましたが、同時にNATセッションも切断されるために結果としてcloudflare tunnelのコネクションも切られ、主回線側での再接続が促されていました。ただ、RTX1300の仕様で1つのインターフェースを全てのインターフェースがリセットされてしまい通信が不安定になる事がありました。

RTX5000、RTX3510、RTX3500、RTX1300 では、このコマンドを実行すると、すべての LAN インタフェースが同時にリセットされる。

56.7.5 インタフェースの再起動

NATテーブルのリセットなら影響は限定的なので、そんな問題も起こらず安全にコネクションを切り替えることができます。

考察

今回の検証で1点だけ気になる点があります。

原因はQUICを用いたcloudflare tunnelの通信が非対称ルーティングになっていたからですが、QUICのRFC(RFC9000)によると相手側のIPアドレスが変化した場合はそちらに切り替えることが必須(must)であると記載されています。

If the recipient permits the migration, it MUST send subsequent packets to the new peer address and MUST initiate path validation (Section 8.2) to verify the peer's ownership of the address if validation is not already underway. If the recipient has no unused connection IDs from the peer, it will not be able to send anything on the new path until the peer provides one; see Section 9.5.

cloudflare tunnelの為に独自にソケット部分を拡張しているのかもしれませんが、仕様でこう記載されているのでcloudflare tunnelの設定で緩和できるのかもしれません。このあたりは今後の宿題にしたいと思います。

ご存じの方がいらしたら是非教えてください!

おわりに

マルチホーム環境でcloudflaredがバックアップ回線に居座って見えるのは、経路が固定されているからではありませんでした。

送信は経路変更に追従しており、残っていたのは旧回線側のNATバインディング宛に届く受信です。

QUICが非対称経路を許容するためcloudflaredはコネクションを張り直さず、放置すると無期限に続きます。

対策は、旧回線のNATアドレステーブルをクリアして受信を止めることです。

推測で書いた説明が実測であっさり否定されたので、やはりネットワークは動かして測らないと分からないですね。

皆さんもこんな穴にハマった等ありましたら是非コメント欄で教えてください。