Checksums
wirespec provides built-in checksum support through the @checksum annotation. Place it on any checksum field and the compiler generates verification logic on parse and auto-computation logic on serialize — no boilerplate required.
How @checksum Works
On parse
When a packet is parsed, wirespec verifies the checksum field against the entire parsed struct byte sequence. If the verification fails, the parse function returns WIRESPEC_ERR_CHECKSUM and the output struct is left unmodified.
On serialize
When a packet is serialized, wirespec:
- Zeros the checksum field in the output buffer.
- Computes the checksum over all bytes of the serialized struct.
- Patches the checksum field in-place with the computed value.
The caller does not need to set the checksum field before calling serialize — its value is always ignored and overwritten.
Coverage
The checksum always covers the entire struct (all fields, including the checksum field itself zeroed). TCP/UDP pseudo-header checksums that span layer boundaries are outside wirespec's scope.
Constraints
- Only one
@checksumannotation is allowed perpacketdefinition orframebranch. Multiple checksum fields in the same scope are a compile error. - The field type must match the algorithm's required type (see table below).
- The annotation must appear immediately before the field it covers:
@checksum(internet)
header_checksum: u16,Supported Algorithms
| Algorithm | Field Type | Standard |
|---|---|---|
internet | u16 | RFC 1071 one's complement sum |
crc32 | u32 | IEEE 802.3 (Ethernet, zlib) |
crc32c | u32 | Castagnoli (iSCSI, SCTP, QUIC) |
fletcher16 | u16 | RFC 1146 |
IPv4 Internet Checksum
The Internet Checksum (RFC 1071) is a 16-bit one's complement sum used in IPv4, ICMP, and UDP headers.
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,
}The header_checksum field is placed at byte 10 in the header, exactly as RFC 791 specifies. On parse, wirespec verifies that the one's complement sum of all 20 bytes equals 0xFFFF (or 0). On serialize, the field is zeroed, the sum is computed, and the result is written back.
CRC32
CRC32 (IEEE 802.3) is used in Ethernet frames, ZIP archives, and many other formats. The field type must be 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,
}The require length >= 8 guard ensures the length field accounts for the fixed-width header fields before the variable-length data region.
CRC32C
CRC32C uses the Castagnoli polynomial and is preferred in storage and networking protocols where hardware acceleration is available (iSCSI, SCTP, QUIC). The field type must be 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,
}The wire format is identical to CRC32 — only the polynomial differs. Use crc32c when targeting iSCSI, SCTP, or QUIC extensions.
Fletcher-16
Fletcher-16 (RFC 1146) is a lightweight checksum with better error detection than a simple sum, at low computational cost. The field type must be 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,
}Note the require length >= 6: the fixed-width fields before data are 6 bytes (id + length + checksum), so the minimum valid length is 6.
Common Patterns
Checksum at the end
The most natural placement is at the end of the struct:
packet EthernetFcs {
dst_mac: bytes[6],
src_mac: bytes[6],
ether_type: u16,
payload: bytes[remaining],
@checksum(crc32)
fcs: u32,
}Checksum in the middle (IPv4-style)
The annotation works at any position. Fields after the checksum are included in coverage:
packet IPv4Header {
# ... fields before checksum ...
@checksum(internet)
header_checksum: u16,
src_addr: u32, # included in checksum coverage
dst_addr: u32, # included in checksum coverage
}Choosing an algorithm
| Protocol / Context | Recommended Algorithm |
|---|---|
| IPv4, ICMP, UDP headers | internet |
| Ethernet FCS | crc32 |
| iSCSI, SCTP, QUIC extensions | crc32c |
| Lightweight embedded protocols | fletcher16 |