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 = '
';
return;
}
if (isNaN(srcPort) || isNaN(dstPort) || srcPort < 0 || srcPort > 65535 || dstPort < 0 || dstPort > 65535) {
div.innerHTML = '
';
return;
}
if (isNaN(numUplinks) || numUplinks < 2 || numUplinks > 64) {
div.innerHTML = '
';
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 = `
`;
}
Error
Invalid IP address. Use dotted-decimal notation (e.g. 10.0.0.1).
Error
Invalid port. Range: 0\u201365535.
Error
Invalid uplink count. Range: 2\u201364.
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}