I'm trying to generate a random IP address given a subnet of IP address. There are plenty of resources available to generate a random IP, but my requirement it to have it generated from within a specific subnet.
I've used an npm module called netmask - however the implementation is absolutely not elegant. Can anyone please give some slick pointers to this?
var netmask = require("netmask").Netmask
var block = new netmask('10.0.0.0/24')
console.log(block) // gives block details
var blockSize = block.size - 1 ;
var randomIndex = Math.floor(Math.random() * blockSize ) +1; // generate a random number less than the size of the block
console.log("randomIndex is: " + randomIndex);
block.forEach(function(ip, long, index){
if(index == randomIndex){
console.log('IP: ' + ip)
console.log('INDEX: ' + index)
// cannot break! this is a forEach :(
}
});
This is quite easy without any additional dependencies, albeit I'm not giving you an exact answer, but an idea how IP's work in general and how to tackle your issue. This lesson will be much more valuable if you do this by yourself.
Let's take 10.0.0.0/20
CIDR for example. Lets convert 10.0.0.0
to bits:
00001010.00000000.00000000.00000000
We strip 20 bits as this is the network from the left, so we are left with 0000.00000000
for the hosts (.
dots are here only for readability):
00001010.00000000.00000000.00000000 Network
XXXXXXXX.XXXXXXXX.XXXX0000.00000000 Strip 20 bits of the subnet
Shuffle each octet with remaining bits anyway you want, for instance we could get 0101.10001010
. Avoid the host with 1
s only (1111.11111111
) as it's the broadcast IP, it's still a valid IP, not for the hosts though. Concatenate the subnet part with the host part. We get:
// S=Subnet, H=Host
SSSSSSSS.SSSSSSSS.SSSSHHHH.HHHHHHHH
00001010.00000000.00000101.10001010
which is 1010 = 10
and 0
and 101 = 5
and 10001010=138
so the final address is 10.0.5.138
Since it was fun to write, I can give you my own implementation which does not involve any string manipulation. As you can see, an IPv4 address is an 2^32
unsigned integer. Thus we can apply basic math:
let ipv4 = {
random: function (subnet, mask) {
// generate random address (integer)
// if the mask is 20, then it's an integer between
// 1 and 2^(32-20)
let randomIp = Math.floor(Math.random() * Math.pow(2, 32 - mask)) + 1;
return this.lon2ip(this.ip2lon(subnet) | randomIp);
},
ip2lon: function (address) {
let result = 0;
address.split('.').forEach(function(octet) {
result <<= 8;
result += parseInt(octet, 10);
});
return result >>> 0;
},
lon2ip: function (lon) {
return [lon >>> 24, lon >> 16 & 255, lon >> 8 & 255, lon & 255].join('.');
}
};
// unit test
console.log(
"192.168.0.35" === ipv4.lon2ip(ipv4.ip2lon('192.168.0.35')) ?
'Test passed' :
'Test failed'
);
for (let i = 0; i < 5; i++) {
console.log(ipv4.random('10.0.0.0', 8));
}
( I was waiting for you to post your own function before posting mine. )
Here is my own version, based on emix's answer.
I tried to make it the most easily understandable using loops and array functions.
1st snippet
// Function to convert string of numbers to 01010101 with leading zeros
function StrToBlock(str) {
return ("00000000" + (+str).toString(2)).slice(-8);
}
// Function to convert 01010101 to string of numbers
function BlockToStr(block) {
return parseInt(block, 2);
}
// Main function
function GetRandomIP(netmask) {
// Split netmask
var netmasks = netmask.split("/");
var maskBlocks = netmasks[0].split(".");
var maskLength = netmasks[1];
// Loop for each address part
var blockBits = '';
maskBlocks.forEach(function(block) {
// Convert to bits
blockBits = blockBits + StrToBlock(block);
});
// Here, blockBits is something like 00110101001101010011010100110101
// Loop for each bit
var ipBits = [];
var ipBlocks = [];
for (var i = 0; i < 32; i++) {
// If in mask, take the mask bit, else, a random 0 or 1
var bit = (i < maskLength) ? blockBits[i] : Math.round(Math.random());
ipBits.push(bit);
// If block is full, convert back to a decimal string
if (ipBits.length == 8) {
ipBlocks.push(BlockToStr(ipBits.join('')));
ipBits = []; // Erase to start a new block
}
}
// Return address as string
return ipBlocks.join('.');
}
// Different tests
console.log(GetRandomIP('255.255.255.0/8'));
console.log(GetRandomIP('255.255.255.0/24'));
console.log(GetRandomIP('10.0.0.0/24'));
⋅ ⋅ ⋅
2nd snippet (enhanced, in my opinion)
// Function to convert string of numbers to 01010101 with leading zeros
function StrToBlock(str) {
return ("00000000" + (+str).toString(2)).slice(-8);
}
// Function to convert 01010101 to string of numbers
function BlockToStr(block) {
return parseInt(block, 2);
}
// Main function
function GetRandomIP(netmask) {
// Split netmask
var netmasks = netmask.split("/");
var maskBlocks = netmasks[0].split(".");
var maskLength = netmasks[1];
// Loop for each of the 4 address parts
var blockBits = '';
maskBlocks.forEach(function(block) {
blockBits = blockBits + StrToBlock(block);
});
// Copy mask and then add some random bits
var ipBits = blockBits.substring(0, maskLength);
for (var i = maskLength; i < 32; i++) {
ipBits = ipBits + Math.round(Math.random());
}
// Split and convert back to decimal strings
var ipBlocks = ipBits.match(/.{8}/g);
ipBlocks.forEach(function(block, i) {
ipBlocks[i] = BlockToStr(block);
});
// Return address as string
return ipBlocks.join('.');
}
// Different tests
console.log(GetRandomIP('255.255.255.0/8'));
console.log(GetRandomIP('255.255.255.0/24'));
console.log(GetRandomIP('10.0.0.0/24'));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With