ECMP Flow Hash Calculator

function toBytes(srcIp32, dstIp32, srcPort, dstPort, proto) { return [ (srcIp32 >>> 24) & 0xff, (srcIp32 >>> 16) & 0xff, (srcIp32 >>> 8) & 0xff, srcIp32 & 0xff, (dstIp32 >>> 24) & 0xff, (dstIp32 >>> 16) & 0xff, (dstIp32 >>> 8) & 0xff, dstIp32 & 0xff, (srcPort >>> 8) & 0xff, srcPort & 0xff, (dstPort >>> 8) & 0xff, dstPort & 0xff, proto & 0xff ]; } function hashXor(srcIp32, dstIp32, srcPort, dstPort) { return (srcIp32 ^ dstIp32 ^ (srcPort ^ dstPort)) >>> 0; } function hashCrc32(bytes) { let crc = 0xFFFFFFFF; const table = buildCrc32Table(); for (const b of bytes) { crc = (table[(crc ^ b) & 0xff] ^ (crc >>> 8)) >>> 0; } return (crc ^ 0xFFFFFFFF) >>> 0; } let _crc32Table = null; function buildCrc32Table() { if (_crc32Table) return _crc32Table; _crc32Table = new Uint32Array(256); for (let i = 0; i < 256; i++) { let c = i; for (let j = 0; j < 8; j++) { c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1); } _crc32Table[i] = c; } return _crc32Table; } function hashFnv1a(bytes) { // FNV-1a 32-bit: offset_basis=2166136261, prime=16777619 let h = 2166136261; for (const b of bytes) { h = Math.imul(h ^ b, 16777619) >>> 0; } return h >>> 0; } function calcHash() { const srcIp = document.getElementById('srcIp').value; const dstIp = document.getElementById('dstIp').value; const srcPort = parseInt(document.getElementById('srcPort').value); const dstPort = parseInt(document.getElementById('dstPort').value); const proto = parseInt(document.getElementById('proto').value); const numUplinks = parseInt(document.getElementById('numUplinks').value); const div = document.getElementById('hashResult'); div.style.display = 'block'; const srcIp32 = ipToU32(srcIp); const dstIp32 = ipToU32(dstIp); if (srcIp32 === null || dstIp32 === null) { div.innerHTML = '
Error
Invalid IP address. Use dotted-decimal notation (e.g. 10.0.0.1).
'; return; } if (isNaN(srcPort) || isNaN(dstPort) || srcPort < 0 || srcPort > 65535 || dstPort < 0 || dstPort > 65535) { div.innerHTML = '
Error
Invalid port. Range: 0\u201365535.
'; return; } if (isNaN(numUplinks) || numUplinks < 2 || numUplinks > 64) { div.innerHTML = '
Error
Invalid uplink count. Range: 2\u201364.
'; return; } const bytes = toBytes(srcIp32, dstIp32, srcPort, dstPort, proto); const xorHash = hashXor(srcIp32, dstIp32, srcPort, dstPort); const crcHash = hashCrc32(bytes); const fnvHash = hashFnv1a(bytes); const xorUplink = (xorHash % numUplinks) + 1; const crcUplink = (crcHash % numUplinks) + 1; const fnvUplink = (fnvHash % numUplinks) + 1; div.innerHTML = `
Hash Result
5-tuple: ${srcIp}:${srcPort} \u2192 ${dstIp}:${dstPort} proto=${proto} | ${numUplinks} uplinks
XOR (Cisco legacy)
0x${xorHash.toString(16).toUpperCase().padStart(8,'0')}
Uplink ${xorUplink}
CRC32 (Arista / NX-OS)
0x${crcHash.toString(16).toUpperCase().padStart(8,'0')}
Uplink ${crcUplink}
FNV-1a (Juniper)
0x${fnvHash.toString(16).toUpperCase().padStart(8,'0')}
Uplink ${fnvUplink}
`; }