Inet stack coder for the @binstruct/* packet family.
@binstruct/inet is a thin orchestration layer: each protocol package
(@binstruct/ethernet, @binstruct/sll, @binstruct/vlan,
@binstruct/pppoe, @binstruct/ipv4, @binstruct/ipv6, @binstruct/arp,
@binstruct/tcp, @binstruct/udp, @binstruct/icmp, @binstruct/icmpv6,
@binstruct/igmp, @binstruct/esp, @binstruct/vxlan, @binstruct/ntp,
@binstruct/bfd) only knows how to decode its own layer's bytes. This
package wires them together via refineSwitch and refineFields —
dispatching on a host discriminator field at every layer (etherType /
protocol, nextHeader, IP protocol, UDP port, PPP protocol ID) — into
round-trippable coder factories that walk a captured frame top-down. Each
layer's payload field is surfaced as the typed value of the next layer;
layers we don't have a coder for default to a raw Uint8Array,
so the coders are safe to point at arbitrary captured traffic.
Coverage:
- L2 — Ethernet II (
inetFrame,@binstruct/ethernet) and Linux cooked capture (sllInetFrame,@binstruct/sll), each a separate root sharing the same L3 dispatch logic. - L2.5 — IEEE 802.1Q VLAN tagging (
@binstruct/vlan), including a single bounded level of QinQ double-tagging, and PPPoE (@binstruct/pppoe) Discovery and Session stages, the latter carrying an internal PPP protocol-ID mini-layer that dispatches to IPv4/IPv6. - L3 — IPv4 (
@binstruct/ipv4), IPv6 (@binstruct/ipv6), ARP (@binstruct/arp). - L4 (under IPv4/IPv6) — TCP (
@binstruct/tcp), UDP (@binstruct/udp), ICMPv4 (@binstruct/icmp), ICMPv6 (@binstruct/icmpv6), IGMP (@binstruct/igmp, IPv4 only), ESP (@binstruct/esp). - L4 (under UDP) — port-based dispatch to VXLAN (
@binstruct/vxlan, whose inner Ethernet frame tunnels back throughinetFramevia alazy()coder to break the build-time recursion), NTP (@binstruct/ntp), and BFD (@binstruct/bfd).
Adding a layer is one new refineFields arm in the relevant
refineSwitch plus an entry in its selector.
Also exports internetChecksum (RFC 1071) for callers that need
to fill in IPv4/UDP/ICMP/TCP checksum fields.
Each refined payload is the typed value directly — no { kind, ... }
wrapper. The on-wire tag (etherType / protocol at L3, IP protocol /
nextHeader at L4, UDP port, PPP protocol ID) on the host record is the
discriminator; narrow the union with property-existence checks
("protocol" in payload, "srcPort" in payload, …) when reading decoded
values.
Round-trip a UDP-over-IPv4-over-Ethernet frame
Round-trip a UDP-over-IPv4-over-Ethernet frame
import { assert, assertEquals } from "@std/assert"; import { parseAddressv4 } from "@hertzg/ip/addressv4"; import { ETHERTYPE_IPV4 } from "@binstruct/ipv4"; import { IP_PROTOCOL_UDP } from "@binstruct/udp"; import { inetFrame } from "@binstruct/inet"; const value = { dstMac: new Uint8Array([0, 0, 0, 0, 0, 1]), srcMac: new Uint8Array([0, 0, 0, 0, 0, 2]), etherType: ETHERTYPE_IPV4, payload: { versionIhl: { version: 4, ihl: 5 }, typeOfService: 0, totalLength: 32, identification: 0, flagsFragmentOffset: { reserved: 0, dontFragment: 0, moreFragments: 0, fragmentOffset: 0 }, timeToLive: 64, protocol: IP_PROTOCOL_UDP, headerChecksum: 0, sourceAddress: parseAddressv4("192.0.2.1").address, destinationAddress: parseAddressv4("192.0.2.2").address, options: new Uint8Array(0), payload: { srcPort: 53, dstPort: 49152, length: 12, checksum: 0, payload: new Uint8Array([0xde, 0xad, 0xbe, 0xef]), }, }, }; const coder = inetFrame(); const buf = new Uint8Array(64); const written = coder.encode(value, buf); const [decoded] = coder.decode(buf.subarray(0, written)); assert(!(decoded.payload instanceof Uint8Array)); assert("protocol" in decoded.payload); assert(!(decoded.payload.payload instanceof Uint8Array)); assert("srcPort" in decoded.payload.payload); assertEquals(decoded.payload.payload.payload, new Uint8Array([0xde, 0xad, 0xbe, 0xef]));