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 through inetFrame via a lazy() 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.

Examples

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]));