「.se と DNSSECと IPv6」で a.ns.se が反応し無かった理由が分かった気がする。([gj].ns.se の問題はまだ考えてない)
■ トポロジa.ns.se <> Internet <A> X <B> Y
- <A> の MTU は 1500 bytes
- <B> の MTU は 1445 bytes (tinc による tunnel を使用)
X と Y はホスト。
■ 問題- あるホスト(上図 X)で実行した dig @2a01:3f0:0:301::53 se. ns +dnssec に反応がない
- ただし X のクエリをインタフェース A で観測してると、a.ns.se からフラグメントされたパケットの断片が届く (フラグメントの2片目のみ) (*)
- Y で実行すると問題ない
- a.ns.se からの返答のサイズは 2706 bytes (Y での結果より)
(*) こんなの
IP6 (hlim 56, next-header Fragment (44) payload length: 1274) 2a01:3f0:0:301::53 > 2001:xxxx:xxxx::x: frag (0x00615ecc:1448|1266)
■ MTU の観測まず初めに、どうせ MTU の問題だろうと当たりをつけて、X から ping6 で MTU を推測してみる
> ping6 -s 1432 -m 2a01:3f0:0:301::53
PING6(1480=40+8+1432 bytes) 2001:xxxx:xxxx::xxxx --> 2a01:3f0:0:301::53
1440 bytes from 2a01:3f0:0:301::53, icmp_seq=0 hlim=56 time=105.266 ms
> ping6 -s 1433 -m 2a01:3f0:0:301::53
PING6(1481=40+8+1433 bytes) 2001:xxxx:xxxx::xxxx --> 2a01:3f0:0:301::53
^C
(注:ICMPv6 Packet Too Big も返ってこない)
このことから、
- X から a.ns.se までの Path MTU は 1480 bytes で、多分 IPv6-over-IPv4 トンネルを使っている (IPv4 ヘッダ 20 byte を足すと、ちょうど Ethernet の MTU 1500 bytes になるから)
- トンネルを終端してるルータが Packet Too Big を返さないらしい
ことが分かった。
■ DNS の観測次に実際の DNS のクエリへの応答を、bufsize を変化させながら観測した。
>dig @2a01:3f0:0:301::53 se. ns +bufsize=1571 +dnssec
[snip]
;; Query time: 108 msec
;; SERVER: 2a01:3f0:0:301::53#53(2a01:3f0:0:301::53)
;; WHEN: Thu Apr 15 21:33:51 2010
;; MSG SIZE rcvd: 1410
>dig @2a01:3f0:0:301::53 se. ns +bufsize=1572 +dnssec
; <<>> DiG 9.6.1-P2 <<>> @2a01:3f0:0:301::53 se. ns +bufsize=1572 +dnssec
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached
>dig @2a01:3f0:0:301::53 se. ns +bufsize=1409 +dnssec
[snip]
;; Query time: 105 msec
;; SERVER: 2a01:3f0:0:301::53#53(2a01:3f0:0:301::53)
;; WHEN: Thu Apr 15 21:35:31 2010
;; MSG SIZE rcvd: 1248
>dig @2a01:3f0:0:301::53 se. ns +bufsize=1247 +dnssec
[snip]
;; Query time: 108 msec
;; SERVER: 2a01:3f0:0:301::53#53(2a01:3f0:0:301::53)
;; WHEN: Thu Apr 15 21:36:23 2010
;; MSG SIZE rcvd: 1086
>
このように 1571 bytes を境に応答がなくなってる。
手始めに、メッセージサイズ(MSG SIZE)が 1410 bytes なので試しに bufsize を 1409 bytes にしてクエリを送ったところ、メッセージサイズが 1410 bytes から 1248 bytes になった。さらに bufsize を 1247 bytes にしたらメッセージサイズは 1086 bytes となった。このことから、おそらく(bufsize に余裕があれば)メッセージサイズは 162 bytes ずつ増加することが予想される。
そこで 1410 + 162 を計算すると 1572 となる。つまり、bufsize が 1572 bytes より大きいとメッセージサイズも 1572 bytes になることが予想される。(というか、Y から実行したら確かにそうなった。)
■ 仮定上記 DNS の観測から、(応答)メッセージサイズが 1410 bytes までは問題ないが、1572 bytes になると応答がなくなることが確認できた。これは、1410 bytes まではフラグメントが必要なかったが 1572 bytes ではフラグメントが必要になるからだと推測する。ここで、
a.ns.se は MTU 1500 (Ethernet の標準の MTU)でパケットを送信しようとしていると仮定し、これを検証してみたい。
MTU が 1500 bytes の時、IPv6ペイロードに使えるのは、IPv6ヘッダを除いて、
1500 - 40 = 1460
フラグメントする場合、8 bytes 境界で分割した上で 8 bytes のフラグメントヘッダを付加するので、実際にペイロードに使えるのは、
1460 -> 1456 - 8 = 1448
このうち 8 bytes は UDP ヘッダに使うので、DNSのクエリ応答のデータのうち、実際に1パケット目に詰め込めるのは、
1448 - 8 = 1440
応答のメッセージサイズが 2706 bytes であることは分かっているので残りは次の通り。
2706 - 1440 = 1266
1226 bytes のデータをフラグメントされた2パケット目で送ると、IPv6ペイロードのサイズ(IPv6ヘッダは除き、フラグメントヘッダは含む)はこのようになる。
1266 + 8 = 1274
これは、上記 (*) の観測結果とも一致する。つまり、a.ns.se は Path MTU を 1500 bytes と仮定してパケットを送出しているという仮定は正しいらしい。
■ 結論仮定の中で計算したように、a.ns.se の1パケット目は 1496 bytes (= 40 + 8 + 8 + 1440) となるが、これは MTU の観測でみたように Path MTU である 1480 bytes のトンネルを通過できない。本来なら、そこで ICMPv6 の Packet Too Big が返り、a.ns.se がパケットを小さくして再送することになるのだが、恐らくその ICMPv6 メッセージが送られていないのだろう。したがって、a.ns.se は正しくパケットが配送されたとみなし、フラグメントの2片目を送り通信を終了している。
というわけで諸悪の根源は、
- Packet Too Big を返さないルータ
- トンネル技術そのもの
といったところだろうか。(しかし、この種の問題が繰り返し発生しているのを見ると、IPv6 ではルータはフラグメントしない、とした判断は果たして正しかったのだろうか?という疑問が浮かんでくる)
さて、誰か経由で、.se の人にコンタクトしてもらうべきか?(追記:と思ったけど、他のASからテストしたところ、そっちからは問題なかった。とすると、.se にあまり近くないところにトンネルがあって、そこが問題になってるっぽいな。とすると、.se の人にコンタクトしても意味ないかも。)
■ おまけY も実はそのままでは通信できない。一度、+bufsize=1571 (1410 から 1571 の値)でクエリを出す必要がある。
理由:
+bufsize=1571 でクエリを送ると、a.ns.se は 40+8+1410 = 1458 bytes のパケットを送ってくる。しかし、<B> の MTU が 1445 bytes なので、X は「MTU は 1445 だよ」とした上で Packet Too Big を返送する。
IP6 (hlim 254, next-header ICMPv6 (58) payload length: 536) 2001:xxxx:xxxx::xxxx > 2a01:3f0:0:301::53: ICMP6, packet too big, length 536, mtu 1445
それにしたがい a.ns.se は、
1パケット目 40 + 8 + 8 + 1384 = 1440
2パケット目 40 + 8 + 1322 = 1370
というパケットを再送し、正常に通信が終わる。
なお、Packet Too Big が a.ns.se まで到達してることから、ICMPv6 が無闇にフィルタされてるわけではなさそうなことが分かる。また、その後、+bufsize を 1572 より大きくしても、a.ns.se は Y への Path MTU を(一定期間は)覚えているので、きちんとフラグメントされてパケットが送られてくる。
想像:
ちなみに、Packet Too Big が a.ns.se に送られてない理由の一つとして uRPF が考えられる。実は別の場所でも発生していたのだが、仕掛けはこうだ。
- ルータは Packet Too Big を返す
- ただし、その src address に routable でないアドレスがついてしまった
- 途中のルータが IPv6 uRPF を動かしていて Packet Too Big を叩き落とす
ざっとこんな感じである。ルーティングを行なう上でルータのインタフェースのアドレスは必ずしも routable である必要はないので、割と見落とされやすい上に、解決に時間がかかるたぐいの問題である。