Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate a random IP address from a subnet in JS

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 :(
    }
});
like image 974
blueren Avatar asked Oct 11 '25 07:10

blueren


2 Answers

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 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));
}
like image 60
emix Avatar answered Oct 13 '25 20:10

emix


( 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'));
like image 23
Takit Isy Avatar answered Oct 13 '25 20:10

Takit Isy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!