NetCalc ISP Suite v3.4 – Topología Detallada @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap'); body { font-family: 'Inter', sans-serif; } .ipv6-font { font-family: 'Courier New', Courier, monospace; letter-spacing: -0.5px; font-size: 0.9em; } .fade-in { animation: fadeIn 0.4s ease-out; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }

1. Red Principal

Ej: 192.168.0.0/24

2. Definir Subredes

3. Cola de Planificación

NombrePerfilHosts/Prefijo
No hay segmentos en la cola.
// --- Configuration & State --- let currentVersion = 'ipv4'; let requestedSubnets = [];// IPv6 Constants const IPV6_OPTS = [ { val: 'site_48', label: 'Delegación Corporativa (/48)', prefix: 48 }, { val: 'site_56', label: 'Delegación Pyme/Residencial (/56)', prefix: 56 }, { val: 'lan_64', label: 'Segmento LAN Estándar (/64)', prefix: 64 }, { val: 'p2p_127', label: 'Infraestructura P2P (/127)', prefix: 127 } ];const IPV4_OPTS = [ { val: 'lan', label: 'LAN / Oficina (Manual)' }, { val: 'p2p_wifi', label: 'Enlace Wireless (4 IPs)' }, { val: 'p2p_cable', label: 'Enlace Fibra/Cable (2 IPs)' }, { val: 'wan', label: 'WAN Pequeña (4 IPs)' }, { val: 'loopback', label: 'Loopback / Gestión (1 IP)' } ];// --- IPv4 Helper Functions --- function ip2long(ip) { let components = ip.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/); if (components) { let iplong = 0; let power = 1; for (let i = 4; i >= 1; i -= 1) { iplong += power * parseInt(components[i]); power *= 256; } return iplong; } else return -1; }function long2ip(long) { return [ (long >>> 24) & 0xFF, (long >>> 16) & 0xFF, (long >>> 8) & 0xFF, long & 0xFF ].join('.'); }// --- BigInt Math Helper for IPv6 --- function parseIPv6(ip) { let addr = ip.split('/')[0].trim(); if (addr.includes('::')) { const parts = addr.split('::'); const left = parts[0].split(':').filter(Boolean); const right = parts[1].split(':').filter(Boolean); const missing = 8 - (left.length + right.length); const zeros = Array(missing).fill('0'); addr = [...left, ...zeros, ...right].join(':'); } const segments = addr.split(':'); while(segments.length { hexStr += (seg || '0').padStart(4, '0'); }); return BigInt(hexStr); }function formatIPv6(bigIntVal) { let hex = bigIntVal.toString(16).padStart(32, '0'); let groups = []; for(let i=0; i g.replace(/^0+/, '') || '0').join(':'); }// --- UI Logic ---function setVersion(ver) { currentVersion = ver; const btn4 = document.getElementById('btn-ipv4'); const btn6 = document.getElementById('btn-ipv6'); const hint = document.getElementById('network-hint'); const rootInput = document.getElementById('root-network'); const select = document.getElementById('use-case'); const lblQty = document.getElementById('lbl-quantity'); const thReq = document.getElementById('th-req-size');if (ver === 'ipv4') { btn4.classList.replace('text-slate-400', 'text-indigo-600'); btn4.classList.replace('bg-transparent', 'bg-white'); btn4.classList.add('shadow-sm'); btn6.classList.replace('text-indigo-600', 'text-slate-400'); btn6.classList.replace('bg-white', 'bg-transparent'); btn6.classList.remove('shadow-sm');hint.textContent = "Ej: 192.168.0.0/24 o 10.0.0.0/8"; rootInput.placeholder = "Ej: 192.168.1.0/24"; lblQty.textContent = "Hosts Requeridos"; thReq.textContent = "Hosts"; select.innerHTML = IPV4_OPTS.map(o => `${o.label}`).join(''); document.getElementById('isp-capacity-card').classList.add('hidden'); } else { btn6.classList.replace('text-slate-400', 'text-indigo-600'); btn6.classList.replace('bg-transparent', 'bg-white'); btn6.classList.add('shadow-sm'); btn4.classList.replace('text-indigo-600', 'text-slate-400'); btn4.classList.replace('bg-white', 'bg-transparent'); btn4.classList.remove('shadow-sm');hint.textContent = "Ej: 2001:db8::/32 (ISP) o /48 (Corp)"; rootInput.placeholder = "Ej: 2001:db8::/32"; lblQty.textContent = "Prefijo / Tamaño"; thReq.textContent = "Tamaño (Prefijo)"; select.innerHTML = IPV6_OPTS.map(o => `${o.label}`).join(''); } updateHostInput(); }function updateHostInput() { const type = document.getElementById('use-case').value; const hostInput = document.getElementById('sub-hosts'); const autoMsg = document.getElementById('auto-msg'); const badge = document.getElementById('ipv6-prefix-badge'); let val = ""; let disabled = false; let showMsg = false;if (currentVersion === 'ipv4') { badge.classList.add('hidden'); hostInput.type = "number"; switch(type) { case 'p2p_cable': val = 2; disabled = true; showMsg = true; break; case 'p2p_wifi': val = 4; disabled = true; showMsg = true; break; case 'loopback': val = 1; disabled = true; showMsg = true; break; case 'wan': val = 4; disabled = true; showMsg = true; break; default: val = ""; disabled = false; showMsg = false; } } else { hostInput.type = "text"; disabled = true; showMsg = true; badge.classList.remove('hidden');const opt = IPV6_OPTS.find(o => o.val === type); if(opt) { val = opt.prefix; badge.textContent = `/${opt.prefix}`; } }hostInput.value = val; hostInput.disabled = disabled; if(disabled) { hostInput.classList.add('bg-slate-100', 'text-slate-500'); if(showMsg) autoMsg.classList.remove('hidden'); } else { hostInput.classList.remove('bg-slate-100', 'text-slate-500'); autoMsg.classList.add('hidden'); } }function addSubnet() { const name = document.getElementById('sub-name').value.trim() || `Segmento ${requestedSubnets.length + 1}`; const type = document.getElementById('use-case').value; const inputVal = document.getElementById('sub-hosts').value; let dataVal = 0; if (currentVersion === 'ipv4') { dataVal = parseInt(inputVal); if (isNaN(dataVal) || dataVal s.id !== id); renderRequestList(); }function clearSubnets() { requestedSubnets = []; renderRequestList(); document.getElementById('results-area').classList.add('hidden'); document.getElementById('isp-capacity-card').classList.add('hidden'); }function renderRequestList() { const tbody = document.getElementById('subnet-req-list'); tbody.innerHTML = ""; if (requestedSubnets.length === 0) { tbody.innerHTML = `No hay segmentos en la cola.`; return; }requestedSubnets.forEach(sub => { let typeLabel = ""; if(currentVersion === 'ipv4') { typeLabel = IPV4_OPTS.find(o=>o.val === sub.type)?.label || 'Custom'; } else { typeLabel = IPV6_OPTS.find(o=>o.val === sub.type)?.label || 'Custom'; } let sizeDisplay = currentVersion === 'ipv4' ? `${sub.val} Hosts` : `/${sub.val}`;const tr = document.createElement('tr'); tr.className = "border-b border-slate-100 bg-white hover:bg-indigo-50 transition"; tr.innerHTML = `${sub.name}${typeLabel}${sizeDisplay} `; tbody.appendChild(tr); }); }// --- Modals ---function showModal(title, content) { document.getElementById('modal-title').innerHTML = title; document.getElementById('modal-content').innerHTML = content; const modal = document.getElementById('topology-modal'); modal.classList.remove('hidden'); modal.classList.add('flex'); }function closeModal() { const modal = document.getElementById('topology-modal'); modal.classList.add('hidden'); modal.classList.remove('flex'); }// --- UPDATED: Detailed Topology Info --- function showIpv4Info(netAddr, mask, type) { // Re-calculate details internally to be robust const netLong = ip2long(netAddr); const totalSize = Math.pow(2, 32 - mask); // Usable Range const firstUsableLong = netLong + 1; const lastUsableLong = netLong + totalSize - 2; const broadcastLong = netLong + totalSize - 1;let contentHtml = "";if (type === 'p2p_wifi') { // Topology: Router A (1st), Radio A (2nd), Radio B (Penultimate), Router B (Last) const rtrA = long2ip(firstUsableLong); const radioA = long2ip(firstUsableLong + 1); const radioB = long2ip(lastUsableLong - 1); const rtrB = long2ip(lastUsableLong);contentHtml = `

Topología Wireless

  • Router A (Origen) ${rtrA}
  • Radio A (AP) ${radioA}
  • Radio B (CPE) ${radioB}
  • Router B (Destino) ${rtrB}
Mácara de Subred: /${mask} (${long2ip(-1 << (32 - mask))})
`; } else if (type === 'p2p_cable') { // Topology: Equip A (1st), Equip B (Last) const eqA = long2ip(firstUsableLong); const eqB = long2ip(lastUsableLong);contentHtml = `

Enlace Fibra / Cable

  • Equipo A (Origen) ${eqA}
  • Equipo B (Destino) ${eqB}
Mácara de Subred: /${mask} (${long2ip(-1 << (32 - mask))})
`; } else { // Standard Logic contentHtml = `

Red

${netAddr}/${mask}

Máscara

/${mask}

  • Gateway (Sug.): ${long2ip(firstUsableLong)}
  • Broadcast: ${long2ip(broadcastLong)}
  • Hosts Útiles: ${totalSize - 2}
Rango usable: ${long2ip(firstUsableLong)} - ${long2ip(lastUsableLong)}
`; } showModal("Detalle IPv4 VLSM", contentHtml); }function showIpv6Info(net, prefix, type) { let desc = ""; let icon = ""; if (prefix === 48) { desc = "Prefijo estándar para Sitio Corporativo."; icon = "fa-building"; } else if (prefix === 56) { desc = "Prefijo estándar para Pymes o Residencial."; icon = "fa-house-signal"; } else if (prefix === 64) { desc = "Unidad atómica de red IPv6 (LAN)."; icon = "fa-network-wired"; } else if (prefix === 127) { desc = "Enlace Punto a Punto (P2P)."; icon = "fa-arrows-left-right"; }const html = `

Bloque /${prefix}

${desc}

Segmento Calculado

${net}

`; showModal("Detalle de Delegación IPv6", html); }// --- Core Calc Logic ---function calculateCapacity(rootCidr) { const calc = (target) => { if(rootCidr > target) return 0; return Math.pow(2, target - rootCidr).toLocaleString(); }; document.getElementById('cap-root').textContent = `/${rootCidr}`; document.getElementById('cap-48').textContent = calc(48); document.getElementById('cap-56').textContent = calc(56); document.getElementById('cap-64').textContent = calc(64); document.getElementById('isp-capacity-card').classList.remove('hidden'); }function calculateVLSM() { const rootInput = document.getElementById('root-network').value.trim(); const errBox = document.getElementById('error-msg'); const resArea = document.getElementById('results-area'); const tbody = document.getElementById('results-body');errBox.classList.add('hidden'); resArea.classList.add('hidden'); tbody.innerHTML = "";if (!rootInput || requestedSubnets.length === 0) { document.getElementById('error-text').textContent = "Ingresa la red base y al menos un segmento."; errBox.classList.remove('hidden'); return; }try { if (currentVersion === 'ipv4') { // --- IPv4 Logic Fixed --- const cidrSplit = rootInput.split('/'); if(cidrSplit.length !== 2) throw new Error("Formato inválido. Usa: x.x.x.x/máscara (Ej: 192.168.1.0/24)");let ipLong = ip2long(cidrSplit[0]); let rootMask = parseInt(cidrSplit[1]);if (ipLong === -1) throw new Error("Dirección IP mal formada (Revisa octetos 0-255)."); if (isNaN(rootMask) || rootMask 32) throw new Error("Máscara CIDR inválida (0-32)."); const totalHosts = Math.pow(2, 32 - rootMask); const hostBits = 32 - rootMask; const clearMask = (0xFFFFFFFF <>> 0; const startIp = (ipLong & clearMask) >>> 0;let currentIp = startIp; const poolEnd = startIp + totalHosts;let sortedV4 = [...requestedSubnets].sort((a, b) => b.val - a.val);sortedV4.forEach(sub => { let needed = sub.val + 2; let power = 0; while(Math.pow(2, power) poolEnd) { throw new Error(`Espacio insuficiente en la red raíz para el segmento: ${sub.name} (Requiere ${blockSize} IPs)`); }const netAddr = long2ip(currentIp); const broadcast = long2ip(currentIp + blockSize - 1); const gateway = long2ip(currentIp + 1); const rangeStr = `${long2ip(currentIp + 1)} - ${long2ip(currentIp + blockSize - 2)}`;const tr = document.createElement('tr'); tr.className = "hover:bg-indigo-50 transition border-b border-slate-100"; tr.innerHTML = `${sub.name}${netAddr}/${newMask}
GW: ${gateway} Rango: ${rangeStr} Hosts útiles: ${blockSize - 2}
`; tbody.appendChild(tr);currentIp += blockSize; });document.getElementById('res-protocol-badge').textContent = "IPV4 VLSM"; resArea.classList.remove('hidden');} else { // --- IPv6 Logic --- const parts = rootInput.split('/'); if(parts.length !== 2) throw new Error("Formato IPv6 inválido (addr/prefix)"); const rootAddr = parts[0]; const rootCidr = parseInt(parts[1]); if (isNaN(rootCidr) || rootCidr > 128) throw new Error("Prefijo CIDR inválido"); calculateCapacity(rootCidr); let currentBigInt = parseIPv6(rootAddr); let sorted = [...requestedSubnets].sort((a, b) => a.val - b.val); sorted.forEach(sub => { const subPrefix = sub.val; if (subPrefix < rootCidr) throw new Error(`El sub-segmento /${subPrefix} es más grande que la red raíz /${rootCidr}`); const shift = BigInt(128 - subPrefix); const increment = 1n << shift; let netAddrStr = formatIPv6(currentBigInt); let rangeEnd = currentBigInt + increment - 1n; const tr = document.createElement('tr'); tr.className = "hover:bg-indigo-50 transition border-b border-slate-100"; tr.innerHTML = `${sub.name}${netAddrStr}/${subPrefix}
Start: ${netAddrStr} End:   ${formatIPv6(rangeEnd)}
`; tbody.appendChild(tr); currentBigInt += increment; }); document.getElementById('res-protocol-badge').textContent = "IPV6 ISP MODE"; resArea.classList.remove('hidden'); } } catch (e) { document.getElementById('error-text').textContent = e.message; errBox.classList.remove('hidden'); } } setVersion('ipv4');
Scroll al inicio