Skip to content

チェックサム

wirespec は @checksum アノテーションでチェックサムを組み込みサポートしています。チェックサムフィールドに付与すると、パース時の検証とシリアライズ時の自動計算を行うコードが生成されます。ボイラープレートは不要です。

@checksum の仕組み

パース時

パケットをパースする際、構造体全体のバイト列に対してチェックサムを検証します。検証に失敗するとパース関数は WIRESPEC_ERR_CHECKSUM を返し、出力構造体は変更されません。

シリアライズ時

パケットをシリアライズする際、wirespec は以下を行います。

  1. 出力バッファのチェックサムフィールドをゼロクリア
  2. シリアライズ済みの全バイトに対してチェックサムを計算
  3. 計算結果でチェックサムフィールドをインプレースでパッチ

呼び出し側がシリアライズ前にチェックサムフィールドを設定する必要はありません。値は常に無視され、上書きされます。

カバレッジ

チェックサムは常に構造体全体(ゼロ化したチェックサムフィールドを含む全フィールド)をカバーします。TCP/UDP の疑似ヘッダチェックサムのようなレイヤーまたぎの計算は wirespec のスコープ外です。

制約

  • 1 つの packet 定義または frame ブランチにつき @checksum1 つだけ許可されます。同一スコープに複数あるとコンパイルエラーです。
  • フィールド型はアルゴリズムが要求する型と一致しなければなりません(下表参照)。
  • アノテーションは対象フィールドの直前に記述します。
wire
@checksum(internet)
header_checksum: u16,

サポートされているアルゴリズム

アルゴリズムフィールド型標準
internetu16RFC 1071 1 の補数和
crc32u32IEEE 802.3(Ethernet、zlib)
crc32cu32Castagnoli(iSCSI、SCTP、QUIC)
fletcher16u16RFC 1146

IPv4 インターネットチェックサム

インターネットチェックサム(RFC 1071)は IPv4、ICMP、UDP ヘッダで使われる 16 ビットの 1 の補数和です。

wire
module ip.v4
@endian big

# IPv4 Header (RFC 791)
packet IPv4Header {
    version: bits[4],
    ihl: bits[4],
    dscp: bits[6],
    ecn: bits[2],
    total_length: u16,
    identification: u16,
    flags: bits[3],
    fragment_offset: bits[13],
    ttl: u8,
    protocol: u8,
    @checksum(internet)
    header_checksum: u16,
    src_addr: u32,
    dst_addr: u32,
}

header_checksum は RFC 791 の規定どおりヘッダの 10 バイト目に配置されます。パース時は 20 バイト全体の 1 の補数和が 0xFFFF(または 0)であることを検証します。シリアライズ時はフィールドをゼロにして和を計算し、結果を書き戻します。


CRC32

CRC32(IEEE 802.3)は Ethernet フレーム、ZIP アーカイブなど多くのフォーマットで使われます。フィールド型は u32 です。

wire
module checksum.crc32_test
@endian big

packet Crc32Packet {
    id: u16,
    length: u16,
    require length >= 8,
    data: bytes[length: length - 8],
    @checksum(crc32)
    checksum: u32,
}

require length >= 8 は、可変長データ領域の前にある固定ヘッダフィールド分のバイト数を確保するガードです。


CRC32C

CRC32C は Castagnoli 多項式を使い、ハードウェアアクセラレーションが使えるストレージ/ネットワークプロトコル(iSCSI、SCTP、QUIC)で好まれます。フィールド型は u32 です。

wire
module checksum.crc32c_test
@endian big

packet Crc32cPacket {
    id: u16,
    length: u16,
    require length >= 8,
    data: bytes[length: length - 8],
    @checksum(crc32c)
    checksum: u32,
}

ワイヤフォーマットは CRC32 と同一で、多項式だけが異なります。iSCSI、SCTP、QUIC 拡張向けには crc32c を使ってください。


Fletcher-16

Fletcher-16(RFC 1146)は単純な和より優れたエラー検出能力を持つ軽量チェックサムで、計算コストも低いです。フィールド型は u16 です。

wire
module checksum.fletcher16_test
@endian big

packet Fletcher16Packet {
    id: u16,
    length: u16,
    require length >= 6,
    data: bytes[length: length - 6],
    @checksum(fletcher16)
    checksum: u16,
}

require length >= 6 は、data より前の固定幅フィールドが 6 バイト(id + length + checksum)であることに対応します。


よくあるパターン

末尾のチェックサム

最も自然な配置は構造体の末尾です。

wire
packet EthernetFcs {
    dst_mac: bytes[6],
    src_mac: bytes[6],
    ether_type: u16,
    payload: bytes[remaining],
    @checksum(crc32)
    fcs: u32,
}

中間のチェックサム(IPv4 スタイル)

アノテーションはどの位置でも機能します。チェックサムフィールドより後のフィールドもカバレッジに含まれます。

wire
packet IPv4Header {
    # ... チェックサム前のフィールド ...
    @checksum(internet)
    header_checksum: u16,
    src_addr: u32,   # チェックサムカバレッジに含まれる
    dst_addr: u32,   # チェックサムカバレッジに含まれる
}

アルゴリズムの選び方

プロトコル / 用途推奨アルゴリズム
IPv4、ICMP、UDP ヘッダinternet
Ethernet FCScrc32
iSCSI、SCTP、QUIC 拡張crc32c
軽量な組み込みプロトコルfletcher16