DHCPv6-PDでIPv6のサブネットワークを作成


フレッツ光ネクストは、ひかり電話契約をすると/64よりも小さなプレフィックスを使用できます。このプレフィックスを使用すると、ひかり電話ルータの下に別のルータを置いて自分のネットワークを持つことができます。今回はこの方法で自分のネットワークをIPv6の世界に接続してみます。

ひかり電話契約をしていない場合は、IPv6とIPv4のデュアルスタックで接続のようにNAPT(マスカレード)を使用してネットワークを接続します。

ここでの設定は、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-hook2にスクリプトを書いておいてトンネルを作成します。スクリプトが実行される時に設定される変数については、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 '{print $2}'`
    if [ "$LOCAL" = '' ]; then
	return
    else
	logger dig DS-Lite tunnel $WAN_IF $LOCAL === $REMOTE
    fi

    # IPIP6 tunnel linkup
    ip -6 tunnel add $TUN_IF mode ip4ip6 remote $REMOTE local $LOCAL dev $WAN_IF
    ip link set dev $TUN_IF up

    # IPv4 routing
    route delete default
    route add default dev $TUN_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
    if $if_up ; then
	case "$reason" in
	    BOUND6|REBIND6)
		dig_tunnel
		;;
	esac
    else
	if [ "$reason" = EXPIRE6 ] ; then
	    delete_tunnel
	fi
    fi
fi

参照と脚注

  1. 今回使ったdhcpd以外にもIPv6用のDHCPクライアントはいくつかありますが、dibblerWIDE-DHCPv6では上手くプレフィックスの委譲を受けることができませんでした。委譲はしてもらえていたのかも知れませんが、インターフェイスにアドレスが設定されませんでした。自分で設定する必要があったのかも知れません。

  2. dhcpcdは、/etc/dhcpcd.enter-hookだけでなく/etc/dhcpcd.exit-hookも実行します。しかし今回順番は関係ないので、トンネルの作成と破棄の両方共/etc/dhcpcd.enter-hookに書いています。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です