socatでUDP proxyを用意してIPv6 LinkLocal経由でWireGuardを繋ぐメモ
... 何で? IPsec箱に追加できるネットワークでDHCPできないもんで。。IPv6 LinkLocalであれば自動的に静的なIPv6アドレスを設定してくれるのでそれを活用することにした。
(IPv4のAutoIPやLinkLocalアドレスはOSによって振られたり振られなかったりするので使い勝手がよくない)
構成
[PC A] -- IPv6 UDP --> [IPsec箱] -- IPv4 UDP + IPsec --> Internet ...
Internet --> [IPsec箱] --> IPv4 UDP --> [PC B]
(PC Aでは既に別のIPv4 VPNに参加してしまっている & IPsec箱のルーティングテーブルを書き換えられないため、PC AとIPsec箱の間はIPv4で交信できない。)
PC A と PC BはUDPで交信し、その上でWireGuardのVPNを貼ることで、仮想的にPC AとPC Bで同一のLANを組む。
このとき、PC AとIPsec箱の間でIPv6 LinkLocalアドレスを使用したUDP通信を行い、IPsec箱には socat
コマンドを持ち込んで IPv6 → IPv4 の載せかえを行う。
socat
を使ってIPv6 → IPv4なUDP proxyを用意する
みんな大好き ...って程でもないか。 socat
コマンドを使うと 双方向 pipeを2つのソケット間で構成できる。
- IPv6アドレス
[fe80::xxxx:yyyy:zzzz:wwww]
UDP ポートPORT_A
- IPv4アドレス
aa.bb.cc.dd
UDPポートPORT_B
の転送を行うUDP proxyは以下のコマンドで作成できる。
socat udp6-listen:$PORT_A,bind=[fe80::xxxx:yyyy:zzzz:wwww] udp-sendto:aa.bb.cc.dd:$PORT_B
socat
コマンドのドキュメント にあるように、コマンドは2つの <address>
を取る。今回のコマンドは2つのアドレス:
udp6-listen:$PORT_A,bind=[fe80::xxxx:yyyy:zzzz:wwww]
udp-sendto:aa.bb.cc.dd:$PORT_B
を結ぶproxyを作っていることになる。アドレスには優先順位があり、 1 をopenしてから 2をopenするとされている。今回のケースでは、IPv6側から最初のパケットを送ることにしたため、IPv6側を 1 に置いた。
重要な点として、 UDPは接続指向のプロトコルではない ため、 socat
は典型的なUDPプロトコルの習慣 "あるUDPメッセージの返信は、そのメッセージを送信してきたUDPパケットのsource port宛に出す" を仮定している。つまり、 2 から送信されてきたデータの宛先は、1に届いたUDPパケットのsource port宛となる。
デフォルトでは 1 と 2 のアドレスファミリが一致していると仮定される。今回は1でIPv6を使い、2でIPv4を使うので、1では明示的にIPv6アドレスにbindしている。
WireGuardの設定
特に特殊な設定はない。IPv6アドレスを書くときは [ ]
で囲むくらいか。
[Interface]
PrivateKey = "PRIVATE_KEY"
ListenPort = PORT_B
Address = 192.168.101.1/32
[Peer]
PublicKey = "PUBLIC_KEY"
AllowedIPs = 192.168.101.2/32
Endpoint = [fe80::xxxx:yyyy:zzzz:wwww]:PORT_A