【入門編】 IPv4ヘッダーのHeader Checksumの計算と検証 – ネットワーク基礎とWebセキュリティ実践ガイド

こんにちは!ネットワークの世界へようこそ。
インフラやWebの裏側を覗いていると、「パケット」という言葉に出会いますよね。私たちが何気なくブラウザでWebサイトを見たり、メッセージを送ったりするとき、データは小さな荷物(パケット)に分解されて、世界中のルーターという中継地点を駆け抜けています。

さて、このパケット、宛先まで無事に届くために色々な工夫がされているのですが、今回はその中でも「IPv4ヘッダーのチェックサム(Header Checksum)」という、ちょっと地味だけどめちゃくちゃ重要な仕組みにスポットを当ててみたいと思います。

「チェックサム? なんか難しそう……」と思ったそこのあなた、大丈夫です!一歩ずつ、身近な例えを交えながら優しく紐解いていきましょう。

—

1. 宛先までの「荷物の送り状」とチェックサムの正体

インターネットの世界を郵便配達に例えてみましょう。
私たちが送るデータ(Webページの画像やテキストなど)は、段ボール箱に入れられます。そして、その箱の表面には、送り主の住所(送信元IPアドレス)や、宛先の住所(宛先IPアドレス)が書かれた「送り状」がペタッと貼られますよね。これがネットワークの世界でいう「IPヘッダー」です。

ここで、こんな心配事が生まれませんか?
「もし、途中の配達員(ルーター)が走り書きするときに、宛先の住所をうっかり書き間違えちゃったらどうしよう?」
「雨に濡れて、住所の文字が滲んで読めなくなったらどうするの?」

そんな悲劇を防ぐために編み出されたのが、「チェックサム(誤り検出符号)」です。
送り状の隅っこに、住所やその他の重要な情報を全部足し算して導き出した「秘密の数字(合言葉)」を書いておくのです。届いた側は、届いた住所をもう一度同じルールで計算し、隅っこに書いてある秘密の数字と一致するかチェックします。もし計算結果が合わなければ、「おや、途中でデータが書き換わってしまったぞ! この荷物は危険だから捨てよう」と、途中で弾くことができるわけですね。

—

2. 現場の苦労:ルーターでTTLが書き換わるたびの「再計算」

さて、このIPv4のチェックサム、実は他のプロトコル(例えばTCPやUDPなど)とはちょっと違う、面白いけれど頭の痛い宿命を背負っています。

それは、「ルーターを通過するたびに、チェックサムを計算し直さなきゃいけない」という点です。

IPヘッダーの中には、TTL (Time to Live) という、いわば「このパケットの寿命(残りホップ数)」を表すカウンターが入っています。ルーターは、パケットを受け取るたびにこの TTL の数字を 1 ずつ減らします。無限ループでパケットがインターネットをさまよい続けるのを防ぐための素晴らしい仕組みなのですが……。

お気づきでしょうか?
ルーターが TTL の数値を書き換えるということは、「IPヘッダーの中身が変わった」ということです。
中身が変わるのだから、当然、先ほど計算した「秘密の数字(チェックサム)」も古いものになってしまいます。そのため、ルーターはパケットを次の宛先に送り出すたびに、
1. TTL を 1 減らす
2. 変わった部分に合わせてチェックサムを再計算する
3. 新しいチェックサムをヘッダーに書き込む

という気の遠くなるような作業を、通過するすべてのパケットに対して秒単位で何百万回もこなしているのです。この「毎回書き換えて再計算するオーバーヘッド」が、実はルーターのCPUにじわじわと負荷をかける要因の一つでもありました。
(ちなみに、次世代の IPv6 では、このルーターの負荷を減らすために、なんとIPヘッダー自体のチェックサムがごっそり廃止されました!時代の進化ですね。)

—

3. 実際にどうやって計算しているの?(1の補数和の仕組み)

「チェックサムって、どうやって計算しているの?」気になりますよね。
IPv4のチェックサムは、難しい暗号技術を使っているわけではなく、「1の補数和(One’s complement sum)」という、ちょっとレトロでシンプルなおいしい計算方法を使っています。

大まかな手順はこうです。
1. IPヘッダーを 16ビット(2バイト)ずつのブロックに区切る。
2. そのブロックを全部足し算する。
3. 足し算した結果から「あふれた桁(キャリー)」を、下位の桁に足し戻す。
4. 最後に、その結果のビットをすべて反転させる(これが「1の補数」です)。

言葉だけだとフワッとしてしまうので、実際にPythonを使って、このチェックサムを計算するシンプルなスクリプトを見てみましょう。実務でパケット解析ツール(Wiresharkなど)を使うときや、自作のパケット送信ツールを作る際の参考になりますよ。

def calculate_checksum(header: bytes) -> int:
    """
    IPv4ヘッダーのチェックサム(1の補数和)を計算する関数
    :param header: チェックサムフィールドを 0 にクリアした 20バイト以上のIPv4ヘッダー
    :return: 16bitのチェックサム値
    """
    if len(header) % 2 != 0:
        # ヘッダーの長さが奇数の場合、パディングとして 0 バイトを追加する
        header += b'\x00'

    checksum = 0
    
    # 16ビット(2バイト)ずつ取り出して足し合わせる
    for i in range(0, len(header), 2):
        # 2バイトを16ビットの整数として結合
        word = (header[i] << 8) + header[i+1]
        checksum += word

    # 32ビットに収まった計算結果の上位16ビット(あふれた分)を下位16ビットに足し込む(キャリーの処理)
    while (checksum >> 16) > 0:
        checksum = (checksum & 0xFFFF) + (checksum >> 16)

    # ビットを反転させる(1の補数を取る)ことで、最終的なチェックサムを得る
    checksum = ~checksum & 0xFFFF

    return checksum

# --- 使用例 ---
# 例として、仮のIPv4ヘッダー(20バイト、チェックサム位置は 0x0000 にクリア済み)を用意します
dummy_ipv4_header = b"""
\x45\x00\x00\x3c\x1c\x46\x40\x00\x40\x06\x00\x00\xc0\xa8\x01\x0a\xc0\xa8\x01\x01
"""

computed_checksum = calculate_checksum(dummy_ipv4_header)
print(f"計算されたチェックサム (16進数): 0x{computed_checksum:04x}")

このコードでは、ヘッダーの値を足し合わせ、あふれた分をクルクルと足し戻し、最後に反転させるというIPv4ならではのルールを再現しています。自分で動かしてみると、「なるほど、こうやってあの数字が生まれているんだな」と実感が湧いてきますよね!

—

4. まとめ:パケットの旅を支える小さな守り神

今回は、IPv4ヘッダーのチェックサムについて、郵便配達の例えや計算の裏側を覗きながら解説してきました。

  • パケットの送り状(IPヘッダー)の整合性を保つための「秘密の数字」であること。
  • ルーターが TTL を書き換えるたびに、チェックサムの再計算という泥臭い努力を強いられていること。
  • その計算には「1の補数和」というシンプルで確実なルールが使われていること。

普段何気なく使っているインターネットですが、こうした目立たない仕組みたちが、私たちのデータを正確に、一文字も狂わずに目的地まで届けようと必死に働いてくれています。

トラブルシューティングでWiresharkを開いたとき、「あ、このパケットのチェックサム、ここで再計算されたんだな」なんて背景に思いを馳せられるようになると、ネットワークエンジニアとしての深みが一気に増していきますよ。

それでは、また次回の技術解説でお会いしましょう!安全で快適なネットワークライフを!

コメント

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