Generate a random IP address from a subnet in JS

£可爱£侵袭症+ 提交于 2020-04-13 19:17:29

问题


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 1s 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!