512バイトを超える DNSパケット
glibc の脆弱性 CVE-2015-7547 でも話題になった 512バイトを超える DNS パケットについてのメモ。
DNS では、TCP が使われたり、512 バイト超えるデータが扱われることは知っていたが、詳しい仕組みなど知らなかったので、備忘録のためにまとめておく。
そもそもなぜ 512 バイト?
調べてみると、
インターネットで使われている IP(IPv4)の仕様では 一度に受信可能なデータグラム(ヘッダーを含むパケッ ト)として、 576 バイトを保証しなければならないと定められています。この値は、64バイトのヘッダーと 512バイトの データブロックを格納可能な大きさとして選択されたものです
refs: https://jprs.jp/related-info/guide/008.pdf
とのこと。
インターネットで使われている IP の仕様では、かならず「1パケットで 512バイトのデータを送れる」ことが保証されるので、DNS では通信コストをさげるため「1パケットで送受信可能なようにデータサイズを 512バイトに制限」したらしい。
(DNS の場合、IPヘッダ+UDPヘッダ+データで、540(20+8+512)バイト使われている)
ちなみに、root DNS が 13 個しかないのも、14 個だと 512バイトを超えてしまうから。
$ dig . NS
; <<>> DiG 9.8.3-P1 <<>> . NS
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22440
;; flags: qr rd ra; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 13
:
;; ANSWER SECTION:
. 80275 IN NS k.root-servers.net.
. 80275 IN NS l.root-servers.net.
. 80275 IN NS m.root-servers.net.
. 80275 IN NS a.root-servers.net.
. 80275 IN NS b.root-servers.net.
. 80275 IN NS c.root-servers.net.
. 80275 IN NS d.root-servers.net.
. 80275 IN NS e.root-servers.net.
. 80275 IN NS f.root-servers.net.
. 80275 IN NS g.root-servers.net.
. 80275 IN NS h.root-servers.net.
. 80275 IN NS i.root-servers.net.
. 80275 IN NS j.root-servers.net.
;; ADDITIONAL SECTION:
:
;; MSG SIZE rcvd: 496
ANSWER: 13
MSG SIZE rcvd: 496
TCPフォールバック
初期の DNSプロトコルでは 512バイトを超えた応答を受け取るためには、TCP で再帰問い合わせ(TCPフォールバック)を 行う必要があった。
サーバは、UDP の問い合わせに対し応答が 512バイトを超える場合、応答するレコードを 512バイト以下に切り詰めた上で、切り詰められたこと示すビット(TC)を立て、UDP の応答を返す。
クライアント側は、その TCビットを確認すると再度、同じ問い合わせをTCPで行い、全てのレコードがつまった応答を受け取る。
(このあたりまでは、RFC 1034/1035/1123/5966 の話)
DNSキャッシュポイズニング対策
1990年台に問題提起された DNSキャッシュポイズニング対応として、DNSSECの標準化が開始された。
しかし、DNSSEC では鍵や署名をデータに乗せるため、512バイトに収まらず TCPフォールバックが発生することでレイテンシの問題が顕在化。
その対策として、UDP で 512バイト以上のデータを応答できる仕組みが検討され、1999年に RFC 26710 として EDNS0 が標準化された。この RFC では、DNSを DNSSEC や IPv6 に対応させる場合、EDNS0 への対応が必須となっている。
EDNS0 (Extension Mechanisms for DNS)
EDNS0 を使用可能なクライアントは、DNS要求の addtional セクションに OPTレコードを記載し、EDNS0 に対応していることをサーバにしめす。サーバ側が EDNS0 に対応していれば正常な応答が返り、対応していなければ、エラーになるか、無視されて TCPフォールバックすることになる。
動作検証
dig を使用して、TCPフォールバックと EDNS0 のパケットを確認してみる。
【検証1】TCPフォールバック
dig で 512バイトを超える応答を受ける場合、標準でTCPフォールバックが行われる。
$ dig @192.36.144.107 se. any
;; Truncated, retrying in TCP mode.
:
;; Query time: 406 msec
;; SERVER: 192.36.144.107#53(192.36.144.107)
;; WHEN: Sat Feb 20 23:30:44 2016
;; MSG SIZE rcvd: 2052
dig を実行すると始めに Truncated, retrying in TCP mode.
の出力があり、TCPフォールバックが実行されたことがわかる。その際の tcpdump。
-
問い合わせ時のパケット。
No.1
: UDP で問い合わせを行った後、No.6
: TCPで再帰問い合わせてしている。 -
UDP の応答。
No2
: No1のレスポンス TCビットが立てられ、応答の数(Answer RRs)は切り詰められて11
となっている。 -
TCP の応答。
No9
: No6のレスポンス TCPフォールバック時の応答の数は UDP時の11
から増えて19
。
【検証2】EDNS0
dig では、+edns=0
オプションをつけると EDNS0 が利用される。(+busize=
や +dnssec
をつけた際も EDNS0 が利用される)
$ dig @192.36.144.107 se. any +edns=0
:
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
:
;; Query time: 312 msec
;; SERVER: 192.36.144.107#53(192.36.144.107)
;; WHEN: Mon Feb 22 20:49:14 2016
;; MSG SIZE rcvd: 2063
-
問い合わせ時の一連のパケット。
UDP だけで処理が完結している。
-
UDP の問い合わせ
No.15
: UDP による問い合わせ addtional section に OPTレコードがある -
UDP の応答
No.20
: UDP による応答 TCPフォールバックせずに UDP で全ての応答が返ってきている
[補足] DNS のメッセージフォーマット
DNS のメッセージフォーマットは、5つのセクションに分かれており、Header
のみが必須。要求と応答ともに同じフォーマットしようされる。
Format
+---------------------+
| Header |
+---------------------+
| Question | the question for the name server
+---------------------+
| Answer | RRs answering the question
+---------------------+
| Authority | RRs pointing toward an authority
+---------------------+
| Additional | RRs holding additional information
+---------------------+
Header は 6つに分かれていて、以下のような感じ。
The header contains the following fields:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ID |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QDCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ANCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| NSCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ARCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
ID(16)
-
フラグ(16)
QR
(1): 問い合わせ/応答- 0: 問い合わせ
- 1: 応答
OPcode
(4): オペレーションコード- 0: 問い合わせ
- 1: 逆問い合わせ
- 2: サーバ状態要求
AA
(1): オーソリティ応答- 0: 反復の結果の応答
- 1: そのネームサーバからの応答
TC
(1): 切り捨て- 0: データサイズ512バイト以下
- 1: 512バイト超
RD
(1): 再帰要望- 0: 再帰問い合わせをサーバに要求しない
- 1: 再帰問い合わせをサーバに要求
RA
(1): 再帰有効- 0: 再帰問い合わせ不可能
- 1: 再帰問い合わせ可能
Z
(3): (予約)- 0: 未使用。すべて 0
RCODE
(4): 戻りコード- 0: エラーなし
- 1: フォーマットエラー
- 2: サーバエラー
- 3: ドメインが存在しない
- 4: 未実装
- 5: 拒否
-
質問の数(16)
-
応答の数(16)
-
オーソリティの数(16)
-
追加情報の数(16)