问题
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 :(
}
});
回答1:
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 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, the IPv4 address is a 2^32 unsigned integer. We can thus 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));
}
回答2:
( 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'));
回答3:
Based on emix's answer -
function getIPFromSubnet(subnetRange) {
// subnetRange = "10.0.0.0/24"
const subnet = subnetRange.split('/')[0]; // 10.0.0.0
const mask = subnetRange.split('/')[1]; // 24
const ipArray = subnet.split('.'); //["10", "0", "0", "0"]
var ipInBinary = ""; // will contain the binary equivalent of the iP
// for each element in the array, convert from decimal to binary
for (let quad of ipArray) {
let octet = parseInt(quad, 10).toString(2)
// we need each octet to be 8 bits. So provide padding for those which are less than 8 bits
// 0101 becomes 00000101
let octetLength = octet.length
if (octetLength < 8) {
let octDiff = 8 - octetLength;
octet = "0".repeat(octDiff) + octet
}
// concat all the octets into a 32 bit binary
ipInBinary = ipInBinary.concat(octet) // 00001010000000000000000000000000
}
// console.log("ipInBinary: ", ipInBinary);
// strip the subnet from the entire address:
let subnetBinary = ipInBinary.slice(0, mask) // 000010100000000000000000
let hostsBinary = ipInBinary.slice(mask, ipInBinary.length) // 00000000
var randomBinarySubstitute = "";
const randomPool = "10101010101010101010101010101010" //fix this nonsense later.
for (let i = 0; i < 32 - mask; i++) {
randomBinarySubstitute += randomPool[Math.floor(Math.random() * ipInBinary.length)]
}
let newIPBinary = subnetBinary + randomBinarySubstitute;
let finalIP = "";
// split the 32 bit binary IP into an array of 8 bits, each representing an octate
let finalIPArray_binary = newIPBinary.match(/.{8}/g) // ["00001010", "00000000", "00000000", "10001010"]
// convert the binary quad array to decimal dotted quad
for (let element of finalIPArray_binary) {
finalIP = finalIP + "." + parseInt(element, 2);
finalIP = finalIP.replace(/^\./, ""); // remnove the leading .
}
console.log("FinalIP", finalIP)
return finalIP
}
getIPFromSubnet('10.0.0.0/16')
来源:https://stackoverflow.com/questions/53815725/generate-a-random-ip-address-from-a-subnet-in-js