diff --git a/public/calculators/subnet.js b/public/calculators/subnet.js index 8986937..c101204 100644 --- a/public/calculators/subnet.js +++ b/public/calculators/subnet.js @@ -142,6 +142,328 @@ export default { return 'Unknown'; } + function getRFCNetworkInfo(ip, cidr) { + const ipLong = ipToLong(ip); + const parts = ip.split('.'); + const firstOctet = parseInt(parts[0]); + const secondOctet = parseInt(parts[1]); + + // Check for specific RFC-defined ranges + if (firstOctet === 0) { + return { + type: 'Current Network', + description: 'RFC 1122: "This host on this network" - used only as source address', + rfc: 'RFC 1122', + cidr: '0.0.0.0/8' + }; + } + + if (firstOctet === 10) { + return { + type: 'Private Network', + description: 'RFC 1918: Private IP address range for Class A networks', + rfc: 'RFC 1918', + cidr: '10.0.0.0/8' + }; + } + + if (firstOctet === 127) { + return { + type: 'Loopback', + description: 'RFC 1122: Loopback addresses - packets sent to this address are processed locally', + rfc: 'RFC 1122', + cidr: '127.0.0.0/8' + }; + } + + if (firstOctet === 169 && secondOctet === 254) { + return { + type: 'Link-Local', + description: 'RFC 3927: Automatic Private IP Addressing (APIPA) - used when DHCP fails', + rfc: 'RFC 3927', + cidr: '169.254.0.0/16' + }; + } + + if (firstOctet === 172 && secondOctet >= 16 && secondOctet <= 31) { + return { + type: 'Private Network', + description: 'RFC 1918: Private IP address range for Class B networks', + rfc: 'RFC 1918', + cidr: '172.16.0.0/12' + }; + } + + if (firstOctet === 192 && secondOctet === 168) { + return { + type: 'Private Network', + description: 'RFC 1918: Private IP address range for Class C networks', + rfc: 'RFC 1918', + cidr: '192.168.0.0/16' + }; + } + + if (firstOctet === 192 && secondOctet === 0 && parseInt(parts[2]) === 0) { + return { + type: 'IETF Protocol Assignments', + description: 'RFC 5736: Reserved for IETF protocol assignments', + rfc: 'RFC 5736', + cidr: '192.0.0.0/24' + }; + } + + if (firstOctet === 192 && secondOctet === 0 && parseInt(parts[2]) === 2) { + return { + type: 'Test-Net', + description: 'RFC 5737: Documentation and example code (TEST-NET-1)', + rfc: 'RFC 5737', + cidr: '192.0.2.0/24' + }; + } + + if (firstOctet === 192 && secondOctet === 88 && parseInt(parts[2]) === 99) { + return { + type: '6to4 Relay', + description: 'RFC 3068: IPv6 to IPv4 relay anycast addresses', + rfc: 'RFC 3068', + cidr: '192.88.99.0/24' + }; + } + + if (firstOctet === 198 && secondOctet === 18) { + return { + type: 'Benchmark Testing', + description: 'RFC 2544: Network interconnect device benchmark testing', + rfc: 'RFC 2544', + cidr: '198.18.0.0/15' + }; + } + + if (firstOctet === 198 && secondOctet === 51 && parseInt(parts[2]) === 100) { + return { + type: 'Test-Net', + description: 'RFC 5737: Documentation and example code (TEST-NET-2)', + rfc: 'RFC 5737', + cidr: '198.51.100.0/24' + }; + } + + if (firstOctet === 203 && secondOctet === 0 && parseInt(parts[2]) === 113) { + return { + type: 'Test-Net', + description: 'RFC 5737: Documentation and example code (TEST-NET-3)', + rfc: 'RFC 5737', + cidr: '203.0.113.0/24' + }; + } + + if (firstOctet === 100 && secondOctet >= 64 && secondOctet <= 127) { + return { + type: 'CGNAT', + description: 'RFC 6598: Carrier-Grade NAT (CGN) shared address space', + rfc: 'RFC 6598', + cidr: '100.64.0.0/10' + }; + } + + if (firstOctet >= 224 && firstOctet <= 239) { + return { + type: 'Multicast', + description: 'RFC 1112: Multicast addresses - used for one-to-many communication', + rfc: 'RFC 1112', + cidr: '224.0.0.0/4' + }; + } + + if (firstOctet >= 240 && firstOctet <= 255) { + return { + type: 'Reserved', + description: 'RFC 1112: Reserved for future use (formerly Class E)', + rfc: 'RFC 1112', + cidr: '240.0.0.0/4' + }; + } + + if (ipLong === 0xFFFFFFFF) { + return { + type: 'Broadcast', + description: 'RFC 919: Limited broadcast address - reaches all hosts on the local network', + rfc: 'RFC 919' + }; + } + + // Check for other broadcast addresses based on CIDR + if (cidr < 32) { + const networkLong = (ipLong & ((0xFFFFFFFF << (32 - cidr)) >>> 0)) >>> 0; + const broadcastLong = (networkLong | (~((0xFFFFFFFF << (32 - cidr)) >>> 0)) >>> 0) >>> 0; + if (ipLong === broadcastLong) { + return { + type: 'Network Broadcast', + description: `Broadcast address for /${cidr} network - reaches all hosts in this subnet`, + rfc: 'RFC 919' + }; + } + } + + // Default case - public IP + return { + type: 'Public IP', + description: 'Globally routable IP address on the Internet', + rfc: null + }; + } + + function getIPv6NetworkInfo(ipv6, cidr) { + const expanded = expandIPv6(ipv6); + const parts = expanded.split(':'); + + // Convert to BigInt for comparison + let ipv6Long = 0n; + for (let i = 0; i < 8; i++) { + const part = parseInt(parts[i], 16); + ipv6Long = (ipv6Long << 16n) + BigInt(part); + } + + // Check for specific IPv6 reserved ranges + + // ::/128 - Unspecified address + if (ipv6Long === 0n) { + return { + type: 'Unspecified', + description: 'RFC 4291: Unspecified address - used only as source address', + rfc: 'RFC 4291', + cidr: '::/128' + }; + } + + // ::1/128 - Loopback + if (ipv6Long === 1n) { + return { + type: 'Loopback', + description: 'RFC 4291: Loopback address - equivalent to IPv4 127.0.0.1', + rfc: 'RFC 4291', + cidr: '::1/128' + }; + } + + // 2000::/3 - Global Unicast (public IPv6) + if ((ipv6Long >> 125n) === 4n) { // First 3 bits are 001 + return { + type: 'Global Unicast', + description: 'RFC 4291: Globally routable IPv6 addresses (public Internet)', + rfc: 'RFC 4291', + cidr: '2000::/3' + }; + } + + // fc00::/7 - Unique Local Address (ULA) + if ((ipv6Long >> 121n) === 0x7Dn) { // First 7 bits are 1111110 + return { + type: 'Unique Local Address', + description: 'RFC 4193: Private IPv6 addresses (equivalent to IPv4 private ranges)', + rfc: 'RFC 4193', + cidr: 'fc00::/7' + }; + } + + // fe80::/10 - Link-Local + if ((ipv6Long >> 118n) === 0xFE80n >> 6n) { // First 10 bits are 1111111010 + return { + type: 'Link-Local', + description: 'RFC 4291: Link-local addresses - valid only on the local network segment', + rfc: 'RFC 4291', + cidr: 'fe80::/10' + }; + } + + // ff00::/8 - Multicast + if ((ipv6Long >> 120n) === 0xFFn) { // First 8 bits are 11111111 + return { + type: 'Multicast', + description: 'RFC 4291: IPv6 multicast addresses - used for one-to-many communication', + rfc: 'RFC 4291', + cidr: 'ff00::/8' + }; + } + + // 2001:db8::/32 - Documentation + if ((ipv6Long >> 96n) === 0x20010DB8n) { + return { + type: 'Documentation', + description: 'RFC 3849: Reserved for documentation and example code', + rfc: 'RFC 3849', + cidr: '2001:db8::/32' + }; + } + + // 2001::/32 - Teredo + if ((ipv6Long >> 96n) === 0x20010000n) { + return { + type: 'Teredo', + description: 'RFC 4380: Teredo tunneling - IPv6 over UDP over IPv4', + rfc: 'RFC 4380', + cidr: '2001::/32' + }; + } + + // 2002::/16 - 6to4 + if ((ipv6Long >> 112n) === 0x2002n) { + return { + type: '6to4', + description: 'RFC 3056: 6to4 tunneling - automatic IPv6 over IPv4 tunneling', + rfc: 'RFC 3056', + cidr: '2002::/16' + }; + } + + // 64:ff9b::/96 - IPv4-IPv6 Translation + if ((ipv6Long >> 96n) === 0x64FF9B000000000000000000n) { + return { + type: 'IPv4-IPv6 Translation', + description: 'RFC 6052: Well-known prefix for IPv4-IPv6 translation', + rfc: 'RFC 6052', + cidr: '64:ff9b::/96' + }; + } + + // 100::/64 - Discard-Only + if ((ipv6Long >> 64n) === 0x100000000000000n) { + return { + type: 'Discard-Only', + description: 'RFC 6666: Discard-only address block - packets are discarded', + rfc: 'RFC 6666', + cidr: '100::/64' + }; + } + + // ::ffff:0:0/96 - IPv4-mapped IPv6 + if ((ipv6Long >> 96n) === 0xFFFF00000000n) { + return { + type: 'IPv4-mapped IPv6', + description: 'RFC 4291: IPv4 addresses mapped to IPv6 format', + rfc: 'RFC 4291', + cidr: '::ffff:0:0/96' + }; + } + + // ::/96 - IPv4-compatible IPv6 (deprecated) + if ((ipv6Long >> 96n) === 0n && (ipv6Long & 0xFFFFFFFF00000000n) === 0n) { + return { + type: 'IPv4-compatible IPv6', + description: 'RFC 4291: IPv4-compatible IPv6 addresses (deprecated)', + rfc: 'RFC 4291', + cidr: '::/96' + }; + } + + // Default case - other reserved or unknown + return { + type: 'Reserved/Unknown', + description: 'Reserved or unassigned IPv6 address range', + rfc: null + }; + } + function ipToLong(ip) { return ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet), 0) >>> 0; } @@ -375,19 +697,52 @@ export default { const networkSize = Math.pow(2, 32 - cidr); const baseLong = ipToLong(baseIP); - // Calculate the base network for the IP (up to /16 level) - // For example: 192.168.1.0 -> 192.168.0.0 (base /16 network) - const baseNetworkLong = baseLong & ((0xFFFFFFFF << (32 - 16)) >>> 0); - const networkLong = baseNetworkLong; + // Calculate the base network for the IP based on the CIDR + // For larger subnets (CIDR < 16), we need to find the appropriate base network + let baseNetworkLong; + let maxNetworks; - // Show up to 64 networks - const count = Math.min(64, Math.floor(65536 / networkSize)); + if (cidr <= 8) { + // For /8 and larger, use /8 as the base (Class A) + baseNetworkLong = baseLong & ((0xFFFFFFFF << (32 - 8)) >>> 0); + maxNetworks = Math.min(64, Math.floor(16777216 / networkSize)); // 2^24 / networkSize + } else if (cidr <= 16) { + // For /9 to /16, use /16 as the base (Class B) + baseNetworkLong = baseLong & ((0xFFFFFFFF << (32 - 16)) >>> 0); + maxNetworks = Math.min(64, Math.floor(65536 / networkSize)); // 2^16 / networkSize + } else { + // For /17 and smaller, use the actual network as base + baseNetworkLong = baseLong & ((0xFFFFFFFF << (32 - cidr)) >>> 0); + maxNetworks = Math.min(64, Math.floor(65536 / networkSize)); + } + + // Show up to 64 networks, but limit based on what makes sense + const count = Math.min(maxNetworks, 64); for (let i = 0; i < count; i++) { - const networkAddr = networkLong + (i * networkSize); + const networkAddr = baseNetworkLong + (i * networkSize); const broadcastAddr = networkAddr + networkSize - 1; - const firstHost = networkAddr + 1; - const lastHost = broadcastAddr - 1; + + // Handle edge cases for host calculations + let firstHost, lastHost; + + if (cidr === 32) { + // /32 - single host, no usable hosts + firstHost = networkAddr; + lastHost = networkAddr; + } else if (cidr === 31) { + // /31 - point-to-point, no usable hosts + firstHost = networkAddr; + lastHost = broadcastAddr; + } else if (cidr === 30) { + // /30 - 4 total hosts, 2 usable + firstHost = networkAddr + 1; + lastHost = broadcastAddr - 1; + } else { + // Normal case - calculate usable hosts + firstHost = networkAddr + 1; + lastHost = broadcastAddr - 1; + } networks.push({ network: longToIp(networkAddr), @@ -452,13 +807,27 @@ export default { lastHostLong = broadcastLong - 1; } - // Calculate total possible networks + // Calculate total possible networks based on CIDR const networkSize = Math.pow(2, 32 - calculatedCidr); - const totalPossibleNetworks = Math.floor(65536 / networkSize); + let totalPossibleNetworks; + + if (calculatedCidr <= 8) { + // For /8 and larger, calculate based on Class A space + totalPossibleNetworks = Math.floor(16777216 / networkSize); // 2^24 / networkSize + } else if (calculatedCidr <= 16) { + // For /9 to /16, calculate based on Class B space + totalPossibleNetworks = Math.floor(65536 / networkSize); // 2^16 / networkSize + } else { + // For /17 and smaller, use the standard calculation + totalPossibleNetworks = Math.floor(65536 / networkSize); + } // Generate available networks table const availableNetworks = generateAvailableNetworks(ipAddress, calculatedCidr); + // Get RFC network information + const rfcInfo = getRFCNetworkInfo(ipAddress, calculatedCidr); + out.innerHTML = `