kawasin73のブログ

技術記事とかいろんなことをかくブログです

UDP の Length はなんのためにあるのか?

1 バイトの無駄も許さない。どうも、かわしんです。1024 回繰り返すと 1 KB の無駄になります。

先週 TCP/IP スタックを自作した 1 のですが、その講義中ずっと気になっていたことがあったのでそれを深掘りします。

TCP / IP スタックを自作した人なら誰でも感じると思うのですが、TCP の後に UDP を見ると、なぜか UDP のヘッダには Length というフィールドがあることに気づきます。

                  0      7 8     15 16    23 24    31
                 +--------+--------+--------+--------+
                 |     Source      |   Destination   |
                 |      Port       |      Port       |
                 +--------+--------+--------+--------+
                 |                 |                 |
                 |     Length      |    Checksum     |
                 +--------+--------+--------+--------+
                 |
                 |          data octets ...
                 +---------------- ...

                      User Datagram Header Format

https://tools.ietf.org/html/rfc768

多分、UDP を見ただけだとふーんって感じで流してしまうと思うのですが、TCP を実装した後の冴えた頭ではどうしても引っかかります。

TCP のヘッダには長さを表すフィールドがないのです。

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Source Port          |       Destination Port        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        Sequence Number                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Acknowledgment Number                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Data |           |U|A|P|R|S|F|                               |
   | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
   |       |           |G|K|H|T|N|N|                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Checksum            |         Urgent Pointer        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             data                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                            TCP Header Format

          Note that one tick mark represents one bit position.

                               Figure 3.

https://tools.ietf.org/html/rfc793#section-3.1

では、TCP はどのようにデータの長さを知るのかというと IP ヘッダに含まれる Total Length というフィールド 2 によって全体の長さを知り、そこから IP ヘッダ自体の長さを引くことで TCP セグメント全体の長さを知ります。

だったら思うわけですね、UDP の Length フィールドいらないじゃん って。TCP みたいに IP ヘッダの長さから UDP セグメントの長さを知ればいいじゃんって。

UDP ヘッダは 8 バイト固定長なので IP の Total Length から IP ヘッダの長さと UDP ヘッダの長さを引けば UDPペイロードの長さがわかるはずです。UDPLength フィールドをなくすことで 1 セグメントあたり 2 バイトも 多くのデータを送ることができます。

これがすごく気になったので、なぜ UDP ヘッダに Length フィールドがあるのか、その理由を求めてネットの海をさまよいました。

なぜ UDP には Length フィールドがあるのか

とりあえず RFC を読んでみます。UDPRFCRFC 768 - User Datagram Protocol です。わずか 3 ページと短いです。

さて、この RFC では Length については以下のように記述されており、ここからはなぜ Length フィールドができたのかを推し量ることはできません。

Length is the length in octets of this user datagram including this header and the data. (This means the minimum value of the length is eight.)

そこで、なぜ UDP に Length フィールドがあるのかをググってみました。やはり、いろんな人が同じ疑問を持っていたらしく、様々なところで議論されていました。

が、結論から言えば、なぜそうなったのかはわかりませんでした 。どれも推測の域を出ることができず、確固たるソースをもってこの疑問に答えているものは1つもありませんでした。

ですが、いい線をいってるなと思う説がいくつかありましたのでここに紹介します。

ヘッダサイズを 32 ビットの倍数に揃えるために、余った 16 ビットを Length フィールドに割り当てた

32 ビットにアラインされていた方がハードウェアとして扱いやすいために 16 ビット余った領域にいい感じな Length を割り当てられたという説です。確かに理由の1つではありそうです。

これはどちらかというと UDPLength フィールドは冗長であることを認める主張です。なくてもいいので。

UDP Lite プロトコルでは Length フィールドは上書きされている。だから Length フィールドは必要ない

UDP を発展させたプロトコルとして UDP Lite というプロトコルがあるそうです。これは、UDP が領域の一部に間違いがあった場合にセグメント全体を破棄してしまうのに対して、チェックサムを計算する領域を指定することでデータの一部に間違いがあっても有効なセグメントとするプロトコルです。

UDPLength フィールドは Checksum Coverage になり、ヘッダを含める UDP Lite セグメントの先頭の範囲だけチェックサムを計算するようになります。おそらく UDP のヘッダやデータに含まれるアプリケーションプロトコルのヘッダ領域だけは正しいことを確認して、データの部分はエラー訂正を行うことを目論んでいるのだと思いました。

確かに、Length フィールドは上書きされているので冗長で必要なかったということがわかります。

UDP-Lite - Wikipedia

UDPLength と IP からの Length が食い違うことでデータの Validation ができる

それはチェックサムでやるのではないでしょうか・・・。IP からの Length を信用しない場合は、TCP も信用できなくなってしまいます。

ですが、データの信頼性を向上させるための Validation の1つにはなります。

IP に依存せず UDP だけで完結するべきだから Length の情報もヘッダに含めるべきだ

この流派には 2 つあるように感じました。

  • UDP は IP に依存せずどんなプロトコルの上でも動けるように IP の Length には依存するべきでない
  • UDPTCP と違ってデータグラムである。カーネル内では1メッセージごとバッファリングされるから Length の情報は必要になる

まず、前者ですが UDP の pseudo header は IP ヘッダ由来の Length に依存するため、この指摘は半分妥当ではありません。もしチェックサムを無効にする場合は確かにその通りだと思います。

UDPRFC の「IP Interface」の項を読むと以下のように書かれており、UDP は IP の Length は必須とはしていないようです。つまり、pseudo header の LengthUDP Header から計算できるということなのでしょうか。

The UDP module must be able to determine the source and destination internet addresses and the protocol field from the internet header.

一方で TCPRFC の「TCP/Lower-Level Interface」では、下層として IP と同等の機能を提供すればどのプロトコルでも許容するとしています。その IP として要求する機能については以下のように記述しています。

Any lower level protocol will have to provide the source address,
destination address, and protocol fields, and some way to determine
the "TCP length", both to provide the functional equivlent service
of IP and to be used in the TCP checksum.

https://tools.ietf.org/html/rfc793#page-51

ここでは、明確に TCP length を要求しているので、UDP では IP レイヤーからの length は必須としていないと考えることができます。

次に後者のカーネル内での扱われ方に着目した説ですが、これも一理あります。

TCP はストリームなので、1セグメント1セグメントの情報はストリーム(Receive Buffer)にデータがコピーされた時点で破棄されます。

一方で UDP はデータグラムなので、アプリケーションがソケットから読むときに1セグメントごとを読み出します。そのため 1 セグメントごとの長さなどの情報は、読み出されるまで本質的にカーネル内に保存される必要があります。

この考え方は一理ありますが、カーネル側で新しいメモリ領域を確保してそこで長さを管理すればいいのでそこまで強い理由にはならないのかなと思いました。

まとめ

僕の結論としては、UDP の下層を IP に限定する場合は UDPLength フィールドは不必要です。 32 ビットアラインのための埋め合わせの意味合いが強いと思います。

UDP を IP に限定しない汎用的なプロトコルと考える場合は、下層が Length を提供する機能を持たない可能性があるため、Length フィールドは必要になると言えます。

だいたいこんな感じでした。この辺りに知見のある方は僕のツイッターアカウント @kawasin73 まで、ぜひ教えていただければと思います。

参考文献

僕が読んだ記事は以下の通りです。

stackoverflow.com

stackoverflow.com

これは、UDP の Length というよりは、pseudo header の Length との重複のことをいっているみたいでした。

kplug-list.kernel-panic.narkive.com