从 JS 中的子网生成随机 IP 地址

Posted

技术标签:

【中文标题】从 JS 中的子网生成随机 IP 地址【英文标题】:Generate a random IP address from a subnet in JS 【发布时间】:2019-05-17 20:23:25 【问题描述】:

我正在尝试在给定 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 :(
    
);

【问题讨论】:

【参考方案1】:

这很容易,无需任何额外的依赖,尽管我没有给你一个确切的答案,但我知道IP's work in general 是如何解决你的问题的。如果你自己做这件事,这一课会更有价值。

我们以10.0.0.0/20 CIDR 为例。让我们将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。避免使用仅带有1s (1111.11111111) 的主机,因为它是broadcast 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));

【讨论】:

谢谢你。它有很大帮助!我正在根据您的方法进行模拟。 :) 当然,如果我有帮助,请考虑支持和标记作为解决方案。 谢谢@emix。我根据你解释的方法给出了一个粗略的答案 完美解决方案! 谢谢。二元运算效率更高。路由器和交换机以这种方式运行。【参考方案2】:

(我在等你发布自己的函数,然后再发布我的函数。)

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

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

1st sn-p

// 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 sn-p (在我看来增强了)

// 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 很高兴它有帮助。在发布我的功能之前,我一直在等待您发布自己的功能。 :) 我添加了另一个版本(因为我更喜欢这个新版本。:p) 我会以不同的方式做这件事。将网络地址转换为一个数字(它是一个 2^32 无符号整数),生成介于 1 和 2 的 11 次方(32 - 掩码 (20))之间的随机数,并对它和子网进行逻辑或。然后再次将号码转换为IP。这是纯粹的数学运算,在这种情况下操作字符串对我来说感觉不自然。 @emix 好吧,也许你可以分享你自己的功能。 :)【参考方案3】:

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

【讨论】:

以上是关于从 JS 中的子网生成随机 IP 地址的主要内容,如果未能解决你的问题,请参考以下文章

js 生成UUID

Java - 在给定范围内打印随机IP地址

Python 随机生成的 IP 地址为字符串

Linux: 生成随机的MAC地址

Js 代码中,ajax请求地址后加随机数防止浏览器缓存的原因

因为路由器是随机分IP的,怎么限制手机的下载速度