【问题标题】:Generate a random IP address from a subnet in JS从 JS 中的子网生成随机 IP 地址
【发布时间】: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 :(
    }
});

【问题讨论】:

    标签: javascript npm ip-address ipv4 subnet


    【解决方案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')

    【讨论】:

      猜你喜欢
      • 2012-03-03
      • 2017-10-02
      • 2012-05-03
      • 2014-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-24
      • 1970-01-01
      相关资源
      最近更新 更多