import React from "react" import IPInput from "./IPInput" import RFCLink from "./RFCLink" import { Address4, Address6 } from "ip-address" import { BigInteger } from "jsbn" import styles from "./CIDRCalculator.module.css" const IP4IN6SUBNET = '64:ff9b::/96' const IPv6TYPEEXTS = { '::1/128': (Loopback), '::/128': (Unspecified), '::ffff:0:0/96': (IPv4 Mapped), '64:ff9b::/96': (IPv4-IPv6 Translation), '100::/64': (Discard-Only Address Block), '2001::/32': (TEREDO Tunnelling), '2001:1::1/128': (Port Control Protocol Anycast), '2001:1::2/128': (Traversal Using Relays around NAT Anycast), '2001:2::/48': (Benchmarking), '2001:3::/32': (AMT), '2001:4:112::/48': (AS112-v6), '2001:5::/32': (EID Space for LISP), '2001:10::/28': (Deprecated (previously ORCHID)), '2001:20::/28': (ORCHIDv2), '2002::/16': (6to4), '2620:4f:8000::/48': (Direct Delegation AS112 Service), 'fc00::/7': (Unique-Local), 'fe80::/10': (Linked-Scoped Unicast), } const IPv4TYPES = { '0.0.0.0/8': (Broadcast to "this"), '10.0.0.0/8': (Private network), '100.64.0.0/10': (Carrier-grade NAT private network), '127.0.0.0/8': (Loopback), '169.254.0.0/16': (Link-local), '172.16.0.0/12': (Private network), '192.0.0.0/24': (IANA IPv4 Special Purpose Address Registry), '192.0.2.0/24': ("TEST-NET" for documentation/examples), '192.88.99.0/24': (6to4 anycast relays), '192.168.0.0/16': (Private network), '198.18.0.0/15': (Testing networking equipment), '198.51.100.0/24': ("TEST-NET-2" for documentation/examples), '203.0.113.0/24': ("TEST-NET-3" for documentation/examples), '224.0.0.0/4': (Multicast), '240.0.0.0/4': (Future use), '255.255.255.255/32': ("Limited broadcast" destination), } const find = (collection, predicate) => { for (const key of Object.getOwnPropertyNames(collection)) { const item = collection[key] if (predicate(item, key)) return item } } const v6Type = (v6) => { const isType = (name, type) => v6.isInSubnet(new Address6(type)) let type = v6.getType() if (type !== 'Global unicast') return type return find(IPv6TYPEEXTS, isType) || type } const v4Type = (v4) => { const isType = (name, type) => v4.isInSubnet(new Address4(type)) return find(IPv4TYPES, isType) || 'Global unicast' } const v4BitsToMask = (subnetMask) => { let n = 0 for (let i = 0; i < subnetMask; i++) { n = (n << 1) | 1 } n <<= 32 - subnetMask const f = (b) => ((n >> b) & 0xff).toString(10) return `${f(24)}.${f(16)}.${f(8)}.${f(0)}` } const highlightElement = (e) => { const range = document.createRange() range.selectNodeContents(e) const sel = window.getSelection() sel.removeAllRanges() sel.addRange(range) } const highlight = (ev) => highlightElement(ev.target) const highlightNext = (ev) => highlightElement(ev.target.nextElementSibling) const toDL = (pairs) => (
{pairs.map(pair => ( ([
{pair[0]}
,
{pair[1]}
]) ))}
) class CIDRCalculator extends React.Component { constructor(props) { super(props) this.state = { } this.handleIPChange = this.handleIPChange.bind(this) this.highlight = this.highlight.bind(this) } handleIPChange(value) { this.setState({ ip: value }) } highlight(event) { const range = document.createRange() range.selectNodeContents(event.target) const sel = window.getSelection() sel.removeAllRanges() sel.addRange(range) } renderIP(ip) { if (!ip) return [] if (ip.to4) return this.renderIPv6(ip) return this.renderIPv4(ip) } renderIPv6(ip) { const meaningful4in6 = ip.isInSubnet(new Address6(IP4IN6SUBNET)) let info = [ ['IPv6 Address', ip.correctForm()], ['Type', v6Type(ip)], ['Prefix Size', ip.subnet], ['Scope', ip.getScope()], [`4-in-6${meaningful4in6 ? '' : ' (probably meaningless)'}`, ip.to4().correctForm()], ] if (ip.getBits(88, 104).toString(16) === 'fffe') { const highBitsPreFix = ip.getBits(64, 88) const lowBits = ip.getBits(104, 128) const highBits = highBitsPreFix.xor(new BigInteger((1 << 17).toString(10))) const mac = highBits.shiftLeft(new BigInteger('24')).or(lowBits) const mask = new BigInteger('255') info.push(['Embedded MAC Address', [...Array(6).keys()].map((n) => { return mac.shiftRight(new BigInteger((8 * 5 - n * 8).toString())).and(mask).toString(16).padStart(2, '0') }).join(':')]) } if (ip.subnetMask <= 126) { const firstAddress = ip.startAddress() const lastAddress = ip.endAddress() info = info.concat([ ['First Usable Address', firstAddress.correctForm()], ['Last Usable Address', lastAddress.correctForm()], ['Usable Addresses', lastAddress.bigInteger().subtract(firstAddress.bigInteger()).toString()], ]) } return toDL(info) } renderIPv4(ip) { const netAddress = ip.startAddress() const firstAddress = Address4.fromBigInteger(netAddress.bigInteger().add(BigInteger.ONE)) const broadcastAddress = ip.endAddress() const lastAddress = Address4.fromBigInteger(broadcastAddress.bigInteger().subtract(BigInteger.ONE)) let info = [ ['IPv4 Address', ip.correctForm()], ['Type', v4Type(ip)], ['Prefix Size', ip.subnet], ['Subnet Mask', v4BitsToMask(ip.subnetMask)], ] if (ip.subnetMask <= 30) info = info.concat([ ['Network Address', netAddress.correctForm()], ['First Usable Address', firstAddress.correctForm()], ['Last Usable Address', lastAddress.correctForm()], ['Broadcast Address', broadcastAddress.correctForm()], ['Usable Addresses', lastAddress.bigInteger().subtract(firstAddress.bigInteger()).toString()], ]) else if (ip.subnetMask == 31) info = info.concat([ ['First Usable Address (point-to-point)', netAddress.correctForm()], ['Last Usable Address (point-to-point)', firstAddress.correctForm()], ]) return toDL(info) } render() { return (
{this.renderIP(this.state.ip)}
) } } export default CIDRCalculator