Router Setting for IPv4 over IPv6
光回線に高速接続できる固定IPが欲しい。
動機
昨今の光回線におけるPPPoEでのプロバイダー接続では時間帯によってはスピードが出ないらしい。 Googleで適当に調べたところ、プロバイダー側でIPv4網と接続する部分で輻輳が発生するそうだ。あー、なるほど、そういうことね。解決策としては、インターネットへはIPv6でネイティブ接続して、 IPv4についてはカプセル化した IPv4 over IPv6 を利用すればいいようだ。 OCN 光 with フレッツの場合、IPv4 over IPv6 は MAP-E なる方式で実現している。ところが、 MAP-E ではアドレス・ポート変換が起こるため、サーバを立てても固定のIPv4アドレスが利用できない[1]。ならば、PPPoEも併用してしまえばいい。クライアント機器からのアクセスには接続スピードを優先してMAP-Eを利用し、固定IPが必要なサーバはPPPoE接続を利用する[2]。なお、固定IPが欲しいのは自宅サーバを立てて色々遊びたいから。
接続方法
ルータを二つ使ってしまえば簡単に出来そう。
赤の矢印がPPPoE接続の経路(IPv4)、紫の矢印がIPv4 over IPv6接続の経路(IPv4)、青の矢印がIPv6接続の経路[3]。 NTTのフレッツ網はもともとIPv6で構築されているようなので[4]、 IPv6で通信する限りは特になにもしないでいい。 IPv6に関しては固定IPも自動で割り当てられる。

ただ、ルータ二台運用とか無駄だし[5]、どのみちサーバを立てるのであれば、サーバにルータの機能も持たせてやればいい。
この場合、イーサネットを二つ持つPCを用意すればいい[6]。 WAN側のイーサネットには、PPPoE用のトンネル(IPv4)と、MAP-E用のトンネル(IPv6)を作成する。前者は簡単。ググればいくらでも情報はある。私はDebian使いなので、「debian pppoe」で検索したが、具体的な設定方法のページがいくつも見つかった。後者も十分に情報は揃っている。このページを参考にすれば、そのままでMAP-EによるIPv4 over IPv6接続が完了する。

今回、PPPoE接続するスクリプトと、MAP-E接続するスクリプトを作成して、 interfecesファイルに次の設定を加えて、それぞれの接続スクリプトを呼び出すようにした。
- # The primary network interface
- allow-hotplug enp1s0
- iface enp1s0 inet6 auto
- up MODE=start /root/etc/mape.sh
- up MODE=start /root/etc/pppoe.sh
- down MODE=stop /root/etc/pppoe.sh
- down MODE=stop /root/etc/mape.sh
- iface enp1s0 inet manual
/etc/network/interfaces
MAP-E接続スクリプトとPPPoE接続スクリプトは以下の通り。ファイアウォールとして機能させるために、いろいろパケット制限も付け加えています。
- #!/bin/sh
- ### parameters
- WANDEV='enp1s0'
- TUNDEV='ip6tnl1'
- #http://ipv4.web.fc2.com/map-e.html
- BR='????:????:????:????' #peeraddr
- CE='????:????:????:????:????:????:????:????'
- IP4='???.???.???.???'
- PSID='?'
- ### map stop
- CHAIN='MAPE'
- if [ "stop" = "$MODE" ]; then
- sysctl -w net.ipv4.ip_forward=0
- sysctl -w net.ipv6.conf.all.forwarding=0
- sysctl -w net.ipv6.conf.enp1s0.accept_ra=1
- sysctl -w net.ipv6.conf.wlp2s0.accept_ra=1
- sysctl -w net.ipv6.conf.enp3s0.accept_ra=1
- iptables -t mangle -L -n -v --line-numbers \
- | grep "TCPMSS.*$TUNDEV.*clamp" | cut -f1 -d " " \
- | sort -r | xargs -n1 -r iptables -t mangle -D FORWARD
- for i in INPUT OUTPUT FORWARD; do
- iptables -D $i -j ${CHAIN}$i
- iptables -F ${CHAIN}$i
- iptables -X ${CHAIN}$i
- done
- for i in OUTPUT PREROUTING POSTROUTING; do
- iptables -t nat -D $i -j ${CHAIN}nat$i
- iptables -t nat -F ${CHAIN}nat$i
- iptables -t nat -X ${CHAIN}nat$i
- done
- iptables -P INPUT ACCEPT
- iptables -P OUTPUT ACCEPT
- iptables -P FORWARD ACCEPT
- ip -4 route del default dev $TUNDEV
- ip -6 address del $CE dev $WANDEV
- ip -6 tunnel del $TUNDEV
- exit 0
- elif [ "start" = "$MODE" ]; then
- ip -6 address add $CE dev $WANDEV
- ip -6 tunnel add $TUNDEV mode ip4ip6 \
- remote $BR local $CE dev $WANDEV encaplimit none
- ip link set dev $TUNDEV mtu 1460
- ip link set dev $TUNDEV up
- ip -4 address add $IP4 dev $TUNDEV
- ip -4 route add default dev $TUNDEV
- iptables -P INPUT DROP
- iptables -P OUTPUT DROP
- iptables -P FORWARD DROP
- sysctl -w net.ipv4.ip_forward=1
- sysctl -w net.ipv6.conf.all.forwarding=1
- sysctl -w net.ipv6.conf.enp1s0.accept_ra=2
- sysctl -w net.ipv6.conf.wlp2s0.accept_ra=2
- sysctl -w net.ipv6.conf.enp3s0.accept_ra=2
- else
- echo unknown MODE
- exit 1
- fi
- ### map start
- iptables -N ${CHAIN}INPUT
- iptables -N ${CHAIN}OUTPUT
- iptables -N ${CHAIN}FORWARD
- iptables -t nat -N ${CHAIN}natOUTPUT
- iptables -t nat -N ${CHAIN}natPREROUTING
- iptables -t nat -N ${CHAIN}natPOSTROUTING
- # nat
- rule=1
- while [ $rule -le 63 ] ; do
- mark=`expr $rule + 16`
- pn=`expr $rule - 1`
- portl=`expr $rule \* 1024 + $PSID \* 16`
- portr=`expr $portl + 15`
- iptables -t mangle -L -n -v --line-numbers \
- | grep "TCPMSS.*$TUNDEV.*clamp" | cut -f1 -d " " \
- | sort -r | xargs -n1 -r iptables -t mangle -D FORWARD
- iptables -t nat -A ${CHAIN}natPREROUTING -m statistic \
- --mode nth --every 63 --packet $pn -j MARK --set-mark $mark
- iptables -t nat -A ${CHAIN}natOUTPUT -m statistic \
- --mode nth --every 63 --packet $pn -j MARK --set-mark $mark
- iptables -t nat -A ${CHAIN}natPOSTROUTING -p icmp \
- -o $TUNDEV -m mark --mark $mark -j SNAT --to $IP4:$portl-$portr
- iptables -t nat -A ${CHAIN}natPOSTROUTING -p tcp \
- -o $TUNDEV -m mark --mark $mark -j SNAT --to $IP4:$portl-$portr
- iptables -t nat -A ${CHAIN}natPOSTROUTING -p udp \
- -o $TUNDEV -m mark --mark $mark -j SNAT --to $IP4:$portl-$portr
- rule=`expr $rule + 1`
- done
- # for big packet
- iptables -t mangle -o $TUNDEV --insert FORWARD 1 \
- -p tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1400:65495 \
- -j TCPMSS --clamp-mss-to-pmtu
- # for return packet
- iptables -A ${CHAIN}FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
- # ping
- iptables -A ${CHAIN}INPUT -p icmp --icmp-type echo-reply -d $IP4 -j ACCEPT
- iptables -A ${CHAIN}OUTPUT -p icmp --icmp-type echo-request -s $IP4 -j ACCEPT
- # security
- iptables -A ${CHAIN}INPUT -f -j DROP
- iptables -A ${CHAIN}INPUT -p tcp --tcp-flags ALL ALL -j DROP
- iptables -A ${CHAIN}INPUT -p tcp --tcp-flags ALL NONE -j DROP
- iptables -A ${CHAIN}INPUT -p tcp ! --syn -m state --state NEW -j DROP
- # SSH client
- iptables -A ${CHAIN}INPUT -p tcp --sport 22 \
- -m state --state ESTABLISHED -j ACCEPT
- iptables -A ${CHAIN}OUTPUT -p tcp --dport 22 \
- -m state --state NEW,ESTABLISHED -j ACCEPT
- # SMTP client
- iptables -A ${CHAIN}INPUT -p tcp --sport 25 \
- -m state --state ESTABLISHED -j ACCEPT
- iptables -A ${CHAIN}OUTPUT -p tcp --dport 25 \
- -m state --state NEW,ESTABLISHED -j ACCEPT
- # DNS lookup
- iptables -A ${CHAIN}INPUT -p tcp --sport 53 \
- -m state --state ESTABLISHED -j ACCEPT
- iptables -A ${CHAIN}OUTPUT -p tcp --dport 53 \
- -m state --state NEW,ESTABLISHED -j ACCEPT
- iptables -A ${CHAIN}INPUT -p udp --sport 53 \
- -m state --state ESTABLISHED -j ACCEPT
- iptables -A ${CHAIN}OUTPUT -p udp --dport 53 \
- -m state --state NEW,ESTABLISHED -j ACCEPT
- # HTTP client
- iptables -A ${CHAIN}INPUT -p tcp --sport 80 \
- -m state --state ESTABLISHED -j ACCEPT
- iptables -A ${CHAIN}OUTPUT -p tcp --dport 80 \
- -m state --state NEW,ESTABLISHED -j ACCEPT
- # HTTPS client
- iptables -A ${CHAIN}INPUT -p tcp --sport 443 \
- -m state --state ESTABLISHED -j ACCEPT
- iptables -A ${CHAIN}OUTPUT -p tcp --dport 443 \
- -m state --state NEW,ESTABLISHED -j ACCEPT
- # NTP query
- iptables -A ${CHAIN}INPUT -p udp --sport 123 \
- -m state --state ESTABLISHED -j ACCEPT
- iptables -A ${CHAIN}OUTPUT -p udp --dport 123 \
- -m state --state NEW,ESTABLISHED -j ACCEPT
- # SMB block
- iptables -A ${CHAIN}FORWARD -p tcp --dport 135 -j DROP
- iptables -A ${CHAIN}FORWARD -p udp --dport 135 -j DROP
- iptables -A ${CHAIN}FORWARD -p tcp --dport 137:139 -j DROP
- iptables -A ${CHAIN}FORWARD -p udp --dport 137:139 -j DROP
- iptables -A ${CHAIN}FORWARD -p tcp --dport 445 -j DROP
- iptables -A ${CHAIN}FORWARD -p udp --dport 445 -j DROP
- ###
- iptables -A INPUT -j ${CHAIN}INPUT
- iptables -A OUTPUT -j ${CHAIN}OUTPUT
- iptables -A FORWARD -j ${CHAIN}FORWARD
- iptables -t nat -A OUTPUT -j ${CHAIN}natOUTPUT
- iptables -t nat -A PREROUTING -j ${CHAIN}natPREROUTING
- iptables -t nat -A POSTROUTING -j ${CHAIN}natPOSTROUTING
- exit 0
/root/etc/mape.sh
- #!/bin/sh
- ### parameters
- WAN=${WAN:-ppp0}
- LAN=${LAN:-enp3s0}
- ### pppoe stop
- CHAIN='PPPOE'
- if [ "stop" = "$MODE" ]; then
- WANHOST=$(ip -4 -o address show $WAN | awk '{print $4}')
- poff dsl-provider
- iptables -D ${CHAIN}SSHCONNECT -m recent --name sshchecklist \
- --rcheck --seconds 60 --hitcount 5 -j ${CHAIN}SSHATTACK
- iptables -D ${CHAIN}INPUT -i $WAN -d $WANHOST -p tcp --dport 22 \
- -m state --state NEW -j ${CHAIN}SSHCONNECT
- iptables -F ${CHAIN}SSHCONNECT
- iptables -F ${CHAIN}SSHATTACK
- iptables -X ${CHAIN}SSHCONNECT
- iptables -X ${CHAIN}SSHATTACK
- for i in INPUT OUTPUT FORWARD; do
- iptables -D $i -j ${CHAIN}$i
- iptables -F ${CHAIN}$i
- iptables -X ${CHAIN}$i
- done
- ip route del default via $WANHOST table 100
- ip rule del from $WANHOST table 100 prio 100
- exit 0
- elif [ "start" = "$MODE" ]; then
- pon dsl-provider
- TRYNUM=0
- while :
- do
- TRYNUM=$(expr $TRYNUM + 1)
- WANHOST=$(ip -4 -o address show $WAN | awk '{print $4}')
- if [ -n "$WANHOST" ]; then
- break
- fi
- if [ 99 -le $TRYNUM ]; then
- exit 1
- fi
- sleep 1
- done
- else
- echo unknown MODE
- exit 1
- fi
- ### pppoe start
- TRYNUM=0
- while :
- do
- TRYNUM=$(expr $TRYNUM + 1)
- LANADDR=$(ip -4 -o address show $LAN | awk '{print $4}')
- if [ -n "$LANADDR" ]; then
- break
- fi
- if [ 99 -le $TRYNUM ]; then
- exit 1
- fi
- sleep 1
- done
- # PBR
- ip rule add from $WANHOST table 100 prio 100
- ip route add default via $WANHOST table 100
- ### server
- iptables -N ${CHAIN}INPUT
- iptables -N ${CHAIN}OUTPUT
- iptables -N ${CHAIN}FORWARD
- # for big packet and ident
- iptables -A ${CHAIN}FORWARD -o $WAN \
- -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
- iptables -A ${CHAIN}INPUT -i $WAN -d $WANHOST -p tcp --dport 113 \
- -j REJECT --reject-with tcp-reset
- iptables -A ${CHAIN}OUTPUT -o $WAN -s $WANHOST -p tcp --sport 113 -j ACCEPT
- # SSH
- iptables -N ${CHAIN}SSHATTACK
- iptables -A ${CHAIN}SSHATTACK -m recent --name sshblacklist \
- --set -j LOG --log-prefix "SSHATTACK: "
- iptables -A ${CHAIN}SSHATTACK -j REJECT
- iptables -N ${CHAIN}SSHCONNECT
- iptables -A ${CHAIN}SSHCONNECT -m recent --name sshblacklist \
- --rcheck --seconds 600 -j REJECT
- iptables -A ${CHAIN}SSHCONNECT -m recent --name sshchecklist \
- --rcheck --seconds 60 --hitcount 5 -j ${CHAIN}SSHATTACK
- iptables -A ${CHAIN}SSHCONNECT -m recent --name sshchecklist --set
- iptables -A ${CHAIN}SSHCONNECT -j ACCEPT
- iptables -A ${CHAIN}INPUT -i $WAN -d $WANHOST -p tcp --dport 22 \
- -m state --state ESTABLISHED -j ACCEPT
- iptables -A ${CHAIN}INPUT -i $WAN -d $WANHOST -p tcp --dport 22 \
- -m state --state NEW -j ${CHAIN}SSHCONNECT
- iptables -A ${CHAIN}OUTPUT -o $WAN -s $WANHOST -p tcp --sport 22 -j ACCEPT
- # SMTP
- iptables -A ${CHAIN}INPUT -i $WAN -d $WANHOST -p tcp --dport 25 \
- -m state --state NEW,ESTABLISHED -j ACCEPT
- iptables -A ${CHAIN}OUTPUT -o $WAN -s $WANHOST -p tcp --sport 25 -j ACCEPT
- # HTTP
- iptables -A ${CHAIN}INPUT -i $WAN -d $WANHOST -p tcp --dport 80 \
- -m state --state NEW,ESTABLISHED -j ACCEPT
- iptables -A ${CHAIN}OUTPUT -o $WAN -s $WANHOST -p tcp --sport 80 -j ACCEPT
- # HTTPS
- iptables -A ${CHAIN}INPUT -i $WAN -d $WANHOST -p tcp --dport 443 \
- -m state --state NEW,ESTABLISHED -j ACCEPT
- iptables -A ${CHAIN}OUTPUT -o $WAN -s $WANHOST -p tcp --sport 443 -j ACCEPT
- ###
- iptables -A INPUT -j ${CHAIN}INPUT
- iptables -A OUTPUT -j ${CHAIN}OUTPUT
- iptables -A FORWARD -j ${CHAIN}FORWARD
- exit 0
/root/etc/pppoe.sh
pppoe.shの72行目から74行目は大事。 PPPoEから入って来たパケットはPPPoEから出て行くようにしている。 MAP-E側をデフォルトゲートウェイにしているので、これがないと外部からサーバへの接続の帰りのパケットがロストしてしまう。
この他、ローカルインターフェイスは全ての通信の許可とIPマスカーレードの設定をしている。また、本来、IPv6においても通信制限が必要だが、まだやっていない。
- [1] 厳密には正しくない。well-known portsにこだわらなければ、固定IPでサーバ運用できる。
- [2] これも厳密には正しくない。PPPoEでは接続のたびにアドレスが変更される。しかし、再接続はそう頻繁には発生しないので、DynamicDNSなどで対応すればいい。
- [3] 簡略化した概念図です。厳密なものではありません。
- [4] よくよく調べてみると実態は少し違うようだ。フレッツ網はグローバルなIPv6アドレスで構築されているものの、インターネットとは直接接続されていないため、巨大なLANと言ってもよいものらしい。それでも普通にIPv6通信は出来ているので、あまり深くは考えないことにした。図のNTT NGXとIPv6との接続は正しくない可能性が高い。
- [5] 高価なルータであれば、MAP-EとPPPoEの同時接続も可能
- [6] やろうと思えばイーサネットは一つでもよい。IP Aliasingを使えば一つのNICにいくらでもIPアドレスを付与できるので、LAN側もWAN側も同じNICを共用できる。ただし、クライアントから外部に向けた全てのパケットがルータのNICを二度通るので効率が悪い。