From 69cb96e53877f835d5273a2e6d26bc72f4d2c443 Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Mon, 17 Nov 2025 16:50:06 -0500 Subject: [PATCH] network: correct MAC classification to match RFC 9542 (SLAP) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RFC 9542 replaces RFC 7042 and defines the Structured Local Address Plan (SLAP), subdividing the locally administered address space into four distinct quadrants: - Administratively Assigned (x2), - Reserved (x6), - Extended Local (xA), - and Standard Assigned (xE). Modern systems — including iOS, Android, virtualization platforms, containers, and privacy-preserving network stacks — use these ranges extensively. This patch updates the classification logic to implement RFC 9542 semantics: - Decode SLAP quadrants for local addresses (x2/x6/xA/xE) - Distinguish global vendor MACs from global addresses with unknown vendors - Correctly identify multicast addresses (bit 0 set) and avoid mislabeling them as local - Preserve support for mock ip- addresses and malformed/unknown formats This improves accuracy for randomized mobile MACs, manufacturer local address blocks, protocol-assigned local addresses, and security-focused deployments where correct interpretation of the first-octet bit fields matters. Signed-off-by: Robin Getz --- scripts/js/network.js | 59 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/scripts/js/network.js b/scripts/js/network.js index e6116e8f10..350ddab5bb 100644 --- a/scripts/js/network.js +++ b/scripts/js/network.js @@ -179,16 +179,57 @@ $(() => { $("td:eq(0)", row).html(ips.join("
")); // MAC + Vendor field if available - if (data.macVendor && data.macVendor.length > 0) { - $("td:eq(1)", row).html( - utils.escapeHtml(data.hwaddr) + "
" + utils.escapeHtml(data.macVendor) - ); - } - - // Make mock MAC addresses italics and add title + const cell = $("td:eq(1)", row); if (data.hwaddr.startsWith("ip-")) { - $("td:eq(1)", row).css("font-style", "italic"); - $("td:eq(1)", row).attr("title", "Mock MAC address"); + // Case 1 - Make mock MAC addresses italics and add title + cell.css("font-style", "italic").attr("title", "Mock MAC address"); + } else if (data.hwaddr.includes(":")) { + // Case 2 — MAC-like address + const parts = data.hwaddr.split(":"); + const firstOctet = Number.parseInt(parts[0], 16); + // Arithmetic-only flag detection + const isMulticast = firstOctet % 2 === 1; // bit 0 + const isLocal = Math.floor(firstOctet / 2) % 2 === 1; // bit 1 + // Multicast overrides all SLAP meaning + if (isMulticast) { + // Case 2.1 - Multicast + cell.html(utils.escapeHtml(data.hwaddr) + "
Multicast Address"); + } else if (isLocal) { + // Case 2.2 - Local SLAP + let slapLabel = null; + // SLAP quadrant resolution (RFC 9542) + const ts = Math.floor(firstOctet / 4) % 4; // bits 2–3 become bits 0–1 + switch (ts) { + case 0x0: + slapLabel = "Administratively Assigned (AAI)"; + break; + case 0x1: + slapLabel = "Extended Local (ELI)"; + break; + case 0x2: + slapLabel = "Reserved"; + break; + case 0x3: + slapLabel = "Standard Assigned (SAI)"; + break; + default: + // satisfy ESLint + slapLabel = "Unclassified"; + break; + } + + cell.html(utils.escapeHtml(data.hwaddr) + "
Locally Administered"); + cell.css("font-style", "italic").attr("title", slapLabel); + } else if (data.macVendor && data.macVendor.length > 0) { + // Case 2.3 - Global address space (U/L = 0) + cell.html(utils.escapeHtml(data.hwaddr) + "
" + utils.escapeHtml(data.macVendor)); + } else { + // Case 2.4 - Lookup failed + cell.html(utils.escapeHtml(data.hwaddr) + "
Global: Unknown"); + } + } else { + // Case 3 — Not IP-mock, not MAC-like + cell.css("color", "red").html(utils.escapeHtml(data.hwaddr) + "
Unknown Format"); } // Add delete button