Ethernet II frame encoding and decoding.

A frame is a 6-byte destination MAC, a 6-byte source MAC, a 2-byte EtherType, and a variable-length payload:

 0      7 8     15 16    23 24    31
+--------+--------+--------+--------+
|      Destination MAC (4 of 6)     |
+--------+--------+--------+--------+
| DstMAC (last 2) | SrcMAC (1st 2)  |
+--------+--------+--------+--------+
|        Source MAC (4 of 6)        |
+--------+--------+--------+--------+
|    EtherType    |                 |
+-----------------+     Payload     |
|             (variable)            |
+-----------------------------------+

The frame check sequence (FCS) at the end of a wire frame is not part of this coder's layout — kernels and NICs handle it.

For MAC address parsing/stringification, use the sister package @hertzg/mac.

Migration from 0.x

The parseMacAddress and stringifyMacAddress exports were removed in 1.0. Replace with @hertzg/mac:

// before
import { parseMacAddress, stringifyMacAddress } from "@binstruct/ethernet";
// after
import { parse as parseMac, stringify as stringifyMac } from "@hertzg/mac";

Examples

Basic encoding and decoding

import { assertEquals } from "@std/assert";
import { ethernet2Frame } from "@binstruct/ethernet";

const frameCoder = ethernet2Frame();
const testFrame = {
  dstMac: new Uint8Array([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]),
  srcMac: new Uint8Array([0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB]),
  etherType: 0x0800, // IPv4
  payload: new Uint8Array([0x45, 0x00, 0x00, 0x14]),
};

const buffer = new Uint8Array(1500);
const bytesWritten = frameCoder.encode(testFrame, buffer);
const [decodedFrame, bytesRead] = frameCoder.decode(buffer);

assertEquals(bytesRead, buffer.length);
assertEquals(bytesWritten, 18);
assertEquals(decodedFrame.dstMac, testFrame.dstMac);
assertEquals(decodedFrame.srcMac, testFrame.srcMac);
assertEquals(decodedFrame.etherType, testFrame.etherType);

Compose with @hertzg/mac for string-form addresses

import { assertEquals } from "@std/assert";
import { refine } from "@hertzg/binstruct";
import { ethernet2Frame, type Ethernet2Frame } from "@binstruct/ethernet";
import { parse as parseMac, stringify as stringifyMac } from "@hertzg/mac";

type StringMacFrame = Omit<Ethernet2Frame, "dstMac" | "srcMac"> & {
  dstMac: string;
  srcMac: string;
};

const frameCoder = refine(ethernet2Frame(), {
  refine: (frame: Ethernet2Frame): StringMacFrame => ({
    ...frame,
    dstMac: stringifyMac(frame.dstMac),
    srcMac: stringifyMac(frame.srcMac),
  }),
  unrefine: (frame: StringMacFrame): Ethernet2Frame => ({
    ...frame,
    dstMac: parseMac(frame.dstMac),
    srcMac: parseMac(frame.srcMac),
  }),
})();

const buffer = new Uint8Array(1500);
frameCoder.encode({
  dstMac: "00:11:22:33:44:55",
  srcMac: "66:77:88:99:aa:bb",
  etherType: 0x0800,
  payload: new Uint8Array([0x45, 0x00, 0x00, 0x14]),
}, buffer);
const [decoded] = frameCoder.decode(buffer);
assertEquals(decoded.dstMac, "00:11:22:33:44:55");