Open3

socatでUDP proxyを用意してIPv6 LinkLocal経由でWireGuardを繋ぐメモ

okuokuokuoku

... 何で? 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 の載せかえを行う。

okuokuokuoku

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つのアドレス:

  1. udp6-listen:$PORT_A,bind=[fe80::xxxx:yyyy:zzzz:wwww]
  2. 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している。

okuokuokuoku

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