Path MTU Blackhole で阿部寛様のご尊顔が表示されない！


こちらは、[IPv6 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/ipv6)の23日目の記事です。

## はじめに

[秒でサイトのIPv6対応ができる！サーバサイド Open NAT64を公開した話](https://qiita.com/__kaname__/items/0c77fb3b5fb01a796a4b)

こちらで、NAT64/DNS64が公開されているので、手元で色々試していたところ、**阿部寛様のご尊顔が表示されない！**問題が発生しました。

## Takeaways
この記事によって、以下の3つがわかります。

 * NAT64/DNS64の設定の仕方
 * IPv6 Path MTU Discovery のメカニズム
 * Path MTU Blackhole問題の解決方法

## 何が起きたのか？

阿部寛のホームページといえば、業界では「シンプル＆軽量で爆速表示されることに定評」があります[^1]。
とにかく、一度、見てください。

[^1]: https://nlab.itmedia.co.jp/nl/articles/1903/31/news027.html

**阿部寛のホームページ:** http://abehiroshi.la.coocan.jp/

シングルドメインで広告もなく、検証にはうってつけ。ということで、NAT64/DNS64の検証に使っていました。
すると、とある条件下で阿部寛さんの顔写真が表示されないではありませんか！なぜベストを尽くさないのか！


<img width="1439" alt="Screen Shot 2019-12-23 at 7.36.20 copy.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44674/54bd9fca-30f2-e74d-bdbe-e3ab9f3cf1fa.png">

## NAT64/DNS64の設定

状況を再現する前に、NAT64/DNS64の設定について説明します。
Open NAT64を利用して、手元のPCにNAT64/DNS64を設定します。

やり方は簡単で、端末にIPv6アドレスが付いている状況で、DNSサーバを、`2402:c800:fe01:a10::53`に変更するだけです。

このDNS64サーバは、Aレコードしかない場合に、NAT64用IPv6アドレスを返します。すでにAAAAレコードがある場合はそちらを優先して返します。
これで、IPv4のサイトに対してもIPv6で通信することになりました。

確認サイトにアクセスして、確認してみましょう。
https://test-ipv6.com/


iOS Appにおいて[「IPv6 DNS64/NAT64 ネットワークをサポートする」](https://developer.apple.com/jp/support/ipv6/)ことが必須になっていますが、この方法は、その検証用ネットワークとしても使えます。


## IPv6 Path MTU Discovery のメカニズム

IPv4とIPv6では非常に重要な仕様違いがあります。
IPv4ではMTUを超えるパケットについて経路の途中にあるルータが分割をすることがありますが、 IPv6では経路の途中では分割をしない仕様となっています[^2]。

[^2]: https://www.nic.ad.jp/ja/basics/terms/mtu.html

> MTU(Maximum Transmission Unit): 各メディアにおける最大パケット長。イーサネットにおいては 1500バイト


Path MTU Discoveryは、送信側が送信可能なMTUサイズを確認するメカニズムです。

### Path MTU Discovery 正常時

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44674/255110a0-3f0d-1903-3263-808259e8677e.png)

**阿部寛のホームページは、すでにIPv6に対応しています**。
そのためDNS64を設定していたとしても、NAT64を経由せず、IPv6 Nativeで通信します。
なので、ここから先は通常のIPv6サイトを閲覧した時の挙動です。

通信開始時、端末はTCP SYNにおいて、MSSが1440バイトであると通知します(無線LAN区間はMTU 1500バイト)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44674/e7e92162-6efe-bc65-3dc7-24369b989846.png)

> MSS(Maximum Segment Size): TCPの最大ペイロードサイズ。MTUから、TCPヘッダ・IPヘッダのサイズを除いたもの。TCPヘッダは20バイト。IPv6ヘッダは40バイト。

サーバ側はMSS=1440を受けて、送りたいデータを1440バイトごとに分割して、TCPヘッダ(20バイト)とIPv6ヘッダ(40バイト)を追加して、1500バイトでパケット化(packetize)して、送り出します。

しかし、私の自宅は、(今となっては珍しい)PPPoE IPv6でIPv6化しておりました。
PPPoE IPv6接続では、フレッツ区間のMTUが46バイト小さい1454バイトになっています[^3]。
そのため1454バイトのパケットまでしか通りません。

[^3]: https://www.infraexpert.com/info/6adsl.htm


**IPv4ではルータがパケットを分割してくれますが、IPv6ではパケットを破棄して、Packet Too BigメッセージをICMPv6で送り返します**。
Packet Too Bigメッセージに格納された適切なMTU値(=1454byte)を参照し、サーバ側はパケットを再構成してリトライします。
この一連のメカニズムをPath MTU Discoveryと言います。


### NG時(NAT64経由)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44674/24aae3ba-f497-f8e2-45f2-df33c51d8c76.png)

さて、問題はNAT64の検証のために、阿部寛のホームページに対して、NAT64経由で通信しようとしたことに起こりました。
強引に、`/etc/hosts`を書き換えて、NAT64用IPv6アドレスに名前解決するようにします。
これにより、NAT64経由で阿部寛のホームページのIPv4アドレスに対して通信することになります。

先ほどと同様にサーバ側はMSS=1440を受けて、送りたいデータを1440バイトごとに分割します。
今回はIPv4なので、TCPヘッダ(20バイト)とIPv4ヘッダ(20バイト)を追加して、1480バイトでパケット化します。

NAT64装置で、IPv4パケットからIPv6パケットに変換します。IPv4ヘッダ 20バイトに対して、IPv6ヘッダ 40バイトなので、パケットが20バイト大きくなります。
しかし、今回はそれは問題ではありません。IPv6空間からTCP SYNを送っているのでIPv6ヘッダ分はすでにケアされているからです。パケットは1500バイトになります。

PPPoE IPv6区間の入口でPacket Too Big(ICMPv6)を送り返した時が問題です。IPv4サーバはICMPv6を解釈できないは当然ですし、IPv4にはMTUを通知するメカニズムがありません。
結果、Path MTU DiscoveryはFailして、サーバ側は同じパケットサイズで再送を繰り返すのみです。

なんらかの理由でPacket Too Big(ICMPv6)メッセージが送信元に返らず、通信不能になることをPath MTU Blackholeといいます。
ICMPv6をフィルタしていたり、ロードバランサを使ったりしていると同様の事象が発生します。


### 解決策(mss-clamping)

というわけで、事象の原因はPath MTU Blackholeでした[^4]。

[^4]: この事象は、実験の条件下で発生させたものです。阿部寛のホームページはIPv4/IPv6対応しており、通常アクセスする際には全く問題ありません。

阿部寛のホームページの構成ですが、top.htmなどのいくつかのファイルが、なんと1パケットに収まるサイズなんですよね。なので、フレームだけは表示されたのですが、ファイルサイズの大きいjpg画像がNGでした。
Path MTU Blackholeに引っかかると、サイトの一部コンテンツだけ表示される、という現象になります。

さて、解決策ですが、NAT64装置でTCP SYNパケットの中のMSSの値を1394バイトに上書きする設定にしました。
これはmss-clampingと呼ばれる手法で、小手先の解決策に思われるかもしれませんが、いろんなところで使われておりうまく動いてしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44674/5145076f-afe2-70e8-2fdd-c6c3fdf15a85.png)

じゃあ、UDPはどうするんだ、ということで実験を通して解決策を探していきたいと思います。
Path MTU Discoveryの問題は根が深く、IETFでも未だに議論されています。

ん、QUIC? 

> QUIC endpoints SHOULD NOT send IP packets larger than 1280 bytes. 

らしいですぜ[^5]。

[^5]: https://tools.ietf.org/html/draft-ietf-quic-transport-24