チェックサム
wirespec は @checksum アノテーションでチェックサムを組み込みサポートしています。チェックサムフィールドに付与すると、パース時の検証とシリアライズ時の自動計算を行うコードが生成されます。ボイラープレートは不要です。
@checksum の仕組み
パース時
パケットをパースする際、構造体全体のバイト列に対してチェックサムを検証します。検証に失敗するとパース関数は WIRESPEC_ERR_CHECKSUM を返し、出力構造体は変更されません。
シリアライズ時
パケットをシリアライズする際、wirespec は以下を行います。
- 出力バッファのチェックサムフィールドをゼロクリア
- シリアライズ済みの全バイトに対してチェックサムを計算
- 計算結果でチェックサムフィールドをインプレースでパッチ
呼び出し側がシリアライズ前にチェックサムフィールドを設定する必要はありません。値は常に無視され、上書きされます。
カバレッジ
チェックサムは常に構造体全体(ゼロ化したチェックサムフィールドを含む全フィールド)をカバーします。TCP/UDP の疑似ヘッダチェックサムのようなレイヤーまたぎの計算は wirespec のスコープ外です。
制約
- 1 つの
packet定義またはframeブランチにつき@checksumは1 つだけ許可されます。同一スコープに複数あるとコンパイルエラーです。 - フィールド型はアルゴリズムが要求する型と一致しなければなりません(下表参照)。
- アノテーションは対象フィールドの直前に記述します。
@checksum(internet)
header_checksum: u16,サポートされているアルゴリズム
| アルゴリズム | フィールド型 | 標準 |
|---|---|---|
internet | u16 | RFC 1071 1 の補数和 |
crc32 | u32 | IEEE 802.3(Ethernet、zlib) |
crc32c | u32 | Castagnoli(iSCSI、SCTP、QUIC) |
fletcher16 | u16 | RFC 1146 |
IPv4 インターネットチェックサム
インターネットチェックサム(RFC 1071)は IPv4、ICMP、UDP ヘッダで使われる 16 ビットの 1 の補数和です。
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 です。
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 です。
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 です。
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)であることに対応します。
よくあるパターン
末尾のチェックサム
最も自然な配置は構造体の末尾です。
packet EthernetFcs {
dst_mac: bytes[6],
src_mac: bytes[6],
ether_type: u16,
payload: bytes[remaining],
@checksum(crc32)
fcs: u32,
}中間のチェックサム(IPv4 スタイル)
アノテーションはどの位置でも機能します。チェックサムフィールドより後のフィールドもカバレッジに含まれます。
packet IPv4Header {
# ... チェックサム前のフィールド ...
@checksum(internet)
header_checksum: u16,
src_addr: u32, # チェックサムカバレッジに含まれる
dst_addr: u32, # チェックサムカバレッジに含まれる
}アルゴリズムの選び方
| プロトコル / 用途 | 推奨アルゴリズム |
|---|---|
| IPv4、ICMP、UDP ヘッダ | internet |
| Ethernet FCS | crc32 |
| iSCSI、SCTP、QUIC 拡張 | crc32c |
| 軽量な組み込みプロトコル | fletcher16 |