【入門編】 UDPチェックサムの計算方法とエラー検出の限界 – ネットワーク基礎とWebセキュリティ実践ガイド

こんにちは!インフラの現場やネットワークの裏側を覗き見するのが大好きな、技術メディアの主筆ライターです。

皆さんは、普段何気なく使っているインターネットの通信、例えばオンラインゲームや動画のライブ配信、あるいはDNSの名前解決などで使われる「UDP(User Datagram Protocol)」というプロトコルをご存知でしょうか?
「TCPと違って、届いたかどうか気にせずとにかくパケットを投げまくるヤンチャなやつ」というイメージを持っている方も多いかもしれませんね。

確かにUDPは、TCPのように「今から通信を始めます!」「今のパケット届いた?」といった事前の握手や確認を行わないため、非常にスピード感があります。しかし、「じゃあ、途中でデータが壊れていてもそのまま知らんぷりして通しちゃうの?」というと、実はそうでもありません。

UDPには、最低限の「これ、途中で文字化けしてない?」をチェックする仕組み、それが「UDPチェックサム」です。

今回は、このUDPチェックサムが一体どんな仕組みで計算され、そしてどんな「限界」を抱えているのかを、身近な例えを交えながら一歩ずつ優しく紐解いていきたいと思います。難しい専門用語に圧倒されそうになっても大丈夫です。「一歩ずつ理解していきましょう!」

—

1. 郵便配達で例えるUDPとチェックサムの役割

まずは、パケットの旅を私たちが普段使っている「郵便配達」に例えて考えてみましょう。

TCPが「本人限定受取郵便」や「書留」だとすれば、UDPは宛先を書いた手紙をそのままポストにポンと放り込む「普通郵便」です。配達員さんは、手紙がちゃんと相手の家に届いたかどうかを追跡しませんし、受け取った相手が中身を読んでどう思ったかも気にしません。

しかし、郵便局の仕分けスタッフや配達員さんが、最低限気をつけていることがあります。それは「封筒の宛先が滲んで読めなくなっていないか」「雨に濡れて中身の紙がドロドロになって文字が消えていないか」という点です。

もし、途中でインクが盛大に滲んでしまい、宛先の住所が「東京都〇〇区…」から「東〇都…?」になっていたら、配達員さんは困ってしまいますよね。そこで、手紙の端っこに「この手紙全体の文字のインクの量を足し算した合計値(=チェックサム)」がこっそり添えてあったとしたらどうでしょう?

受け取った人は、届いた手紙の文字を自分で改めて足し算してみて、端っこに書いてある合計値と一致するかどうかを確認できます。もし合計値が合わなければ、「おっと、途中で雨に濡れて文字が消えたり、書き換わったりしたな!」と気づくことができるわけです。

この「配達途中のデータ破損を見つけるための計算」こそが、ネットワークの世界におけるチェックサムの正体なのです。

—

2. UDPチェックサムは、どうやって計算されているの?

それでは、ネットワークの世界では具体的にどのようにこのチェックサムを計算しているのでしょうか?

実は、UDPのチェックサムは、UDPのヘッダーやデータ本体だけでなく、なんと「IPアドレス(送信元と宛先)」といったネットワーク層の情報まで巻き込んで計算されています。この計算のために一時的に作り出される架空のヘッダーのことを、ネットワークの世界では「擬似ヘッダー(Pseudo Header)」と呼んでいます。

「えっ、UDPの話なのに、なんでIPアドレスまで関係あるの?」と思いますよね。
これは、「あて先違いのパケットが、偶然別の場所に届いていないか」を厳重にチェックするためです。たとえUDPのデータ自体が壊れていなくても、もし「宛先のIPアドレス」が途中で書き換わっていたら大惨事ですよね。そのため、UDPは「この送信元から、この宛先へ向かう正しい旅の途中だよ」という証明も含めて計算を行っているのです。

チェックサムの計算アルゴリズムの基本ステップ

1. 擬似ヘッダーの準備: 送信元IPアドレス、宛先IPアドレス、プロトコル番号(UDPを示す値)、UDPのデータ長などを並べます。
2. データの分割: 擬似ヘッダー、UDPヘッダー、そして実際のデータをすべて「16ビット(2バイト)ごと」の塊に切り分けます。
3. 1の補数和の計算: 切り分けた塊を、ひたすら足し算していきます。もし足し算した結果桁あふれ(キャリー)が発生したら、一番下の桁にクルッと足し戻します(これを「1の補数和(One’s complement sum)」と呼びます)。
4. ビット反転: 最後に、得られた合計値のすべてのビットを「0は1に、1は0に」パカッと反転させます。これがチェックサムの値になります。

受信側は、このチェックサムも含めて全く同じ計算をもう一度行います。もしデータが一切壊れていなければ、計算結果はきれいな「すべて1(または全て0)」になる仕組みになっています。

—

3. ちょっと待って!UDPチェックサムの「見えない限界」

ここまで聞くと、「UDPって結構しっかりチェックしてるじゃん!」と思われるかもしれません。しかし、インフラエンジニアたちが頭を悩ませる「チェックサムの限界」がここに存在します。

世の中の仕組みには、どうしても「すり抜け」が発生するもの。UDPチェックサムが持つ、代表的な2つの弱点を見ていきましょう。

① 「1ビットの反転」なら完璧だけど、「2ビット以上」だと見破れないことがある

UDPチェックサムは「1の補数和」というシンプルな足し算をベースにしています。そのため、パケット内のどこか1箇所で「0が1に、あるいは1が0に」化けた場合(1ビットエラー)は、確実に検知することができます。

しかし、もし運悪く「ある場所のビットが0から1に変わり、別の場所のビットが1から0に変わった(2箇所のビットエラー)」という奇跡的な事故が起きるとどうなるでしょうか?
足し算の「プラスマイナス」が相殺されてしまい、合計値が変わらなくなってしまうのです!その結果、受信側は「おっ、計算がピッタリ合ったからデータは正常だな!」と勘違いし、壊れたデータをそのまま上のアプリケーション層に渡しちゃいます。これが、チェックサムの信頼性の限界です。

② IPv4では「オプション」だった歴史

実は、IPv4の時代、UDPのチェックサム計算は「任意(省略可能)」でした。(※厳密には、送信側が計算をサボってチェックサムにすべて0を入れることが許されていました)。
「ルーターのCPU負荷を少しでも下げて、高速にパケットを転送したい!」という昔のエンジニアたちの知恵(あるいは妥協)だったのですが、現代の信頼性が求められるネットワークや、IPv6(こちらはチェックサム計算が必須になりました)の世界では、この「計算をサボれる」という仕様が思わぬトラブルの温床になることもありました。

—

4. 実際にPythonでUDPチェックサムの雰囲気を覗いてみよう

百聞は一見に如かず。実際にPythonを使って、UDPチェックサムの計算(1の補数和)がどのように行われているのか、そのエッセンスを短いコードで覗いてみましょう。

import socket
import struct


def calculate_ones_complement_sum(data: bytes) -> int:
    """与えられたバイト列に対して1の補数和を計算する簡易関数"""
    if len(data) % 2 != 0:
        # データ長が奇数の場合、末尾にパディング(0x00)を1バイト追加する
        data += b"\x00"

    sum_val = 0
    # 2バイト(16ビット)ずつ取り出して足し合わせる
    for i in range(0, len(data), 2):
        # 2バイトをビッグエンディアンの符号なし短整数として結合
        word = struct.unpack("!H", data[i : i + 2])[0]
        sum_val += word

        # 16ビットを超える桁あふれ(キャリー)が発生した場合の処理
        # 上位ビットのあふれを下位に足し戻す
        while sum_val >> 16:
            sum_val = (sum_val & 0xFFFF) + (sum_val >> 16)

    return sum_val


# サンプルデータの作成(例として適当なバイナリデータを渡す)
sample_payload = b"\x00\x01\x02\x03\x04\x05"
result_sum = calculate_ones_complement_sum(sample_payload)

# 最後にビットを反転させる(1の補数)
checksum = ~result_sum & 0xFFFF

print(f"計算されたUDPチェックサム (16進数): 0x{checksum:04X}")

実務の現場では、このようなチェックサムの計算は、CPU任せにするのではなく、NIC(ネットワークカード)のハードウェア機能(オフロード機能)を使って超高速に処理させています。しかし、パケットキャプチャツール(Wiresharkなど)で「Checksum: 0x… [incorrect]」という赤い警告を見たときは、まさに今回解説した計算結果が一致しなかった瞬間なのだと気づけるはずです。

—

5. おわりに:完璧ではないからこそ、上位層で守る

いかがでしたでしょうか? 今回はUDPチェックサムの計算アルゴリズムと、その背後にあるビット反転検出の限界についてお話ししました。

UDPチェックサムは、決して完璧なエラー検出機能ではありません。「2ビット以上の同時破損をすり抜けてしまうかもしれない」「IPv4では昔サボることができた」といった、どこか人間臭い(?)限界を抱えています。

だからこそ、現代のWebアプリケーションやネットワーク設計では、「UDPのチェックサムだけに頼らない」というアプローチが重要視されています。例えば、音声・動画ストリーミング(WebRTCなど)やオンラインゲームの通信では、UDPの上で独自のアプリケーション層の暗号化やシーケンス番号チェック、前方誤り訂正(FEC)といった仕組みを組み合わせることで、ヤンチャなUDPの信頼性を補っているのです。

ネットワークの基礎を学ぶことは、こうした「プロトコルが抱える絶妙な妥協点と工夫」の歴史を紐解くことでもあります。ぜひ、日々のインフラ構築やトラブルシューティングの際に、「今、パケットの中ではこんな計算が行われているんだな」と想像を膨らませてみてくださいね。

それでは、また次回の技術解説でお会いしましょう!

コメント

タイトルとURLをコピーしました