mirror of
https://git.burble.com/burble.dn42/dn42regsrv.git
synced 2024-02-26 20:28:04 +01:00
Add Free Explorer functionality
This commit is contained in:
parent
22234d8918
commit
a88cf6d47a
237
StaticRoot/free
Normal file
237
StaticRoot/free
Normal file
@ -0,0 +1,237 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>DN42 Free Explorer</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
<link rel="stylesheet" href="bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="material-icons.css"/>
|
||||
<!-- Style overrides -->
|
||||
<style>
|
||||
.material-icons { display:inline-flex;vertical-align:middle }
|
||||
body { box-shadow: inset 0 2em 10em rgba(0,0,0,0.4); min-height: 100vh }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="free-app">
|
||||
|
||||
<nav class="navbar navbar-fixed-top navbar-expand-md navbar-dark bg-dark">
|
||||
<router-link to="/4" class="btn btn-secondary mx-2">IPv4</router-link>
|
||||
<router-link to="/6" class="btn btn-secondary mx-2">IPv6</router-link>
|
||||
<router-link to="/asn" class="btn btn-secondary mx-2">ASN</router-link>
|
||||
<div class="collapse navbar-collapse w-100 ml-auto text-nowrap">
|
||||
<div class="ml-auto"><router-link class="navbar-brand"
|
||||
to="/">Free Explorer</router-link> <a class="navbar-brand"
|
||||
href="/">Registry Explorer</a> <a class="pull-right navbar-brand"
|
||||
href="https://dn42.dev/"><img src="/dn42_logo.png" width="173" height="60"/></a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<router-view></router-view>
|
||||
|
||||
</div>
|
||||
<footer class="page-footer font-small">
|
||||
<div style="margin-top: 20px; padding: 5px">
|
||||
<a href="https://git.burble.com/burble.dn42/dn42regsrv">Source Code</a>.
|
||||
Powered by
|
||||
<a href="https://getbootstrap.com/">Bootstrap</a>,
|
||||
<a href="https://vuejs.org">Vue.js</a>,
|
||||
<a href="https://github.com/axios/axios">axios</a>,
|
||||
<a href="http://alexcorvi.github.io/anchorme.js/">Anchorme</a>.
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script type="text/x-template" id="app-free4-template">
|
||||
<div class="p-5">
|
||||
<p>Select prefix length:
|
||||
<span class="btn-group mx-3" role="group" aria-label="Prefix Length" id="prefixselect">
|
||||
<button @click="updatePrefixLen" value="24" type="button" class="btn btn-secondary">/24</button>
|
||||
<button @click="updatePrefixLen" value="25" type="button" class="btn btn-secondary">/25</button>
|
||||
<button @click="updatePrefixLen" value="26" type="button" class="btn btn-secondary">/26</button>
|
||||
<button @click="updatePrefixLen" value="27" type="button" class="btn btn-secondary">/27</button>
|
||||
<button @click="updatePrefixLen" value="28" type="button" class="btn btn-secondary">/28</button>
|
||||
<button @click="updatePrefixLen" value="29" type="button" class="btn btn-secondary">/29</button>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<div class="w-auto d-inline-block px-4">
|
||||
<section v-if="filter == 29">
|
||||
<p class="alert alert-danger">A /29 is a very small allocation,
|
||||
providing only 8 IP addresses.<br/>
|
||||
Whilst you will likely only need one IPv4 address per host a more
|
||||
typical allocation in DN42 is a /27.</p>
|
||||
</section>
|
||||
<section v-else-if="filter == 28">
|
||||
<p class="alert alert-success">/28 provides only 16 IPv4 addresses, but may be suitable
|
||||
for small networks.</p>
|
||||
</section>
|
||||
<section v-else-if="filter == 27">
|
||||
<p class="alert alert-success">A /27 is the typical allocation in DN42 providing
|
||||
you with 32 IPv4 addresses.</p>
|
||||
</section>
|
||||
<section v-else-if="filter == 26">
|
||||
<p class="alert alert-info">A /26 would provide 64 IPv4 addresses
|
||||
and would be enough for most large networks.</p>
|
||||
</section>
|
||||
<section v-else-if="filter == 25">
|
||||
<p class="alert alert-warning">A /25 is considered a very
|
||||
large allocation in DN42.<br/>Typically you only need one
|
||||
IPv4 per host and should consider carefully before requesting such a big allocation.</p>
|
||||
</section>
|
||||
<section v-else-if="filter == 24">
|
||||
<p class="alert alert-danger">/24 is a huge allocation in DN42
|
||||
and is likely unecessary unless you have special requirements or are an organisation.
|
||||
You may request a /24 prefix, but expect to provide justification to the registry maintainers.
|
||||
DN42 also has address ranges specifically reserved for organisations or large
|
||||
allocations. See the
|
||||
<a href="https://dn42.dev/howto/Address-Space">DN42 Wiki</a> for more details.</p>
|
||||
</section>
|
||||
</div>
|
||||
<p>
|
||||
{{ ftotal }} free /{{ filter }} prefixes found, showing up to 10 random results<br/>
|
||||
Select the prefix size again to get more networks.
|
||||
</p>
|
||||
|
||||
<div class="m-5">
|
||||
<pre v-for="prefix in filtered" class="text-center my-1"
|
||||
style="font-size: 1.2rem">{{ prefix }}</pre>
|
||||
</div>
|
||||
|
||||
<div class="container d-flex flex-column w-75">
|
||||
<section v-if="state == 'error'">
|
||||
<div class="alert alert-warning clearfix" role="alert">
|
||||
An error recurred whilst retrieving data from the API
|
||||
<button type="button" class="float-right btn btn-primary"
|
||||
v-on:click="reload"><span class="material-icons">refresh</span>
|
||||
Refresh</button>
|
||||
</div>
|
||||
</section>
|
||||
<section v-else-if="state == 'loading'">
|
||||
<div class="alert alert-info" role="alert">Loading data ...</div>
|
||||
</section>
|
||||
<section v-else>
|
||||
<div class="text-center w-100"><span
|
||||
class="mx-3"><b>IPv4</b><span><span
|
||||
class="mx-3">Inetnum: {{ stats.alloc }}</span><span
|
||||
class="mx-3">Free net blocks: {{ stats.nets }}</span><span
|
||||
class="mx-3">Available IPs: {{ stats.addr }} ({{
|
||||
Math.round((stats.addr/262144)*100) }}%)</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/x-template" id="app-free6-template">
|
||||
<div class="p-5">
|
||||
<p>
|
||||
The recommended IPv6 prefix size in DN42 is a /48 which provides plenty of address
|
||||
space for a multi-site, global network.<br/>Smaller networks ranges are not advised
|
||||
as they are likely to limit the future growth of your network.
|
||||
</p>
|
||||
<p>
|
||||
The DN42 registry is not authoritative across the whole fd00::/8 range.<br/>It is
|
||||
recommended to generate an RFC4193 compliant random prefix to reduce the risk of
|
||||
clashing with another network.
|
||||
<p>
|
||||
Creating 10 random IPv6 /48 prefixes
|
||||
<button type="button" class="mx-3 btn btn-primary" @click="generate">More !</button>
|
||||
</p>
|
||||
|
||||
<div class="m-5">
|
||||
<pre v-for="prefix in plist" class="text-center my-1"
|
||||
style="font-size: 1.2rem">{{ prefix }}</pre>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="container d-flex flex-column w-75">
|
||||
<section v-if="state == 'error'">
|
||||
<div class="alert alert-warning clearfix" role="alert">
|
||||
An error recurred whilst retrieving data from the API
|
||||
<button type="button" class="float-right btn btn-primary"
|
||||
v-on:click="reload"><span class="material-icons">refresh</span>
|
||||
Refresh</button>
|
||||
</div>
|
||||
</section>
|
||||
<section v-else-if="state == 'loading'">
|
||||
<div class="alert alert-info" role="alert">Loading data ...</div>
|
||||
</section>
|
||||
<section v-else>
|
||||
<div class="text-center w-100"><span
|
||||
class="mx-3"><b>IPv6</b><span><span
|
||||
class="mx-3">Inet6num: {{ stats.alloc }}</span><span
|
||||
class="mx-3">Assigned /64s: ~{{ stats.nets }}M ({{
|
||||
Math.round(stats.nets*10000/72057594)/100000 }}%)</span><span
|
||||
class="mx-3">Free /64s: ~{{ freenets }}T</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/x-template" id="app-asn-template">
|
||||
<div class="p-5">
|
||||
<p>
|
||||
Showing 10 random free ASNs
|
||||
<button type="button" class="mx-3 btn btn-primary" @click="generate">More !</button>
|
||||
</p>
|
||||
|
||||
<div class="m-5">
|
||||
<pre v-for="prefix in alist" class="text-center my-1"
|
||||
style="font-size: 1.2rem">{{ prefix }}</pre>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="container d-flex flex-column w-75">
|
||||
<section v-if="state == 'error'">
|
||||
<div class="alert alert-warning clearfix" role="alert">
|
||||
An error recurred whilst retrieving data from the API
|
||||
<button type="button" class="float-right btn btn-primary"
|
||||
v-on:click="reload"><span class="material-icons">refresh</span>
|
||||
Refresh</button>
|
||||
</div>
|
||||
</section>
|
||||
<section v-else-if="state == 'loading'">
|
||||
<div class="alert alert-info" role="alert">Loading data ...</div>
|
||||
</section>
|
||||
<section v-else>
|
||||
<div class="text-center w-100"><span
|
||||
class="mx-3"><b>ASN</b><span><span
|
||||
class="mx-3">Allocated: {{ stats.alloc }}</span><span
|
||||
class="mx-3">Available: {{ 4000 - stats.alloc }}
|
||||
({{ Math.round(stats.alloc / 40) }}%)</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/x-template" id="app-root-template">
|
||||
<div class="p-3">
|
||||
<div class="jumbotron">
|
||||
<h1>DN42 IP Explorer</h1>
|
||||
<p class="lead">Select IPv4, IPv6 or ASN to find free blocks and ASNs</p>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
||||
<script src="jquery.slim.min.js"></script>
|
||||
<script src="bootstrap.bundle.min.js"></script>
|
||||
<script src="vue.min.js"></script>
|
||||
<script src="vue-router.min.js"></script>
|
||||
<script src="axios.min.js"></script>
|
||||
<script src="anchorme.min.js"></script>
|
||||
<script src="anchorme.min.js"></script>
|
||||
<script src="free.js"></script>
|
||||
</body>
|
||||
</html>
|
701
StaticRoot/free.js
Normal file
701
StaticRoot/free.js
Normal file
@ -0,0 +1,701 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DN42 IP Explorer
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// root component
|
||||
|
||||
Vue.component('app-root', {
|
||||
template: '#app-root-template',
|
||||
|
||||
methods: {
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// free IPv4 view
|
||||
|
||||
Vue.component('app-free4', {
|
||||
template: '#app-free4-template',
|
||||
data() {
|
||||
return {
|
||||
|
||||
state: 'invalid',
|
||||
error: '',
|
||||
inetnum: null,
|
||||
p4: [ ],
|
||||
free4: [ ],
|
||||
stats: {
|
||||
alloc: 0,
|
||||
addr: 0,
|
||||
nets: 0
|
||||
},
|
||||
|
||||
filter: 27,
|
||||
filtered: [ ],
|
||||
ftotal: 0,
|
||||
|
||||
asn: '4242423999',
|
||||
mntner: 'FOO',
|
||||
person: 'FOO',
|
||||
example: '0.0.0.0/0'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
// not used
|
||||
pretty4(msg, subnet, plen) {
|
||||
var octets = [ ]
|
||||
octets[0] = (subnet >> 24) & 0xFF
|
||||
octets[1] = (subnet >> 16) & 0xFF
|
||||
octets[2] = (subnet >> 8) & 0xFF
|
||||
octets[3] = subnet & 0xFF
|
||||
console.log(msg, ': ', octets.join("."),'/',plen)
|
||||
},
|
||||
|
||||
// reload prefix data
|
||||
reload() {
|
||||
|
||||
// reset current data
|
||||
this.inetnum = null
|
||||
this.p4.splice(0,this.p4.length)
|
||||
this.state = "loading"
|
||||
|
||||
// IPv4 prefixes
|
||||
axios
|
||||
.get('/api/registry/inetnum/*')
|
||||
.then(response => {
|
||||
this.inetnum = response.data
|
||||
this.processIPv4()
|
||||
})
|
||||
.catch(error => {
|
||||
this.error = error
|
||||
this.state = 'error'
|
||||
console.log(error)
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
// parse an IPv4 string in to an object
|
||||
parse4(p) {
|
||||
p = p.replace('_','/')
|
||||
|
||||
// split out prefix
|
||||
var s1 = p.split('/')
|
||||
// and octets
|
||||
var s2 = s1[0].split('.')
|
||||
if (s1.length !=2 || s2.length != 4) {
|
||||
console.log("Failed to IPv4 parse ", p)
|
||||
return null
|
||||
}
|
||||
|
||||
// convert to a 32bit integer
|
||||
var num =
|
||||
((parseInt(s2[0]) << 24) +
|
||||
(parseInt(s2[1]) << 16) +
|
||||
(parseInt(s2[2]) << 8) +
|
||||
parseInt(s2[3]))>>>0
|
||||
|
||||
var plen = parseInt(s1[1])
|
||||
|
||||
var mask = (0xFFFFFFFF - ((2**(32 - plen))-1))>>>0
|
||||
|
||||
// finally return the ipv4 object
|
||||
return {
|
||||
plen: plen,
|
||||
mask: mask,
|
||||
subnet: num
|
||||
}
|
||||
},
|
||||
|
||||
// process IPv4 prefixes
|
||||
processIPv4() {
|
||||
|
||||
var inets = [ ]
|
||||
Object.values(this.inetnum).forEach(rp => {
|
||||
|
||||
// map the attributes in to a hash
|
||||
// no need to worry about duplicated attribs
|
||||
var attrib = { }
|
||||
rp.Attributes.forEach(a => {
|
||||
attrib[a[0]] = a[1]
|
||||
})
|
||||
|
||||
// convert the cidr to an object
|
||||
obj = this.parse4(attrib.cidr)
|
||||
if (obj != null) {
|
||||
if (attrib.policy != null) {
|
||||
obj.policy = attrib.policy
|
||||
}
|
||||
inets.push(obj)
|
||||
}
|
||||
})
|
||||
|
||||
// sort the prefixes in ascending order
|
||||
var sorted = inets.sort((a,b) => {
|
||||
if (a.subnet == b.subnet) {
|
||||
return a.plen - b.plen
|
||||
}
|
||||
else {
|
||||
return a.subnet - b.subnet
|
||||
}
|
||||
})
|
||||
|
||||
// splice in sorted array
|
||||
this.p4.splice(0, this.p4.length, ...sorted)
|
||||
|
||||
// update the free list
|
||||
this.updateFree4()
|
||||
|
||||
this.updatePrefixLen({ target: { value: this.filter } })
|
||||
|
||||
// all done
|
||||
this.state = 'ready'
|
||||
},
|
||||
|
||||
// update ipv4 stats
|
||||
stats4(subnet, plen) {
|
||||
// check DN42 range 172.20.0.0/14
|
||||
var masked = (subnet & 0xFFFC0000)>>>0
|
||||
if (masked == 0xAC140000) {
|
||||
// add em up
|
||||
this.stats.nets += 1
|
||||
this.stats.addr += 2**(32-plen)
|
||||
}
|
||||
},
|
||||
|
||||
// recursively check a subnet for free blocks
|
||||
scanSubnets(subnet, mask, plen) {
|
||||
|
||||
// check for the last defined prefix
|
||||
if (this.ix >= this.p4.length) {
|
||||
this.stats4(subnet, plen)
|
||||
this.free4.push({
|
||||
subnet: subnet,
|
||||
mask: mask,
|
||||
plen: plen
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var prefix = this.p4[this.ix]
|
||||
|
||||
// does the next prefix match this subnet ?
|
||||
if ((prefix.subnet == subnet) && (prefix.plen == plen)) {
|
||||
var policy = 'assigned'
|
||||
if (prefix.policy != null) {
|
||||
policy = prefix.policy
|
||||
}
|
||||
|
||||
if (policy != 'open') {
|
||||
// optimisation to reduce recursion by
|
||||
// scanning forward for open children
|
||||
this.ix += 1
|
||||
|
||||
while(this.ix < this.p4.length) {
|
||||
prefix = this.p4[this.ix]
|
||||
var masked = (prefix.subnet & mask)>>>0
|
||||
if (subnet != masked) {
|
||||
// no longer a child, we're done
|
||||
return
|
||||
}
|
||||
|
||||
// found a subnet that is open
|
||||
if (prefix.policy == 'open') {
|
||||
this.scanSubnets(prefix.subnet,
|
||||
prefix.mask,
|
||||
prefix.plen,
|
||||
prefix.policy)
|
||||
}
|
||||
else {
|
||||
// don't recurse closed subnets
|
||||
var tmppolicy = 'assigned'
|
||||
if (prefix.policy != null) {
|
||||
tmppolicy = prefix.policy
|
||||
}
|
||||
this.ix += 1
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// move on to next index
|
||||
this.ix += 1
|
||||
if (this.ix >= this.p4.length) {
|
||||
this.stats4(subnet, plen)
|
||||
this.free4.push({
|
||||
subnet: subnet,
|
||||
mask: mask,
|
||||
plen: plen
|
||||
})
|
||||
return
|
||||
}
|
||||
prefix = this.p4[this.ix]
|
||||
|
||||
}
|
||||
|
||||
// is the next prefix a subnet of the current search ?
|
||||
var masked = (prefix.subnet & mask)>>>0
|
||||
if (subnet == masked) {
|
||||
|
||||
// split the subnet and try again
|
||||
plen += 1
|
||||
var bit = (2**(32 - plen))>>>0
|
||||
mask = (mask | bit)>>>0
|
||||
|
||||
this.scanSubnets((subnet & (~bit))>>>0, mask, plen)
|
||||
this.scanSubnets((subnet | bit)>>>0, mask, plen)
|
||||
}
|
||||
else {
|
||||
// found an open block
|
||||
this.stats4(subnet, plen)
|
||||
this.free4.push({
|
||||
subnet: subnet,
|
||||
mask: mask,
|
||||
plen: plen
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// update the free blocks list
|
||||
updateFree4() {
|
||||
// reset stats
|
||||
this.stats.alloc = this.p4.length
|
||||
this.stats.addr = 0
|
||||
this.stats.nets = 0
|
||||
|
||||
// recursively scan for free nets
|
||||
this.ix = 0
|
||||
this.scanSubnets(0, 0, 0, 'undefined')
|
||||
},
|
||||
|
||||
// filter subnets based on prefix length
|
||||
filterFree() {
|
||||
|
||||
var tlist = [ ]
|
||||
// filter the free list
|
||||
this.free4.forEach(free => {
|
||||
if (free.plen == this.filter) {
|
||||
tlist.push(free)
|
||||
}
|
||||
})
|
||||
|
||||
this.ftotal = tlist.length
|
||||
|
||||
// pick up to ten random prefixes
|
||||
var result = [ ]
|
||||
for(var i = 0; ((tlist.length > 0) && (i < 10)); i++) {
|
||||
var ix = Math.round(Math.random() * tlist.length)
|
||||
var obj = tlist[ix]
|
||||
|
||||
// push to result
|
||||
var octets = [ ]
|
||||
octets[0] = (obj.subnet >> 24) & 0xFF
|
||||
octets[1] = (obj.subnet >> 16) & 0xFF
|
||||
octets[2] = (obj.subnet >> 8) & 0xFF
|
||||
octets[3] = obj.subnet & 0xFF
|
||||
result.push(octets.join(".")+'/'+obj.plen)
|
||||
|
||||
// remove from the list
|
||||
tlist.splice(ix, 1)
|
||||
}
|
||||
|
||||
this.filtered.splice(0, this.filtered.length, ...result)
|
||||
},
|
||||
|
||||
|
||||
// update function when selecting prefixes
|
||||
updatePrefixLen(e) {
|
||||
this.filter = e.target.value
|
||||
|
||||
// update buttons
|
||||
var group = document.getElementById("prefixselect")
|
||||
group.childNodes.forEach(button => {
|
||||
if (button.nodeName == "BUTTON") {
|
||||
button.classList.remove('btn-primary')
|
||||
button.classList.remove('btn-secondary')
|
||||
if (button.value == this.filter) {
|
||||
button.classList.add('btn-primary')
|
||||
}
|
||||
else {
|
||||
button.classList.add('btn-secondary')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// select available prefixes
|
||||
this.filterFree()
|
||||
|
||||
},
|
||||
|
||||
// update the example templates
|
||||
updateExample(e) {
|
||||
this.example = e.text
|
||||
}
|
||||
|
||||
},
|
||||
mounted() {
|
||||
// reload data if required
|
||||
if (this.p4.length == 0) {
|
||||
this.reload()
|
||||
}
|
||||
|
||||
// always update the prefix selection
|
||||
this.updatePrefixLen({ target: { value: this.filter } })
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// free IPv6 view
|
||||
|
||||
Vue.component('app-free6', {
|
||||
template: '#app-free6-template',
|
||||
data() {
|
||||
return {
|
||||
|
||||
state: 'invalid',
|
||||
error: '',
|
||||
inet6num: null,
|
||||
p6: [ ],
|
||||
stats: {
|
||||
alloc: 0,
|
||||
nets: 0
|
||||
},
|
||||
|
||||
plist: [ ]
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// reload prefix data
|
||||
reload() {
|
||||
// reset current data
|
||||
this.inet6num = null
|
||||
this.p6.splice(0,this.p6.length)
|
||||
this.state = "loading"
|
||||
|
||||
|
||||
// IPv6 prefixes
|
||||
axios
|
||||
.get('/api/registry/inet6num/*')
|
||||
.then(response => {
|
||||
this.inet6num = response.data
|
||||
this.processIPv6()
|
||||
})
|
||||
.catch(error => {
|
||||
this.error = error
|
||||
this.state = 'error'
|
||||
console.log(error)
|
||||
})
|
||||
},
|
||||
|
||||
// parse an IPv6 string in to an object
|
||||
parse6(cidr) {
|
||||
cidr = cidr.replace('_','/')
|
||||
|
||||
// split prefix and length
|
||||
var s1 = cidr.split('/')
|
||||
|
||||
var plen = parseInt(s1[1])
|
||||
// ignore networks longer than /64 as they aren't valid
|
||||
if (plen <= 64) {
|
||||
|
||||
// split out the quads
|
||||
var quads = s1[0].split(':')
|
||||
|
||||
// and canonicalise '::'
|
||||
var ix=quads.indexOf('')
|
||||
if (ix != -1) {
|
||||
while(quads.length < 8) {
|
||||
quads.splice(ix, 0, '0')
|
||||
}
|
||||
}
|
||||
|
||||
// calculate 64bit network part of prefix
|
||||
var num = BigInt(0)
|
||||
var quad
|
||||
for(var i = 0; i < 4; i++) {
|
||||
num *= BigInt(65536)
|
||||
quad = (quads[i] == '' ? 0 : parseInt('0x' + quads[i]))
|
||||
num += BigInt(quad)
|
||||
}
|
||||
|
||||
var plen = parseInt(s1[1])
|
||||
var mask = BigInt(0xFFFFFFFFFFFFFFFF) - ((2n**BigInt(64-plen))-1n)
|
||||
|
||||
// return object
|
||||
return {
|
||||
plen: plen,
|
||||
mask: mask,
|
||||
subnet: num
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// process IPv6 prefixes
|
||||
processIPv6() {
|
||||
|
||||
var netspace = BigInt(0)
|
||||
|
||||
var nets = [ ]
|
||||
Object.values(this.inet6num).forEach(rp => {
|
||||
// extract attributes
|
||||
var attrib = { }
|
||||
rp.Attributes.forEach(a => {
|
||||
attrib[a[0]] = a[1]
|
||||
})
|
||||
|
||||
// turn in to object
|
||||
obj = this.parse6(attrib.cidr)
|
||||
if (obj != null) {
|
||||
if (attrib.policy != null) {
|
||||
obj.policy = attrib.policy
|
||||
}
|
||||
// ignore ::/0 and fd00::/8 when calculating netspace
|
||||
if ((obj.plen > 8) && (obj.policy != 'open')) {
|
||||
netspace += 2n**BigInt(64 - obj.plen)
|
||||
}
|
||||
nets.push(obj)
|
||||
}
|
||||
})
|
||||
|
||||
netspace /= 1000000n
|
||||
this.stats.nets = Number(netspace)
|
||||
|
||||
// sort by subnet
|
||||
var sorted = nets.sort((a,b) => {
|
||||
if (a.subnet == b.subnet) {
|
||||
return a.plen - b.plen
|
||||
}
|
||||
else {
|
||||
if (a.subnet > b.subnet) { return 1 }
|
||||
return -1
|
||||
}
|
||||
})
|
||||
|
||||
// update
|
||||
this.p6.splice(0, this.p6.length, ...nets)
|
||||
|
||||
this.stats.alloc = this.p6.length
|
||||
|
||||
this.generate()
|
||||
|
||||
// all done
|
||||
this.state = 'ready'
|
||||
},
|
||||
|
||||
// check if a network is already allocated
|
||||
check6(n) {
|
||||
|
||||
var match = null
|
||||
this.p6.forEach(obj => {
|
||||
// only check longer prefixes than current match
|
||||
if ((match == null) || (obj.plen >= match.plen)) {
|
||||
var masked = n & obj.mask
|
||||
if (obj.subnet == masked) {
|
||||
// new, more precise prefix found
|
||||
match = obj
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if ((match != null) && (match.policy != 'open')) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
|
||||
// create new random prefixes
|
||||
generate() {
|
||||
|
||||
var nlist = [ ]
|
||||
// generate 10 new prefixes
|
||||
for(var i = 0; i < 10; i++) {
|
||||
var valid = false
|
||||
var prefix = ''
|
||||
|
||||
while(!valid) {
|
||||
|
||||
// create 48bits of random address
|
||||
var quads = [ ]
|
||||
for(var j = 0; j < 3; j++) {
|
||||
quads[j] = Math.round(Math.random()*65536)
|
||||
}
|
||||
// fix the first byte to be in fd00::/8
|
||||
quads[0] = 0xFD00 + (quads[0] & 0xFF)
|
||||
|
||||
// convert to hex
|
||||
var hex = [ ]
|
||||
for (j = 0; j < 3; j++) {
|
||||
hex[j] = quads[j].toString(16)
|
||||
}
|
||||
|
||||
prefix = `${hex[0]}:${hex[1]}:${hex[2]}::/48`
|
||||
|
||||
// convert to a BigInt
|
||||
var num = BigInt(0)
|
||||
for(var j = 0; j < 3; j++) {
|
||||
num *= BigInt(65536)
|
||||
num += BigInt(quads[j])
|
||||
}
|
||||
num *= 65536n
|
||||
|
||||
// now check that the random prefix is not a subnet of another allocation
|
||||
valid = this.check6(num)
|
||||
}
|
||||
|
||||
nlist.push(prefix)
|
||||
}
|
||||
|
||||
|
||||
// swap in the new prefix list
|
||||
this.plist.splice(0, this.plist.length, ...nlist)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
computed: {
|
||||
freenets() {
|
||||
return Math.round(72057.594 - (this.stats.nets/1000000))
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.p6.length == 0) {
|
||||
this.reload()
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// free IPv6 view
|
||||
|
||||
Vue.component('app-asn', {
|
||||
template: '#app-asn-template',
|
||||
data() {
|
||||
return {
|
||||
|
||||
state: 'invalid',
|
||||
error: '',
|
||||
autnum: null,
|
||||
free: [ ],
|
||||
stats: {
|
||||
alloc: 0,
|
||||
},
|
||||
|
||||
alist: [ ]
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// reload prefix data
|
||||
reload() {
|
||||
// reset current data
|
||||
this.asn = null
|
||||
this.free.splice(0,this.free.length)
|
||||
this.state = "loading"
|
||||
|
||||
// fetch ASN list
|
||||
axios
|
||||
.get('/api/registry/aut-num')
|
||||
.then(response => {
|
||||
this.autnum = response.data['aut-num']
|
||||
this.processASN()
|
||||
})
|
||||
.catch(error => {
|
||||
this.error = error
|
||||
this.state = 'error'
|
||||
console.log(error)
|
||||
})
|
||||
},
|
||||
|
||||
// process ASN list
|
||||
processASN() {
|
||||
|
||||
var asn = [ ]
|
||||
// extract used ASN
|
||||
this.autnum.forEach(a => {
|
||||
// only interested in DN42 ASN
|
||||
if (a.startsWith('AS424242')) {
|
||||
var num = parseInt(a.substr(8))
|
||||
asn[num] = true
|
||||
this.stats.alloc += 1
|
||||
}
|
||||
})
|
||||
|
||||
// now work out which ASN are free
|
||||
for(var a = 0; a < 4000; a++) {
|
||||
if (!asn[a]) {
|
||||
var str = a.toString(10)
|
||||
// zero pad
|
||||
while(str.length < 4) {
|
||||
str = '0' + str
|
||||
}
|
||||
|
||||
this.free.push('AS424242' + str)
|
||||
}
|
||||
}
|
||||
|
||||
this.generate()
|
||||
|
||||
// all done
|
||||
this.state = 'ready'
|
||||
},
|
||||
|
||||
|
||||
// create new random prefixes
|
||||
generate() {
|
||||
|
||||
var nlist = [ ]
|
||||
for(var i = 0; i < 10; i++) {
|
||||
// pick a random free ASN
|
||||
var rand = Math.round(Math.random() * this.free.length)
|
||||
nlist.push(this.free[rand])
|
||||
}
|
||||
|
||||
// swap in the new prefix list
|
||||
this.alist.splice(0, this.alist.length, ...nlist)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.free.length == 0) {
|
||||
this.reload()
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// main vue application starts here
|
||||
|
||||
// initialise the Vue Router
|
||||
const router = new VueRouter({
|
||||
routes: [
|
||||
{ path: '/', component: Vue.component('app-root') },
|
||||
{ path: '/4', component: Vue.component('app-free4') },
|
||||
{ path: '/6', component: Vue.component('app-free6') },
|
||||
{ path: '/asn', component: Vue.component('app-asn') }
|
||||
]
|
||||
})
|
||||
|
||||
// and the main app instance
|
||||
const vm = new Vue({
|
||||
el: '#free-app',
|
||||
data: {
|
||||
|
||||
},
|
||||
router
|
||||
})
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// end of code
|
@ -20,8 +20,9 @@ body { box-shadow: inset 0 2em 10em rgba(0,0,0,0.4); min-height: 100vh }
|
||||
<nav class="navbar navbar-fixed-top navbar-expand-md navbar-dark bg-dark">
|
||||
<search-input></search-input>
|
||||
<div class="collapse navbar-collapse w-100 ml-auto text-nowrap">
|
||||
<div class="ml-auto"><router-link class="navbar-brand"
|
||||
to="/">Registry Explorer</router-link> <a class="pull-right navbar-brand"
|
||||
<div class="ml-auto"><a class="navbar-brand"
|
||||
href="/free">Free Explorer</a> <router-link class="navbar-brand"
|
||||
to="/">Registry Explorer</router-link> <a class="pull-right navbar-brand"
|
||||
href="https://dn42.dev/"><img src="/dn42_logo.png" width="173" height="60"/></a>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user