在JS中从子网生成一个随机IP地址

3
我正在尝试生成一个随机IP地址,给定一个IP地址子网。有很多资源可以生成随机IP,但我的要求是从特定的子网中生成它。我使用了一个名为netmask的npm模块,但实现方式绝对不够优雅。请问有人能提供一些优秀的指针吗?
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 :(
    }
});
3个回答

8
这很容易,不需要任何额外的依赖,尽管我不会给你一个确切的答案,但会给你一个关于IP工作原理以及如何解决你的问题的想法。如果你自己完成此任务,这个教训将更有价值。
例如,我们来看10.0.0.0/20CIDR。让我们将10.0.0.0转换为位:

00001010.00000000.00000000.00000000

我们从左边剥离了20个位,因为这是网络,所以我们留下 0000.00000000 作为主机(. 点仅用于可读性):
00001010.00000000.00000000.00000000 Network
XXXXXXXX.XXXXXXXX.XXXX0000.00000000 Strip 20 bits of the subnet

将每个八位字节与其余位混合,任何方式都可以,例如我们可以得到0101.10001010。避免使用仅包含1的主机(1111.11111111)作为广播IP,这仍然是有效的IP,但不适用于主机。将子网部分与主机部分连接起来。我们得到:
// S=Subnet, H=Host
SSSSSSSS.SSSSSSSS.SSSSHHHH.HHHHHHHH
00001010.00000000.00000101.10001010

这是一个关于编程的内容,其中 1010 = 100101 = 510001010=138,因此最终地址为 10.0.5.138


既然写起来很有趣,我可以给你我的实现方式,它不涉及任何字符串操作。正如你所看到的,IPv4地址是一个2^32无符号整数。因此我们可以应用基本数学:

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));
}


谢谢你。这对我很有帮助!我正在根据你的方法进行模拟。 :) - blueren
如果我有帮助到您,请考虑给我点赞并将其标记为解决方案。 - Mike Doe
谢谢 @emix。根据你解释的方法,我已经提供了一个简单的答案。 - blueren
漂亮的解决方案! - Takit Isy
谢谢。二进制操作更加高效。路由器和交换机就是这样运作的。 - Mike Doe

1

(我在等你发布自己的函数之前再发布我的。)

这是我自己的版本,基于emix的答案。

我尝试使用循环和数组函数使它最易理解。

第一个片段

// 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'));

第二段代码片段(我认为是增强版)

// 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'));


谢谢,感谢您的帮助! - blueren
@blueren 很高兴能帮到你。在发布我的函数之前,我一直在等待你发布自己的函数。:) 我添加了另一个版本(因为我更喜欢这个新版本。:p) - Takit Isy
我会用不同的方法来处理。将网络地址转换为数字(它是一个2^32无符号整数),生成1到2的11次方(32-掩码(20))之间的随机数,并对其和子网进行逻辑或运算。然后再将数字转换回IP地址。这是纯数学,对我来说在这种情况下操纵字符串感觉不自然。 - Mike Doe
@emix 嗯,也许你可以分享一下你自己的函数。 :) - Takit Isy

0
基于emix的回答-

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')


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接