macOSのopenconnectで Can't assign requested address が出る原因と対処(古いホストルートの残り)
TL;DR
-
openconnectでVPNに繋ごうとしたらFailed to connect ...: Can't assign requested addressで失敗した - 原因は、以前別のネットワークで繋いだときのVPNサーバ宛て固定ホストルートが残っていたこと
- 今いるネットワーク(en0)のサブネットからは、その古いルートのゲートウェイに届かず、送信元アドレスを用意できなかった
- 対処は
sudo route delete <VPNサーバIP>で古いルートを消すだけ
環境
- macOS
- openconnect(Homebrew)
- クライアント証明書認証でVPN接続
現象
$ sudo openconnect --user vpn_user \
--cafile ~/.ssh/vpn/ca-cert.pem \
--certificate ~/.ssh/vpn/vpn_user-cert.pem \
--sslkey ~/.ssh/vpn/vpn_user-key.pem \
--servercert pin-sha256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX= \
https://vpn.example.com
POST https://vpn.example.com/
Failed to connect to 203.0.113.10:443: Can't assign requested address
Failed to open HTTPS connection to vpn.example.com
Failed to complete authentication
名前解決自体は成功(203.0.113.10 に解決)していて、TCP接続の段階で Can't assign requested address(= EADDRNOTAVAIL)になっている。
原因
ルーティングテーブルに、VPNサーバ宛ての古い固定ホストルートが残っていた。
$ netstat -rn -f inet | grep 203.0.113.10
203.0.113.10 192.168.11.1 UGHS en0
203.0.113.10 へはゲートウェイ 192.168.11.1(en0)経由で行け、というルートだ。
ところが、今の en0 のIPは別サブネットだった。
$ ifconfig en0 | grep "inet "
inet 10.0.0.216 netmask 0xffffff00 broadcast 10.0.0.255 # = 10.0.0.0/24
ゲートウェイ 192.168.11.1(192.168.11.0/24)は、今の en0 のサブネット(10.0.0.0/24)にいない。つまり直接は届かない。
route get も、この壊れたゲートウェイを返してきた。
$ route get 203.0.113.10
route to: 203.0.113.10
destination: 203.0.113.10
gateway: 192.168.11.1
interface: en0
openconnect が 203.0.113.10 に connect() するとき、カーネルはこの壊れたルートを選ぶ。でも届かないゲートウェイ向けの送信元アドレスを用意できず、EADDRNOTAVAIL → Can't assign requested address になっていた。
なぜ古いルートが残ったのか
openconnect は接続時、VPNサーバ宛てのパケットだけはトンネルを通さず物理ゲートウェイへ送る、というホストルートを自動で追加する。
以前 192.168.11.0/24(自宅ルータ 192.168.11.1)で繋いだときに張られたこのルートが、異常終了で片付けられずに残った。その後ネットワークが変わって en0 が 10.0.0.x になり、ルートだけ取り残された、というわけだ。
対処
古いホストルートを消すだけ。
$ sudo route delete 203.0.113.10
消えたか確認。
$ netstat -rn | grep 203.0.113.10 # 何も出なければOK
あらためて openconnect を実行すれば、接続時に正しいゲートウェイでルートを張り直して繋がる。
切り分け手順まとめ
-
route get <VPNサーバIP>で、実際に使われるゲートウェイとインターフェースを確認する - そのゲートウェイが
ifconfig <IF>の今のサブネットに属しているか確認する(属していなければ壊れたルート) -
netstat -rn | grep <VPNサーバIP>で固定ルートの有無を確認する - 古ければ
sudo route delete <VPNサーバIP>
教訓
Can't assign requested address(EADDRNOTAVAIL)は、送信元アドレスを用意できないというエラーだ。外向きの接続でこれが出たら、まずルーティングの不整合(古くて壊れたルート)を疑うといい。
VPNクライアントが異常終了すると、後始末されないルートが残ることがある。ネットワークを切り替えたあとに急に繋がらなくなったら、route テーブルを見るのが近道だった。