Skip to content

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:

  1. Zeros the checksum field in the output buffer.
  2. Computes the checksum over all bytes of the serialized struct.
  3. 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 @checksum annotation is allowed per packet definition or frame branch. 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:
wire
@checksum(internet)
header_checksum: u16,

Supported Algorithms

AlgorithmField TypeStandard
internetu16RFC 1071 one's complement sum
crc32u32IEEE 802.3 (Ethernet, zlib)
crc32cu32Castagnoli (iSCSI, SCTP, QUIC)
fletcher16u16RFC 1146

IPv4 Internet Checksum

The Internet Checksum (RFC 1071) is a 16-bit one's complement sum used in IPv4, ICMP, and UDP headers.

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,
}

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.

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,
}

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.

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,
}

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.

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,
}

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:

wire
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:

wire
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 / ContextRecommended Algorithm
IPv4, ICMP, UDP headersinternet
Ethernet FCScrc32
iSCSI, SCTP, QUIC extensionscrc32c
Lightweight embedded protocolsfletcher16