2017-09-20 10:49:00 -05:00
< html >
< head >
< title > Visual Subnet Calculator< / title >
< script language = "javascript" type = "text/javascript" >
<!--
var curNetwork = 0;
var curMask = 0;
2024-11-23 01:20:57 -06:00
var curComments = {};
2017-09-20 10:49:00 -05:00
function updateNetwork()
{
var newNetworkStr = document.forms['calc'].elements['network'].value;
var newMask = parseInt(document.forms['calc'].elements['netbits'].value);
var newNetwork = inet_aton(newNetworkStr);
if (newNetwork === null) {
alert('Invalid network address entered');
return;
}
var tmpNetwork = network_address(newNetwork, newMask);
if (newNetwork != tmpNetwork) {
alert('The network address entered is not on a network boundary for this mask.\nIt has been changed to '+inet_ntoa(tmpNetwork)+'.');
newNetwork = tmpNetwork;
document.forms['calc'].elements['network'].value = inet_ntoa(tmpNetwork);
}
if (newMask < 0 | | newMask > 32) {
alert('The network mask you have entered is invalid');
return;
}
if (curMask == 0) {
curMask = newMask;
curNetwork = newNetwork;
startOver();
}
else if (curMask != newMask & & confirm('You are changing the base network from /'+curMask+' to /'+newMask+'. This will reset any changes you have made. Proceed?')) {
curMask = newMask;
curNetwork = newNetwork;
startOver();
}
else {
document.forms['calc'].elements['netbits'].value = curMask;
curNetwork = newNetwork;
recreateTables();
}
}
function startOver()
{
rootSubnet = [0, 0, null];
recreateTables();
}
function recreateTables()
{
var calcbody = document.getElementById('calcbody');
if (!calcbody) {
alert('Body not found');
return;
}
while (calcbody.hasChildNodes()) {
calcbody.removeChild(calcbody.firstChild);
}
updateNumChildren(rootSubnet);
updateDepthChildren(rootSubnet);
createRow(calcbody, rootSubnet, curNetwork, curMask, [curMask, rootSubnet[1], rootSubnet], rootSubnet[0]);
document.getElementById('joinHeader').colSpan = (rootSubnet[0] > 0 ? rootSubnet[0] : 1);
2024-11-23 01:20:57 -06:00
document.getElementById('col_join').span = (rootSubnet[0] > 0 ? rootSubnet[0] : 1);
/* Disable joins for subnets with comments. */
var joinLocks = {}; // a unique collection of join elements to disable
for (var addressWithMask in curComments) {
let splitAddressMask = addressWithMask.split('/');
let addressToLock = inet_aton(splitAddressMask[0]);
let upperMaskToLock = splitAddressMask[1];
for (let maskToLock = upperMaskToLock; maskToLock >= curMask; maskToLock--) {
joinLocks[inet_ntoa(network_address(addressToLock, maskToLock)) + "/" + maskToLock] = true;
}
}
2017-09-20 10:49:00 -05:00
2024-11-23 01:20:57 -06:00
for (var lock in joinLocks) {
let joinElement = document.getElementById("join_" + lock);
if (joinElement & & joinElement.onclick) {
joinElement.onclick = null;
joinElement.classList.remove("maskSpanJoinable");
joinElement.classList.add("maskSpan");
}
};
createBookmarkHyperlink();
}
function createBookmarkHyperlink() {
2017-09-20 10:49:00 -05:00
var link = document.getElementById('saveLink');
if (link) {
2024-11-23 01:20:57 -06:00
link.href = '?network='+inet_ntoa(curNetwork)
+'& mask='+curMask
+'& division='+binToAscii(nodeToString(rootSubnet))
+'&comments='+encodeURIComponent(JSON.stringify(curComments));
2017-09-20 10:49:00 -05:00
}
}
function nodeToString(node)
{
if (node[2]) {
return '1'+nodeToString(node[2][0])+nodeToString(node[2][1]);
}
else {
return '0';
}
}
function binToAscii(str)
{
var curOut = '';
var curBit = 0;
var curChar = 0;
for (var i=0; i< str.length ; i + + ) {
if (str.charAt(i) == '1') {
curChar |= 1< < curBit ;
}
curBit++;
if (curBit > 3) {
curOut += curChar.toString(16);
curChar = 0;
curBit = 0;
}
}
if (curBit > 0) {
curOut += curChar.toString(16);
}
return str.length+'.'+curOut;
}
function asciiToBin(str)
{
var re = /([0-9]+)\.([0-9a-f]+)/;
var res = re.exec(str);
var len = res[1];
var encoded = res[2];
var out = '';
for (var i=0; i< res [ 1 ] ; i + + ) {
var ch = parseInt(res[2].charAt(Math.floor(i/4)), 16);
var pos = i % 4;
out += (ch & (1< < pos ) ? ' 1 ' : ' 0 ' ) ;
}
return out;
}
2024-11-23 01:20:57 -06:00
// Recursive function that creates rows working from the outer most mask and working inwards
2017-09-20 10:49:00 -05:00
function createRow(calcbody, node, address, mask, labels, depth)
{
2024-11-23 01:20:57 -06:00
if (node[2]) { // We need to go deeper
2017-09-20 10:49:00 -05:00
var newlabels = labels;
newlabels.push(mask+1);
newlabels.push(node[2][0][1]);
newlabels.push(node[2][0]);
createRow(calcbody, node[2][0], address, mask+1, newlabels, depth-1);
newlabels = new Array();
newlabels.push(mask+1);
newlabels.push(node[2][1][1]);
newlabels.push(node[2][1]);
createRow(calcbody, node[2][1], address+subnet_addresses(mask+1), mask+1, newlabels, depth-1);
}
2024-11-23 01:20:57 -06:00
else { // Actually create a row
2017-09-20 10:49:00 -05:00
var newRow = document.createElement('TR');
calcbody.appendChild(newRow);
/* subnet address */
2024-11-23 01:20:57 -06:00
var newCell = document.createElement('TD');
newCell.appendChild(document.createTextNode(inet_ntoa(address)+'/'+mask));
newRow.appendChild(newCell);
2017-09-20 10:49:00 -05:00
var addressFirst = address;
var addressLast = subnet_last_address(address, mask);
var useableFirst = address + 1;
var useableLast = addressLast - 1;
var numHosts;
var addressRange;
var usaebleRange;
2024-11-23 01:20:57 -06:00
var comment = curComments[inet_ntoa(address) + "/" + mask] || null;
2017-09-20 10:49:00 -05:00
if (mask == 32) {
addressRange = inet_ntoa(addressFirst);
useableRange = addressRange;
numHosts = 1;
}
else {
addressRange = inet_ntoa(addressFirst)+' - '+inet_ntoa(addressLast);
if (mask == 31) {
useableRange = addressRange;
numHosts = 2;
}
else {
useableRange = inet_ntoa(useableFirst)+' - '+inet_ntoa(useableLast);
numHosts = (1 + useableLast - useableFirst);
}
}
/* netmask */
2024-11-23 01:20:57 -06:00
var newCell = document.createElement('TD');
newCell.appendChild(document.createTextNode(inet_ntoa(subnet_netmask(mask))));
newRow.appendChild(newCell);
2017-09-20 10:49:00 -05:00
/* range of addresses */
2024-11-23 01:20:57 -06:00
var newCell = document.createElement('TD');
newCell.appendChild(document.createTextNode(addressRange));
newRow.appendChild(newCell);
2017-09-20 10:49:00 -05:00
/* useable addresses */
2024-11-23 01:20:57 -06:00
var newCell = document.createElement('TD');
newCell.appendChild(document.createTextNode(useableRange));
newRow.appendChild(newCell);
2017-09-20 10:49:00 -05:00
/* Hosts */
2024-11-23 01:20:57 -06:00
var newCell = document.createElement('TD');
newCell.appendChild(document.createTextNode(numHosts));
newRow.appendChild(newCell);
/* Comments */
var newCell = document.createElement('TD');
var textarea = document.createElement('TEXTAREA');
textarea.id = "comment_" + inet_ntoa(network_address(address, mask)) + "/" + mask;
textarea.onchange = ((mask, address) => ( function() {
var key = inet_ntoa(address) + "/" + mask;
var needToRedraw = false;
if (this.value == null || this.value === "") {
needToRedraw = curComments[key] !== undefined;
delete curComments[key];
} else {
needToRedraw = curComments[key] === undefined;
curComments[key] = this.value;
}
if (needToRedraw) {
recreateTables();
// Restore previous focus after redrawing table
document.getElementById(this.id).focus();
} else {
// Just update link if we don't need to redraw.
createBookmarkHyperlink();
}
}))(mask, address); // keep some vars in scope
textarea.innerText = comment;
newCell.appendChild(textarea);
newRow.appendChild(newCell);
2017-09-20 10:49:00 -05:00
2023-01-08 06:10:39 -06:00
/* actions */
2017-09-20 10:49:00 -05:00
2024-11-23 01:20:57 -06:00
var newCell = document.createElement('TD');
newRow.appendChild(newCell);
if (mask == 32 || comment != null) {
var newLink = document.createElement('SPAN');
newLink.className = 'disabledAction';
newLink.appendChild(document.createTextNode('Divide'));
newCell.appendChild(newLink);
}
else {
var newLink = document.createElement('A');
newLink.href = '#';
newLink.onclick = function () { divide(node); return false; }
newLink.appendChild(document.createTextNode('Divide'));
newCell.appendChild(newLink);
}
var colspan = depth - node[0];
for (var i=(labels.length/3)-1; i>=0; i--) {
var mask = labels[i*3];
var rowspan = labels[(i*3)+1];
var joinnode = labels[(i*3)+2];
2017-09-20 10:49:00 -05:00
var newCell = document.createElement('TD');
2024-11-23 01:20:57 -06:00
newCell.rowSpan = (rowspan > 1 ? rowspan : 1);
newCell.colSpan = (colspan > 1 ? colspan : 1);
newCell.id = "join_" + inet_ntoa(network_address(address, mask)) + "/" + mask;
2017-09-20 10:49:00 -05:00
2024-11-23 01:20:57 -06:00
if (i == (labels.length/3)-1) {
newCell.className = 'maskSpan';
2017-09-20 10:49:00 -05:00
}
else {
2024-11-23 01:20:57 -06:00
newCell.className = 'maskSpanJoinable';
newCell.onclick = newJoin(joinnode);
// newCell.onmouseover = function() { window.status = joinnode[0]+'---'+joinnode[1]+'---'+joinnode[2]+'>>>>>'+node[2];}
2017-09-20 10:49:00 -05:00
}
2024-11-23 01:20:57 -06:00
var newImg = document.createElement('IMG');
newImg.src = 'img/'+mask+'.gif';
newCell.appendChild(newImg);
newRow.appendChild(newCell);
colspan = 1; // reset for subsequent cells
2017-09-20 10:49:00 -05:00
}
}
}
/* This is necessary because 'joinnode' changes during the scope of the caller */
function newJoin(joinnode)
{
2024-11-23 01:20:57 -06:00
return function() {
join(joinnode);
return false; // prevent click event
};
2017-09-20 10:49:00 -05:00
}
function divide(node)
{
node[2] = new Array();
node[2][0] = [0, 0, null];
node[2][1] = [0, 0, null];
recreateTables();
}
function join(node)
{
/* easy as pie */
node[2] = null;
recreateTables();
}
function updateNumChildren(node)
{
if (node[2] == null) {
node[1] = 0;
return 1;
}
else {
node[1] = updateNumChildren(node[2][0]) + updateNumChildren(node[2][1]);
return node[1];
}
}
function updateDepthChildren(node)
{
if (node[2] == null) {
node[0] = 0;
return 1;
}
else {
node[0] = updateDepthChildren(node[2][0]) + updateDepthChildren(node[2][1]);
return node[1];
}
}
var rootSubnet;
// each node is Array:
// [0] => depth of children, total number of visible children, children
function inet_ntoa(addrint)
{
return ((addrint >> 24) & 0xff)+'.'+
((addrint >> 16) & 0xff)+'.'+
((addrint >> 8) & 0xff)+'.'+
(addrint & 0xff);
}
function inet_aton(addrstr)
{
var re = /^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/;
var res = re.exec(addrstr);
if (res === null) {
return null;
}
for (var i=1; i< =4; i++) {
if (res[i] < 0 | | res [ i ] > 255) {
return null;
}
}
return (res[1] < < 24 ) | ( res [ 2 ] < < 16 ) | ( res [ 3 ] < < 8 ) | res [ 4 ] ;
}
function network_address(ip, mask)
{
var maskbits = 0;
for (var i=31-mask; i>=0; i--) {
ip & = ~ 1< < i ;
}
return ip;
}
function subnet_addresses(mask)
{
2024-11-23 01:20:57 -06:00
return 1< < (32-mask);
2017-09-20 10:49:00 -05:00
}
function subnet_last_address(subnet, mask)
{
return subnet + subnet_addresses(mask) - 1;
}
function subnet_netmask(mask)
{
return network_address(0xffffffff, mask);
}
function preloadSubnetImages()
{
if (document.images) {
if (!document.preloadedImages) {
document.preloadedImages = new Array();
}
for (var i=0; i< =32; i++) {
var img = new Image();
img.src = 'img/'+i+'.gif';
document.preloadedImages.push(img);
}
}
}
function calcOnLoad()
{
preloadSubnetImages();
args = parseQueryString();
if (args['network'] & & args['mask'] & & args['division']) {
document.forms['calc'].elements['network'].value = args['network'];
document.forms['calc'].elements['netbits'].value = args['mask'];
2024-11-23 01:20:57 -06:00
if (args['comments']) {
curComments = JSON.parse(args['comments']);
} else {
curComments = {};
}
2017-09-20 10:49:00 -05:00
updateNetwork();
var division = asciiToBin(args['division']);
rootSubnet = [0, 0, null];
if (division != '0') {
loadNode(rootSubnet, division);
}
recreateTables();
}
else {
updateNetwork();
}
}
function loadNode(curNode, division)
{
if (division.charAt(0) == '0') {
return division.substr(1);
}
else {
curNode[2] = new Array();
curNode[2][0] = [0, 0, null];
curNode[2][1] = [0, 0, null];
division = loadNode(curNode[2][0], division.substr(1));
division = loadNode(curNode[2][1], division);
return division;
}
}
function parseQueryString (str)
{
str = str ? str : location.search;
var query = str.charAt(0) == '?' ? str.substring(1) : str;
var args = new Object();
if (query) {
var fields = query.split('&');
for (var f = 0; f < fields.length ; f + + ) {
var field = fields[f].split('=');
args[unescape(field[0].replace(/\+/g, ' '))] =
unescape(field[1].replace(/\+/g, ' '));
}
}
return args;
}
window.onload = calcOnLoad;
function toggleColumn(cb)
{
var colName = 'col_'+(cb.id.substr(3));
2024-11-23 01:20:57 -06:00
var col = document.getElementById(colName);
if (cb.checked) {
col.style.display = 'block';
}
else {
col.style.display = 'none';
}
2017-09-20 10:49:00 -05:00
recreateTables(); /* because IE draws lines all over the place with border-collapse */
}
//-->
< / script >
< style type = "text/css" >
H1 {
font-family: Arial, Verdana, sans-serif;
font-size: 18pt;
}
BODY {
font-family: Arial, Verdana, sans-serif;
}
P {
font-family: Arial, Verdana, sans-serif;
font-size: 75%;
}
2024-11-23 01:20:57 -06:00
.label {
2017-09-20 10:49:00 -05:00
font-family: Arial, Verdana, sans-serif;
font-size: 60%;
}
.calc {
font-family: Arial, Verdana, sans-serif;
font-size: 80%;
border-collapse: collapse;
}
2024-11-23 01:20:57 -06:00
.calc td {
2017-09-20 10:49:00 -05:00
border: 1px solid black;
}
.calc thead {
font-weight: bold;
background-color: #eeeeee;
}
2024-11-23 01:20:57 -06:00
.calc textarea {
height: 1.5em;
}
2017-09-20 10:49:00 -05:00
.disabledAction {
color: #dddddd;
}
.maskSpan {
background-color: #cccccc;
text-align: right;
}
.maskSpanJoinable {
background-color: #cccccc;
text-align: right;
2024-11-23 01:20:57 -06:00
cursor: grab;
2017-09-20 10:49:00 -05:00
}
.maskSpanRotate {
filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
background-color:green;
}
< / style >
< / head >
< body >
< table width = "100%" >
< tr valign = top >
< td >
< h1 > Visual Subnet Calculator< / h1 >
< p > Enter the network you wish to subnet:< / p >
< form name = "calc" onsubmit = "updateNetwork(); return false;" >
< table cellspacing = "0" >
< tr >
< td class = "label" > Network Address< / td >
< td class = "label" > Mask bits< / td >
< / tr >
< tr >
< td > < input type = "text" name = "network" size = "15" maxlength = "15" value = "192.168.0.0" > < / td >
< td > /< input type = "text" name = "netbits" size = "2" maxlength = "2" value = "16" > < / td >
< td > < input type = "submit" value = "Update" >
< input type = "button" value = "Reset" onclick = "if (confirm('This will reset all subnet divisions you have made. Proceed?')) startOver();" >
< / td >
< / tr >
< / table >
2024-11-23 01:20:57 -06:00
< p > Show columns:
2017-09-20 10:49:00 -05:00
< input type = "checkbox" id = "cb_subnet" checked onclick = "toggleColumn(this)" > < label for = "cb_subnet" > Subnet address< / label >
< input type = "checkbox" id = "cb_netmask" onclick = "toggleColumn(this)" > < label for = "cb_netmask" > Netmask< / label >
< input type = "checkbox" id = "cb_range" checked onclick = "toggleColumn(this)" > < label for = "cb_range" > Range of addresses< / label >
< input type = "checkbox" id = "cb_useable" checked onclick = "toggleColumn(this)" > < label for = "cb_useable" > Useable IPs< / label >
< input type = "checkbox" id = "cb_hosts" checked onclick = "toggleColumn(this)" > < label for = "cb_hosts" > Hosts< / label >
2024-11-23 01:20:57 -06:00
< input type = "checkbox" id = "cb_comments" checked onclick = "toggleColumn(this)" > < label for = "cb_comments" > Comments< / label >
2017-09-20 10:49:00 -05:00
< input type = "checkbox" id = "cb_divide" checked onclick = "toggleColumn(this)" > < label for = "cb_divide" > Divide< / label >
< input type = "checkbox" id = "cb_join" checked onclick = "toggleColumn(this)" > < label for = "cb_join" > Join< / label >
< / p >
< / form >
< p > Click below to split and join subnets.< br >
If you wish to save this subnetting for later, bookmark < a href = "subnets.html" id = "saveLink" > this hyperlink< / a > .< / p >
< / td >
< td align = "right" >
2024-11-23 01:20:57 -06:00
< a href = "https://github.com/davidc/subnets" > < img alt = "Fork me on GitHub" src = "https://github.blog/wp-content/uploads/2008/12/forkme_right_white_ffffff.png" style = "right: 0;top: 0;position: absolute;" > < / a >
2017-09-20 10:49:00 -05:00
< / td >
< / tr >
< / table >
< br >
< hr noshade color = "black" size = "1" >
< br >
< table class = "calc" cellspacing = "0" cellpadding = "2" >
2024-11-23 01:20:57 -06:00
< colgroup >
< col id = "col_subnet" >
< col id = "col_netmask" style = "display: none" >
< col id = "col_range" >
< col id = "col_useable" >
< col id = "col_hosts" >
< col id = "col_comments" >
< col id = "col_divide" >
< col id = "col_join" >
< / colgroup >
2017-09-20 10:49:00 -05:00
< thead >
< tr >
2024-11-23 01:20:57 -06:00
< td > Subnet address< / td >
< td > Netmask< / td >
< td > Range of addresses< / td >
< td > Useable IPs< / td >
< td > Hosts< / td >
< td > Comments< / td >
< td > Divide< / td >
< td id = "joinHeader" > Join< / td >
2017-09-20 10:49:00 -05:00
< / tr >
< / thead >
< tbody id = "calcbody" >
<!-- tr>
< td > 130.94.203.0/24< / td >
< td > 130.94.203.0 - 130.94.203.255< / td >
< td > 130.94.203.1 - 130.94.203.254 (254)< / td >
< td > Divide< / td >
< / tr-- >
< / tbody >
< / table >
< / body >
< / html >