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.

Examples

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