OpenBSD で ND Proxy を動作させる
コンテンツ
OpenBSD で ND Proxy 的な動作を有効にした際のメモ。
自宅ネットワークでグローバルなIPv6アドレスを利用するために ND Proxy 機 能が必要だった。ルータとして利用している OpenBSD で利用できる ND Proxy 実装がなかったため自作した。ソースコードは https://github.com/toru-mano/nd-reflector で公開している。
ND Proxy がなぜ必要か
次のような一般的な家庭ネットワークを想定する。
|
|
IPv4 ネットワークでは LAN 内の端末はプライベートアドレスが割り当てられ、 自宅のルータで NAT してインターネットと通信する。一方で、 IPv6 ネットワー クでは LAN 内であってもグローバルアドレスを割り当て、 NAT せずに通信する。 もちろん NAT することもできるがアドレス空間が広大な IPv6 ではその必要がな い。
(回線契約にも依存するが) ISP ルータから割り当てられる IPv6 ネットワー クが /64 の場合がある。このとき LAN 内の端末が、インターネットと通信す るには ISP Router が LAN 内の端末の MAC アドレスを Neighbor Discovery Protocol (NDP)で知る必要がある( NDP は IPv6 の ARP に相当する)。こ の NDP パケットはマルチキャストされるためルータを越えらず、LAN 内の端 末に届かない。これを届くようにするのが ND Proxy である(IPv4 では同様の Proxy ARP が存在する)。
OpenBSD で動作する ND Proxy がない問題
ND Proxy は基本的な機能であるが、少し調べた限り、残念ながら OpenBSD で 動作する実装がないようであった。
コマンド ndp(8) は NDP テーブルを操作することができる。オプション -s
でプロキシするアドレスを登録することができる。しかし、 IPv6 では端末が
使うアドレスは時間ともに変化し事前に分からないのでこのコマンドを使い
ND Proxy を実装することは難しい。
オープンソースで ND Proxy を実装したものは存在するが、いずれも Linux もしくは FreeBSD 向けに実装してあり OpenBSD では動作しない。
-
https://github.com/AlexandreFenyo/ndproxy
- FreeBSD の kernel module
-
https://github.com/DanielAdolfsson/ndppd
- Linux と FreeBSD 向け
-
https://github.com/setaou/ndp-proxy
- Linux 向け
ND Proxy を実現する ND Reflector の実装
そこで ND Proxy 的な動作をする ND Reflector https://github.com/toru-mano/nd-reflector を実装した。
このプログラムはWANインターフェイスで受信した Neighbor Solicitation (NS) メッセージを受信して、LAN 向けだった場合、 Neighbor Advertisement (NA) メッセージをWANインターフェイスから返信する。
RFC的にはプロキシは NS メッセージを LAN 内にプロキシして、LANから NAメッ セージを受信したときに限り、WANインターフェイスから NA メッセージを返 信すべきである(参照、RFC4390 Neighbor Discovery Proxies (ND Proxy))。 これには状態管理が必要であり実装が複雑になる。一方で、WAN インターフェ イスで LAN 向けの NS メッセージを受信したら即座に WAN インターフェイス から返信するシンプルなプログラムでも目的が達成できると考えた。動作的に はNSメッセージをNAメッセージとして反射するため ND Reflector と名付けて 実装した。
ビルド、インストールと動作
ソースコードをクローンして次のようにビルドする。
|
|
次のようにしてWAN インターフェイスを em0
, LAN インターフェイスを
em1
に指定して ND Reflector ndrd
を実行できる。
|
|
設定例
次のように WAN インターフェイスを em0
, LAN インターフェイスを em1
とするネットワークでは次のようにOpenBSDを設定する。
|
|
ISP ルータから割り当てられる IPv6 プレフィックスを 2001:db8::/64
, デ
フォルトゲートウェイを fe80::1
とする。
WANインターフェイスの設定 /etc/hostname.em0
.
|
|
LANインターフェイスの設定 /etc/hostname.e10
.
|
|
LAN 内の端末に IPv6 アドレスを配布するための Router advertisement daemon (rad) の設定 /etc/rad.conf
.
|
|
次で IPv6 パケットのルーティングを有効にし、 ND Reflector ndrd
を実
行する。
|
|
理由は後述するが "cannot forward" というメッセージを抑制するために次を pf.conf
に追加しておく。
|
|
"cannot forward" message
何もしないと dmesg
や /var/log/messages
に次のようなメッセージが複
数記録される。
cannot forward src fe80::1, dst 2001:db8::1, nxt 58, rcvif 1, outif 2
これは ISP Router が NDP テーブルを更新するために NS メッセージをユニ キャスト宛に(マルチキャスト宛ではなく)送信するため。ND Reflector は これらのメッセージを受信すると、通常通りNAメッセージを返信する。これと 同時に OpenBSD カーネルは NS メッセージを LAN インターフェイスに転送し ようとする。この NS メッセージはリンクローカルな送信元とグローバルな宛 先アドレスをもっており、送信元のアドレススコープは宛先のスコープよりも 小さいため、カーネルはこれらのパケットの転送を拒否して上のようなメッセー ジを記録する。
また、カーネルは ICMP6 destination unreachable メッセージをコード 2(beyond the scope of source address)で ISP ルータに通知する。結果と して ISP ルータは NA メッセージと unreachable エラーメッセージを同時に 受け取るため混乱する可能性がある。
回避するにはこれらのメッセージをカーネルが受信しないような次のルールを pf.conf(5) に設定すれば良い。
|
|
ND Reflector ndrd
は bpf(4) を使いパケットを受信し、 bpf(4) は pf(4)
よりも先に動作するため、上のルールの影響を受けない。
実装について簡単にメモ
-
自分宛ではないパケットを受信するため
bpf
を使う- NS メッセージがマルチキャストアドレス宛に送信されるため受信インター フェイスをプロミスキャスモードにする
-
NS メッセージに NA メッセージを返信すべきか否かはルーティングテーブルを見る
- NS のターゲットアドレスのルーティング先が LAN インターフェイスであった場合 NA メッセージを返信する
- ルーティングテーブルはソケット
AF_ROUTE
を利用する(参考 route(4)) - ソケットを読むときは timeout を設定しないと、時々プログラムが停止する
作成者 Toru Mano
最終更新時刻 2023-01-01 (c70d5a1)