IPv6 fixed header encoding and decoding (RFC 8200).
Every IPv6 packet opens with a 40-byte fixed header, followed by zero or more extension headers and then the upper-layer payload:
0 7 8 15 16 23 24 31 +--------+--------+--------+--------+ |Version | Traffic Class| Flow Label| +--------+--------+--------+--------+ | Payload Length | Next Hd|Hop Lim.| +--------+--------+--------+--------+ | | + + | | + Source Address + | (16 bytes / 128 bits) | + + | | +--------+--------+--------+--------+ | | + + | | + Destination Address + | (16 bytes / 128 bits) | + + | | +--------+--------+--------+--------+ | Payload (variable) | +-----------------------------------+
version, trafficClass, and flowLabel share the first 32-bit
big-endian word of the header (4 + 8 + 20 bits), so they are decoded as a
single nested versionClassFlow object via bitStruct rather than three
independent fields.
payloadLength counts only the bytes after the 40-byte fixed header —
unlike IPv4's totalLength, it excludes the header itself. payload is
therefore exactly payloadLength bytes.
This is a v0.0.1, header-only coder: extension headers (Hop-by-Hop
Options, Routing, Fragment, Destination Options, ESP, AH, …) are not
parsed. nextHeader is surfaced as a raw protocol number and everything
after the fixed header — extension headers included, if any are present —
is handed back as opaque payload bytes. Callers who need to walk the
extension header chain must do so themselves, or await a future version
of this package that adds it.
sourceAddress / destinationAddress are raw 16-byte slices, not
bigint. Pair this with @hertzg/ip's addressv6FromBytes /
addressv6ToBytes for the bigint view and stringifyAddressv6 /
parseAddressv6(s).address for the human-readable one; the conversion is
left to the caller to keep this package free of that dependency.
Round-trip a minimal IPv6 packet (no payload)
Round-trip a minimal IPv6 packet (no payload)
import { assertEquals } from "@std/assert"; import { ipv6Packet, IPV6_HEADER_SIZE } from "@binstruct/ipv6"; const coder = ipv6Packet(); const packet = { versionClassFlow: { version: 6, trafficClass: 0, flowLabel: 0 }, payloadLength: 0, nextHeader: 59, hopLimit: 64, sourceAddress: new Uint8Array(16), destinationAddress: new Uint8Array(16), payload: new Uint8Array(0), }; const buffer = new Uint8Array(IPV6_HEADER_SIZE); const written = coder.encode(packet, buffer); const [decoded, read] = coder.decode(buffer); assertEquals(written, IPV6_HEADER_SIZE); assertEquals(read, IPV6_HEADER_SIZE); assertEquals(decoded.versionClassFlow.version, 6); assertEquals(decoded.hopLimit, 64);