DHCPv6-PDでIPv6のサブネットワークを作成
フレッツ光ネクストは、ひかり電話契約をすると/64 よりも小さなプレフィックスを使用できます。このプレフィックスを使用すると、ひかり電話ルータの下に別のルータを置いて自分のネットワークを持つことができます。今回はこの方法で自分のネットワークを IPv6 の世界に接続してみます。
ひかり電話契約をしていない場合は、IPv6 と IPv4 のデュアルスタックで接続のように NAPT(マスカレード)を使用してネットワークを接続します。
- サイト構成
- DHCPv6-PD によるプレフィックスの委譲
- dhcpcd を有効に
- dhcpcd の設定
- interface 設定を削除
- ufw の設定
- IPv6 の転送を許可
- radvd の設定
- IPv6 での通信確認
- 設定に間違いがないのに通信できない
- DS-Lite によるトンネル作成
ここでの設定は、ZOOT NATIVE (DS-Lite)でネット接続の状態になっている前提での説明です。追加で必要なradvd
のインストール方法は、IPv6 と IPv4 のデュアルスタックで接続を参照して下さい。
サイト構成
Raspberry Pi のインターフェイスは、次のように使用します。
インターフェイス | ネットワーク | アドレス |
---|---|---|
eth0 | 自分のネットワーク/64 | DHCPv6-PD で自動設定 |
eth1 | Internet(IPoE) | DHCPv6 で自動設定 |
自分のネットワーク内の端末は、Raspberry Pi が送信する RA で自動的に IPv6 アドレスやデフォルトルータが設定されます。
DHCPv6-PD によるプレフィックスの委譲
自分のネットワークを上位のネットワークへ接続するためには、プレフィックス委譲を受けます。この委譲を受けるのに使われるのが DHCPv6-PD です。上位のルータは、DHCPv6-PD によるやりとりでプレフィックスの払い出しと同時に、払い出したプレフィックスに相当する IPv6 パケットをどこに送ったらよいかを記録しています。
DHCP は IPv4 で IP アドレスなどネット接続に必要な情報を自動設定する時にお馴染みですが、IPv6 用の DHCP(DHCPv6)は名前は同じでも全く別のプロトコールです。もっとも IPv4 で使った DHCP クライアントは、DHCPv6 クライアントとしても機能します。
DHCPv6 対応クライアントには何種類かありますが、今回は Raspbian(jessie)に標準でインストールされているdhcpcd
を使用します。他の DHCPv6 クライアントを使用してプレフィックスの委譲を受ける時は、IPv6 プレフィックス委譲 (DHCPv6-PD) – Archlinuxを参照して下さい(注 1)。
dhcpcd を有効に
dhcpcd
は、ZOOT NATIVE (DS-Lite)でネット接続で無効にしているので、次のコマンドでシステムが起動時する時に自動的に実行開始されるようにします。
$ sudo systemctl enable dhcpcd.service
dhcpcd の設定
dhcpcd の設定は、/etc/dhcpcd.conf
に書きます。
# この上はデフォルトのまま
# IPv4は設定しない
ipv6only
# LANのIPv4用
# /etc/network/interfacesで設定される。
interface eth0
noipv6
# https://wiki.archlinuxjp.org/index.php/IPv6#.E3.83.97.E3.83.AC.E3.83.95.E3.82.A3.E3.83.83.E3.82.AF.E3.82.B9.E5.A7.94.E8.AD.B2_.28DHCPv6-PD.29
duid
noipv6rs
waitip 6
# WANのIPv6用 DHCPv6-DP
interface eth1
ipv6rs
iaid 1
# use the interface connected to your LAN
ia_pd 1 eth0
ひかり電話ルータは IPv4 の DHCP サーバ機能を持っているので、ip6only(noipv4)をしないと DHCP により IPv4 のアドレスが eth1 に割り当てられます。
interface 設定を削除
dhcpcd
でインターフェイスを設定するので/etc/network/interfaces
にある eth1 の設定は不要です。そこで eth1 に関する部分は全て削除してしまいます。
eth0 もdhcpcd
で設定できるので不要な気がしますが、これを削除するとシステムを起動する時に DHCP サーバがエラーで実行開始できなくなってしまいます。また DHCPv6-PD でプレフィックスの委譲を受ける時に/etc/resolv.conf
の内容がひかり電話ルータからのものになってしまいます。そのため eth0 はinterfaces
で設定します。
ufw の設定
プレフィックス委譲を使って自分のネットワークを接続すると、ネットワーク内の全ての端末にグローバルな IPv6 が割り振られます。そのためアドレスが分かればほとんど何の制限もなしに世界中からアクセスすることが可能となります。
その裏返しとして想定外の端末が世界に公開されてしまうなど、NAPT(マスカレード)を使ったIPv6 と IPv4 のデュアルスタックで接続より各端末がより危険な状態に置かれ、世界中から自分のネットワーク内の端末が攻撃される危険性にさらされます。
この危険性を少しでも小さくするには、ルータを通過するパケットの選別が非常に大切です。
と言っても今回は実験なので必要最小限の制限とし、次のような一般的な最小限のポリシーとしました。
- 自分のネットワークから外へは無制限に出ていける。
- 自分のネットワークへは、出て行ったパケットに関連するパケットのみが入れる。
そこで/etc/default/ufw
を次のように変更し、許可されたパケット以外は全て破棄するようにします。
DEFAULT_FORWARD_POLICY="DROP"
DEFAULT_INPUT_POLICY="DROP"
DEFAULT_OUTPUT_POLICY="ACCEPT"
許可するパケットのルールは、ufw
の設定ファイルに追加します。
/etc/ufw/before.rules(IPv4 用)
# 以下を追加する。
# accept all from LAN and returned RELATED or ESTABLISHED packets
-A ufw-before-forward -i eth0 -j ACCEPT
-A ufw-before-forward -m state --state RELATED,ESTABLISHED -j ACCEPT
/etc/ufw/before6.rules(IPv6 用)
# 以下を追加する。
# https://www.cert.org/downloads/IPv6/ip6tables_rules.txt
# accept from LAN
-A ufw6-before-input -i eth0 -j ACCEPT
# allow DHCPv6-PD
#-A ufw6-before-input -p udp -s fe80::/10 --sport 547 -d fe80::/10 --dport 546
-j ACCEPT
-A ufw6-before-input -p udp -s fe80::/10 -d fe80::/10 --dport 546 -j ACCEPT
-A ufw6-before-input -p icmpv6 -s fe80::/10 --icmpv6-type 130 -j ACCEPT
# accept all from LAN and returned RELATED or ESTABLISHED packets
-A ufw6-before-forward -i eth0 -j ACCEPT
-A ufw6-before-forward -m state --state RELATED,ESTABLISHED -j ACCEPT
ufw
に元からあったルールを少し緩めて宛先ポート番号だけで許可するようにしているのは、DHCPv6-PD によるプレフィックスの委譲を受けるためです。
DHCPv6 の通信を許可するルールが before6.rules に入っていたので大丈夫だと思ったのですが、最初は DHCPv6-PD によるプレフィックスの委譲を受けることができませんでした。そこで原因を探るために捨てているパケットを見ていたら、ひかり電話ルータの DHCPv6 サーバから送られてくるパケットのポート番号が 547 ではありませんでした。このため送信元のポート番号を無視して宛先のポート番号だけを見るようにしました。
IPv6 の転送を許可
IPv4 をトンネルするために IPv4 の転送を許可していましたが、加えて IPv6 も転送するように設定します。
/etc/ufw/sysctl.conf
# 追加する
net/ipv6/conf/default/forwarding=1
net/ipv6/conf/all/forwarding=1
radvd の設定
radvd
を使って RA 送信します。自分のネットワークに接続された端末は、この RA を受信することで自動的に IPv6 の設定します。
radvd
が配布する RA の設定は/etc/radvd.conf/radvd.conf
に書きます。
interface eth0 {
AdvSendAdvert on;
#MaxRtrAdvInterval 30;
prefix ::/64 {
AdvRouterAddr on;
};
};
ここで指定しているプレフィックス::/64
は、マジックワードです。このアドレスを指定すると、指定された interface の全グローバルアドレスからプレフィックスを決定して RA を送り出してくれます。今回のように DHCPv6-PD で指定されるアドレスが何になるかわからない場合に便利な指定方法です。
IPv6 での通信確認
以上でルータの設定は完了です。ここで再起動して自分のネットワーク内の端末から IPv6 で通信できるか試してみます。
確認方法とトラブルシューティングは、IPv6 と IPv4 のデュアルスタックで接続のIPv6 での通信確認を参照して下さい。
設定に間違いがないのに通信できない
今回どこにも間違いはないのに外と通信できないことがありました。自分のルータとひかり電話ルータの間に置いた端末とは通信できるのでルータの設定に問題はないはずですが、なぜかひかり電話ルータを越えて外に出られませんでした。
ひかり電話ルータに Web ブラウザで接続して DHCPv6-PD によるプレフィックスの払い出し状況を確認するとなぜか Raspberry Pi の LAN 側の MAC アドレスが登録されていました。そこから推測するとひかり電話ルータは外からの通信を接続されていない RasPi の LAN 側宛に送ってしまっているのではないかと推測しました。
RaspPi を何度再起動して DHCPv6-PD をしなおしても登録されている MAC アドレスは変わりません。
そこで/etc/dhcpcd.duid
と/etc/dhcpcd.secret
を削除して再起動してみました。するとひかり電話ルータに登録されている MAC アドレスは変化ありませんでしたが問題なく外と通信できるようになりました。
DS-Lite によるトンネル作成
IPv6 に関する設定は以上ですが、interfaces
から eth1 に関する設定を全て削除してしまったので IPv4 をトンネルするインターフェイスが作成されません。そこでdhcpcd
が設定のフェーズ毎に実行する/etc/dhcpcd.enter-hook
(注 2)にスクリプトを書いておいてトンネルを作成します。スクリプトが実行される時に設定される変数については、dhcpcd-run-hooks
のマニュアルを参照して下さい。
/etc/dhcpcd.enter-hook
#!/bin/sh
# http://techlog.iij.ad.jp/contents/dslite-raspi
REMOTE='2404:8e01::feed:101'
WAN_IF='eth1'
TUN_IF='ip6tnl1'
dig_tunnel()
{
# already tunnel exist?
ip addr show | grep TUN_IF>/dev/null
if [? -eq 0 ] ; then
return
fi
LOCAL=`ip -6 addr show WAN_IF scope global |grep inet6 | awk '{print2}'`
if [ "LOCAL" = '' ]; then
return
else
logger dig DS-Lite tunnelWAN_IF LOCAL ===REMOTE
fi
# IPIP6 tunnel linkup
ip -6 tunnel add TUN_IF mode ip4ip6 remoteREMOTE local LOCAL devWAN_IF
ip link set dev TUN_IF up
# IPv4 routing
route delete default
route add default devTUN_IF
}
delete_tunnel()
{
ip addr show | grep TUN_IF>/dev/null
if [? -ne 0 ] ; then
return
fi
logger delete DS-Lite tunnel
# delete DS-Lite tunnel
ip -6 tunnel delete TUN_IF
# delete default route
route delete default
}
if [ "interface" = WAN_IF ] ; then
ifif_up ; then
case "reason" in
BOUND6|REBIND6)
dig_tunnel
;;
esac
else
if [ "reason" = EXPIRE6 ] ; then
delete_tunnel
fi
fi
fi
参照と脚注
- 今回使った dhcpd 以外にも IPv6 用の DHCP クライアントはいくつかありますが、
dibbler
とWIDE-DHCPv6
では上手くプレフィックスの委譲を受けることができませんでした。委譲はしてもらえていたのかも知れませんが、インターフェイスにアドレスが設定されませんでした。自分で設定する必要があったのかも知れません。 dhcpcd
は、/etc/dhcpcd.enter-hook
だけでなく/etc/dhcpcd.exit-hook
も実行します。しかし今回順番は関係ないので、トンネルの作成と破棄の両方共/etc/dhcpcd.enter-hook
に書いています。