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 (
)
}
}
export default CIDRCalculator