PPPoE (PPP over Ethernet) header encoding and decoding (RFC 2516).
A PPPoE frame opens with a 6-byte header followed by a variable-length payload:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | VER | TYPE | CODE | SESSION ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | LENGTH | PAYLOAD ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
version and type are each 4-bit fields packed into the first octet;
both are fixed at 1 by RFC 2516. code distinguishes Discovery stage
packets (PADI, PADO, PADR, PADS, PADT) from session data. length is the
payload size in octets, so the payload is always length bytes long — it
does not include the 6-byte header itself.
PPPoE runs directly over Ethernet using two dedicated EtherTypes: Discovery
stage frames (ETHERTYPE_PPPOE_DISCOVERY) carry PADI/PADO/PADR/
PADS/PADT, while Session stage frames (ETHERTYPE_PPPOE_SESSION)
carry PPP frames as the PPPoE payload.
This package covers the 6-byte header and its payload framing only. The
Discovery stage TAG-based TLV encoding (TAG_TYPE/TAG_LENGTH/TAG_VALUE) and
the PPP protocol carried inside a session's payload are out of scope for
v0.0.1 — the payload field is handed back as raw bytes for callers to
parse further.
Round-trip a PADI discovery packet
Round-trip a PADI discovery packet
import { assertEquals } from "@std/assert"; import { pppoeHeader, PPPOE_CODE, PPPOE_HEADER_SIZE } from "@binstruct/pppoe"; const coder = pppoeHeader(); const packet = { versionType: { version: 1, type: 1 }, code: PPPOE_CODE.PADI, sessionId: 0, length: 4, payload: new Uint8Array([0x01, 0x01, 0x00, 0x00]), }; const buffer = new Uint8Array(PPPOE_HEADER_SIZE + packet.payload.length); const written = coder.encode(packet, buffer); const [decoded, read] = coder.decode(buffer.subarray(0, written)); assertEquals(written, read); assertEquals(decoded.code, PPPOE_CODE.PADI); assertEquals(decoded.sessionId, 0); assertEquals(decoded.payload, packet.payload);