自力でIPv4 over IPv6をした(Tunnel編) | 愚行録 the Next Generation

自力でIPv4 over IPv6をした(Tunnel編) | 愚行録 the Next Generation

これまでのあらすじ

やめて!So-netのサービス変更で、MAP-Eなv6プラスを適用されたら、DS-liteでIPv4ネットワークと繋がってるRTX1200の精神まで燃え尽きちゃう!
お願い、死なないで城之内!あんたが今ここで倒れたら、舞さんや遊戯との約束はどうなっちゃうの? ライフはまだ残ってる。ここを耐えれば、マリクに勝てるんだから!
次回、「RTX1200死す」。デュエルスタンバイ!

VPSを利用した自力IPv4 over IPv6

夕方から夜のPPPoE経由の通信が遅いなら、プロバイダの提供するIPv4 over IPv6が使い物にならないなら、自力でやってしまえばいいじゃない、ということでやってみた。
自宅とVPSとの間にIPv6でトンネル張って、その中にIPv4の通信を通すのである。
VPSのOSはUbuntu 18.04、自宅のルータはYAMAHA RTX1200。

とはいえ、本当に自力で1からやれるほどLinuxの仕組みに詳しいわけではないので、やはり先人の知恵にすがるわけである。

やることは以下の4つ。

  • トンネルデバイスの作成とルーティング設定
  • カーネルパラメータの設定
  • ファイアウォールの設定
  • RTX1200の設定

これらを、Ubuntuの流儀(systemd)に従って実施する。

トンネルデバイスの作成とルーティング設定

/etc/systemd/network 以下に.netdevファイルを置くとデバイスが作られ、.networkファイルでネットワークの設定ができる。

/etc/systemd/network/01-tun0.netdev

1

2

3

4

5

6

7

8

9

[NetDev]

Name=tun0

Kind=ip6tnl

[Tunnel]

Mode=ipip6

Local=【VPSのIPv6アドレス】

Remote=【ルータのIPv6アドレス】

EncapsulationLimit=none

最後の1行(EncapsulationLimit=none)が非常に重要(後述)

/etc/systemd/network/01-tun0.network

1

2

3

4

5

6

[Match]

Name=tun0

[Route]

Destination=【自宅側のネットワーク】

Scope=link

/etc/systemd/network/00-ens3.network

1

2

3

4

5

6

7

8

9

10

11

12

[Match]

Name=ens3

[Network]

Address=【VPSのIPv4アドレス】

Gateway=【VPSのIPv4ゲートウェイ】

DNS=【VPSのIPv4 DNSサーバ(1)】

DNS=【VPSのIPv4 DNSサーバ(2)】

Address=【VPSのIPv6アドレス】

Gateway=【VPSのIPv6ゲートウェイ】

DNS=【VPSのIPv6 DNSサーバ】

Tunnel=tun0

本当は「Tunnel=tun0」だけで済ませたかったのだが、その場合なぜかnetplanの設定が無視される(Name=ens3にマッチしたからこちらが優先された?)ので、やむなくIPv4/v6アドレス等を記載している。

カーネルパラメータの設定

IPv4パケットのフォワーディングを有効にし、各インタフェースのrp filterを無効にする。
rp filterが有効な場合、インターフェースと無関係なアドレス情報を持ったパケットが破棄されてしまうらしい。
今回tun0にはIPアドレスを振っていないため、パケットを破棄されないために無効にする。
ens3でも無効にしているがこれは元文献がそうしていたので同じようにしている。
rp filterを有効にした場合に本当に破棄されてしまうのか、検証はしていない。

/etc/sysctl.d/60-ipip6-tunnel.conf

1

2

3

4

net.ipv4.conf.all.forwarding=1

net.ipv4.conf.ens3.rp_filter=0

net.ipv4.conf.tun0.rp_filter=0

net.nf_conntrack_max=65535

ファイアウォールの設定

Ipv4の設定

1

2

3

4

5

6

7

8

9

10

11

iptables -F

iptables -P INPUT DROP

iptables -P FORWARD DROP

iptables -P OUTPUT ACCEPT

iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

iptables -A INPUT -p icmp -j ACCEPT

iptables -A INPUT -i lo -j ACCEPT

iptables -A INPUT -i tun0 -j ACCEPT

iptables -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -j ACCEPT

iptables -A FORWARD -i tun0 -j ACCEPT

iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT

IPv6の設定

1

2

3

4

5

6

7

8

ip6tables -F

ip6tables -P INPUT DROP

ip6tables -P FORWARD DROP

ip6tables -P OUTPUT ACCEPT

ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

ip6tables -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -j ACCEPT

ip6tables -A INPUT -s 【自宅のIPv6ネットワーク】 -j ACCEPT

ip6tables -A INPUT -p ipv6-icmp -j ACCEPT

IPv6ではICMPv6が重要な役割を占めていて、周辺情報等がICMPv6でやり取りされているので通してやらないと通信できなくなるので注意。
22番ポートは自宅以外からもアクセスすることを想定して公開。
これらを設定した後、iptables-persistentパッケージを導入して設定を固定。
iptables-persistentパッケージインストール後にファイアウォールの設定を変えてそれを反映させたい場合には以下のコマンドで。

1

dpkg-reconfigure iptables-persistent

このパッケージ、やってることはip6?tables-saveとip6?tables-restoreなのでそれでできる人はそれでも構わない。

RTX1200の設定

VPSとの間にトンネル作ってIPv4パケットはそちらに流すように設定するだけ

1

2

3

4

5

6

tunnel select 1

 tunnel encapsulation ipip

 tunnel endpoint address 【VPSのIPv6アドレス】

 ip tunnel tcp mss limit auto

 tunnel enable 1

ip route default gateway tunnel 1

EncapsulationLimitの罠

当初、/etc/sytemd/network/01-tun0.netdevの最後の「EncapsulationLimit=none」の行は入れてなかった。
だがその状態だとトンネル経由の通信がうまくいかない。パケットは出ていくが戻りがない。
VPS側で見てみるとパケットは投げ返しているようだったので、ルータで何かが起きていると思ってルータのログを見てみた。

1

2

3

4

[IPv6] received malformed option from 【VPSのIPv6アドレス】

[IPv6] received malformed option from 【VPSのIPv6アドレス】

[IPv6] received malformed option from 【VPSのIPv6アドレス】

...

パケットが変だと言われている。VPSでキャプチャしてみた結果がこちら。
8.8.8.8に対するpingとその応答。192.168.100.4は自宅マシン。

表示されているのはルータで破棄される定めの応答パケット。
IPv6ヘッダ内のNext HeaderがDestination Options for IPv6 (60)となっており、Tunnel Encapsulation Limitの情報が含まれていることがわかる。
そしてその中のNext HeaderはIPIP (4)で、IPヘッダが続くことを示している。
ping送信のパケットはDestination Options for IPv6 (60)はなく、IPv6ヘッダ内のNext HeaderはIPIP (4)となっていた。
IPパケットのカプセル化は入れ子が可能であり、Tunnel Encapsulation Limitはその上限を表している(参考:RFC2473)。
上限を示すためにこのオプションがあるのだが、残念なことにRTX1200ではこれをmalformedとして扱ってしまうようだ。

調べてみると、このオプションがついているパケットを検出するIPSシグネチャがあるようだ。

デフォルトではオフなようだが、RFC2473で規定されている通常のパケットを検出するとかお前は何を言っているんだ(RTX1200もだが)。

閑話休題。
LinuxのトンネルデバイスではTunnel Encapsulation Limitのデフォルト値がRFCで推奨されている4となっている。
これをnoneにすることで受信したパケットに前述のオプションがついていない場合は戻りパケットにもつけないようにできる。
この設定をすることにより、トンネルが正常に機能するようになった。

おわりに

PPPoEを経由しないと速くて快適である。みんなも早くこっちにおいでよ。

おわび

次回に予定していたIPsec編は、IPsecでのトンネルがうまく作れないため無期限延期となります。