ホームルータとして利用している OpenBSD を使い,固定回線(フレッツ)をIPv6で接続した話.IPv6で接続する方法は複数存在しており,実際にどの方法を使えば良いかなどの情報が少なく,色々と苦労したというメモ.

ISP は OCN を利用しており,OCNは IPv6接続を PPPoE 形式で提供している(ちなみに追加料金は不要).

別途,機器が必要なようにも読めるがなくても大丈夫である.

今回はIPv6 PPPoE 接続の話題である.別方式としてフレッツ網内で PPPoE をカプセル化しない,IPv6 IPoE (IP over Ethernet) 方式が存在する.素人考え的には PPPoE のカプセンリングなんて面倒なことはせずに NTT東西が ISPサービスも提供すれば,シンプルになるとは思うのだが.

IPv6 PPPoE 接続の流れ

次のような流れで IPv6 接続ができることが分かった.

  1. IPv6 用の PPPoE セッションを確立する(IPv4とは別セッション).
  2. DHCPv6 クライアントを使用して IPv6 のプレフィックスを取得する.
  3. DHCPv6 クライアントを使用して LAN インターフェイスに IPv6 アドレスを割り当てる.
  4. Router Advertisement (RA) と DHCPv6 サーバ を使用して LAN 内の端末に IPv6 アドレス割り当て,同時にデフォルトルート,DNSサーバの情報を渡す.

IPv4接続の場合は step 1 で接続完了なのだが,IPv6の場合は step 4 まで(厳密には 3 まで)必要である.ここを理解していなかったので,アレIPv6アドレスふってこない,などと勘違いしていた.

IPv6の場合は追加で DHCPv6 の prefix delegation が必要になる.これはIPアドレスではなくIPv6のサブネットを取得する.IPv4 で例えると 192.0.2.1 ではなく 192.0.2.0/24 を取得するイメージ.このサブネットからルータのLAN側インターフェイスにIPv6を割り当て,LAN内の端末にIPv6を払い出す.ここで DHCPv6 と RA を利用する.

IPv6用PPPoEセッションの確立

IPv4用の PPPoE セッションが pppoe0 で確立されているので,別の PPPoE用のインターフェイス pppoe1 でセッションを開始する.

IPv6用の PPPoE セッションを開始するには次のようなコマンドを実行する.

1
2
ifconfig pppoe1 inet6 <link-local-address> prefixlen 64 mtu 1454 pppoedev em0\
         authproto chap authname <username> authkey <password> up

オプションの inet6 の後の <link-local-address> prefixlen 64eui64 でも可だが,後でIPv6のデフォルトゲートウェイを pppoe1 の IPv6 リンクローカルアドレスに設定するので, ifconfig pppoe1 で確認しておく.また, eui64 を使用した場合,物理インターフェイスのマックアドレス,今回は em0, を元にリンクローカルアドレスが生成される.

ifconfig pppoe1 で次のような表示が含まれていればセッションが確立されている.

1
sppp: phase network authproto chap authname "username"

IPv4の場合と違い,この段階ではグローバルな IPv6 アドレスが割り当てられていないので,インターネットには接続できない.グローバルアドレスは次の DHCPv6-PD で取得する.

DHCPv6によるIPv6のプレフィックスの取得

Prefix Delegation (PD) によりグローバルな IPv6 サブネットを取得する.IPv4の場合は単一のアドレスが割り当てられるため NAT が必要だが, IPv6 の場合はサブネットが割り当てられるので NAT が不要となる.

IPv6では128ビットのうち前半64ビットでサブネット,後半64ビットでホストを表す.一般に DHCPv6-PD では64ビット未満のプレフィックスが割り当てられる.例えば私の環境(OCN)では56ビットのプレフィックスが割り当てられている.残り 8 ビットはどうするのかというと,DHCPv6-PD でプレフィックスを受け取った側で自由に設定できる.後半64ビットはIPアドレスを設定するインターフェイスのMACアドレスから生成する.


(追記) 以下ではDHPCv6-PDクライアントとして wide-dhcpv6 を利用してるが,現在は dhcpcd を利用している.理由は次.

  • IPv6アドレスがeui64に固定される. 2001:0DB8::1 等の設定できずMACアドレスが埋め込まれたIPアドレスで通信することになる.
  • 移譲されたプレフィックスの経路を reject していないためパケットループが発生する可能性がある(実際は問題にならないが…).
  • 下記でも dhcpcd が良いのではと指摘されている.

インストールは pkg_add dhcpcd でできる.設定方法は /usr/local/share/doc/pkg-readmes/ にインストールされるドキュメントに記載してある.例えばWAN側PPPoEインターフェイス pppoe1 でDHCPv6-PDのリクエストを送信して,取得したプレフィックスをLAN側 vether0 に割り当てるには次のような設定ファイル /etc/dhcpcd.conf を利用する.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
ipv6only
duid
persistent

script /usr/bin/true

noipv6rs

allowinterfaces pppoe1 vether0

interface pppoe1
        ia_pd 0 vether0/1

(追記終り)


DHCPv6は標準ではインストールされておらず,バイナリパッケージを pkg_add でインストールする必要がある.いくつか DHCPv6 の機能を提供するパッケージが複数存在する.DHCPv6 のサーバ機能も必要になる(後述)こと,必要なのは IPv6 の DHCP だけなので wide-dhcpv6 を選択した.インストールするには pkg_add wide-dhcpv6 を実行する.

設定は /etc/dhcp6c.conf に記述する.次の設定はWAN側PPPoEインターフェイス pppoe1 で DHCPv6-PD のリクエストを送信し,/56のIPv6プレフィックスを取得する.そのプレフィックスに最下位のみ 1 となっている 8 ビットを追加して /64 のサブネットを作成し,LAN側インターフェイス vether0 にIPv6アドレスを割り当てる.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
interface pppoe1 {
        send ia-pd 0;
};

id-assoc pd {
        prefix-interface vether0 {
                sla-id 1;
                sla-len 8;
        };
};

DHCPv6のクライアントを許可するように /etc/pf.conf を編集する.DHCPv6は宛先IPv6が特殊なパケットを送信するためステートフルファイアウォールが効かない.

1
pass in inet6 proto udp to (self) port dhcpv6-client

IPv4の場合と違い PPPoE のインターフェイスにはグローバルアドレスは割り当てない.しかし,デフォルトゲートウェイは PPPoE インターフェイスなので次のように設定する.

1
route add -inet6 default <link-local-address>%pppoe1

この時点で OpenBSD ルータは IPv6 でインターネットに接続できる. AAAAで名前解決ができる場合は次のコマンド

1
ping6 ipv6.google.com

名前解決ができない場合は Google の public DNS に ping を打つことで疎通を確認できる.

1
ping6 2001:4860:4860::8888

次はLAN内の端末がインターネットに IPv6 で接続できるように RA と DHCPv6 サーバを設定する.

LAN内の端末にIPv6, デフォルトルート,DNS情報を配布する.

Router Advertisement (RA) と DHCPv6 サーバにより実現する.技術的には RA だけで実現できるが Windows は RA による DNS サーバの配布(RFC 6106)に対応していないので,そのため DHCPv6のサーバ機能を利用する.

(上のリンクにはWindowsでRAでのDNSをサポートしないのはレイヤ違反だから,RAはネットワークレイヤでDNSはアプリケーションレイヤ,というコメントがありましたが,個人的には細けえこたあいいんだよという気がします)

RA, DHCPv6 どちらも機能的にはLAN内の端末にIPアドレスとDNS情報を配布することができる.クライアントにどちらから情報を取得するかを伝えるためには RA の Managed address configuration flag (Mフラグ) と Other configuration flag (Oフラグ)を使用する.MフラグがオンのときはIPアドレスとDNSの両方をDHCPv6から取得することを伝える.OフラグがオンがDNSなどのみDHCPv6を利用することを伝える.

Windows がRAによるDNSサーバの配布に対応していないのでOフラグをONにして,IPアドレスはRA,DNSサーバはDHCPv6を利用する.

RAの設定は /etc/rtadvd.conf に記述する.次は vether0 でMフラグをオンにしてRAを有効にする.

1
2
3
4
5
vether0:\
        :raflags#64:\
        :rdnss="your-dns-server-ipv6-address":\
        :dnssl="example.con":\
        :mtu#1454:

(念の為,DHCPv6に対応していない機器も考慮してDNS情報を設定している)

DHCPv6サーバの設定は /etc/dhcp6s.conf に記述する.

1
2
option domain-name-servers your-dns-server-ipv6-address;
option domain-name "example.com";

LAN内のDNSリゾルバのグローバルIPv6アドレスは変化する可能性があるので,現在はユニークローカルIPv6ユニキャストアドレス(IPv4でいうところのプライベートアドレス)を割り当て,そちらをDNSサーバの情報として配布している.ULAでリッスンしておけばファイアウォールに不備があってもオープンリゾルバにはらないので.

pf の設定

IPv6用に pf を設定する必要がある.現在は次のように設定している(該当部分のみ抜粋).

1
2
3
4
5
6
7
8
pass out quick inet6 modulate state

pass in quick on $int_if inet6 from (vether0:network)

pass in on egress inet6 proto ipv6-icmp icmp6-type \
    { unreach, toobig, timex, paramprob, echoreq, echorep, 144, 145, 146, 147 }

pass in inet6 proto udp to (self) port dhcpv6-client

1行目の pass out quick inet6 modulate state は外向きの IPv6 トラフィックを許可している.

2行目の pass in quick on $int_if inet6 は LAN内の端末からの IPv6 トラフィックを許可している.

3行目の pass in on egress inet6 proto ipv6-icmp ... で ICMPv6 を許可している.IPv6 ではフラグメントできないので Path MTU の発見が失敗すると最悪の場合,通信ができなくなる.その他に関しては RFC 4890 の Section 4.3 を参考にした.

4行目の pass in inet6 proto udp to (self) port dhcpv6-client は DHCPv6 のクライアントパケットの戻りを許可するため.

起動時設定

あとは起動時に自動でIPv6のPPPoEセッションが確立して,DHCPv6-PDでプレフィックスを取得するように /etc/hostname.pppoe1 に設定すれば良いのだが,ここがうまくいかなかった.

次のような設定を記述したが, PPPoEセッションがいつまでたっても確立されなかった.

1
2
3
inet6 <link-local-address> prefixlen 64 mtu 1454 pppoedev em0\
      authproto chap authname <username> authkey <password> up
!/sbin/route add -inet6 default <link-local-address>%pppoe1

なぜかIPv4のPPPoEセッションも確立しないときもあった.どうやら,同時に2つのPPPoEセッションを確立しようとすると失敗することがあるようだったので,IPv4のPPPoEセッションが確立してからIPv6のPPPoEセッションを開始するようなスクリプトを用意して,起動時に実行するようにしている.

ひとまず,現在は次のようなシェルスクリプトでその場しのぎをしている( tcpdump とかでパケットキャプチャしてデバッグするほどの気力と知識がなかたっので,その場しのぎ).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/bin/sh

# wait until pppoe0 get an IPv4 address
until ifconfig pppoe0 | grep 'sppp: phase network' > /dev/null
do
        /bin/sleep 15
done

# before starting, destroy pppoe1 interface, just in case
/sbin/ifconfig pppoe1 down
/sbin/ifconfig pppoe1 destroy
/sbin/ifconfig pppoe1 create

# enable debug message output
#/sbin/ifconfig pppoe1 debug

# start pppoe1 session to get an IPv6 address
/sbin/ifconfig pppoe1 inet6 <link-local-address> prefixlen 64 mtu 1454 \
        pppoedev em0 authproto chap \
        authname username authkey secret up

# wait until pppoe1 get an IPv6 address
until ifconfig pppoe1 | grep 'sppp: phase network' > /dev/null
do
        /bin/sleep 15
done

# set defalt route for IPv6
/sbin/route delete -inet6 default
/sbin/route add -inet6 default <link-local-address>%pppoe1

# Start DHCPv6 client on pppoe1
/etc/rc.d/dhcp6c start
/usr/local/sbin/dhcp6ctl stop interface pppoe1
/usr/local/sbin/dhcp6ctl start interface pppoe1

参考