22

所以我有 ~12600 个子网:

例如。123.123.208.0/20

和一个IP。

我可以使用 SQLite 数据库或数组或其他任何东西

大约一个月前有一个类似的问题被问到,但是我不是要检查一个 IP 与一个子网,而是一堆子网(显然是最有效的方法,希望不是 O(总子网)):)

如何检查 IP 是否属于这些子网之一,如果这有助于优化,我需要 true 或 false 而不是子网。

当前列表中有类似的子网,例如:(实际提取)

123.123.48.0/22 <-- not a typo
123.123.48.0/24 <-- not a typo
123.123.90.0/24
123.123.91.0/24
123.123.217.0/24

总的来说,它们的范围从 4.xyz 到 222.xyz

4

6 回答 6

27

最好的方法是 IMO 利用位运算符。例如,123.123.48.0/22(123<<24)+(123<<16)+(48<<8)+0(=2071670784;这可能是一个负数) 表示为 32 位数字 IP 地址,并将-1<<(32-22)= -1024 表示为掩码。有了这个,同样,你的测试 IP 地址转换为一个数字,你可以这样做:

(inputIP & testMask) == testIP

例如,123.123.49.123 在该范围内,2071671163 & -10242071670784 也是如此

所以,这里有一些工具功能:

function IPnumber(IPaddress) {
    var ip = IPaddress.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/);
    if(ip) {
        return (+ip[1]<<24) + (+ip[2]<<16) + (+ip[3]<<8) + (+ip[4]);
    }
    // else ... ?
    return null;
}

function IPmask(maskSize) {
    return -1<<(32-maskSize)
}

测试:

(IPnumber('123.123.49.123') & IPmask('22')) == IPnumber('123.123.48.0')

产量true

如果您的掩码格式为“255.255.252.0”,那么您也可以使用 IPnumber 函数作为掩码。

于 2009-02-02T13:37:35.250 回答
10

尝试这个:

var ip2long = function(ip){
    var components;

    if(components = ip.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/))
    {
        var iplong = 0;
        var power  = 1;
        for(var i=4; i>=1; i-=1)
        {
            iplong += power * parseInt(components[i]);
            power  *= 256;
        }
        return iplong;
    }
    else return -1;
};

var inSubNet = function(ip, subnet)
{   
    var mask, base_ip, long_ip = ip2long(ip);
    if( (mask = subnet.match(/^(.*?)\/(\d{1,2})$/)) && ((base_ip=ip2long(mask[1])) >= 0) )
    {
        var freedom = Math.pow(2, 32 - parseInt(mask[2]));
        return (long_ip > base_ip) && (long_ip < base_ip + freedom - 1);
    }
    else return false;
};

用法:

inSubNet('192.30.252.63', '192.30.252.0/22') => true
inSubNet('192.31.252.63', '192.30.252.0/22') => false
于 2013-08-01T18:28:22.423 回答
4

我设法通过使用节点网络掩码模块解决了这个问题。您可以通过以下方式检查 IP 是否属于子网:

import { Netmask } from 'netmask'

const block = new Netmask('123.123.208.0/20')
const ip = '123.123.208.0'
console.log(block.contains(ip))

将在这里打印true

您可以使用以下方法安装它:

npm i --save netmask
于 2015-09-25T14:03:35.593 回答
3

Convert the lower ip and the upper ip in the range to integers and store the range in the db then make sure both columns are indexed.

Off the top of my head (pseudo code):

function ipmap(w,x,y,z) {
  return 16777216*w + 65536*x + 256*y + z;
}

var masks = array[ipmap(128,0,0,0), ipmap(196,0,0,0), ..., ipmap(255,255,255,255)]

function lowrange(w, x, y, z, rangelength) {
  return ipmap(w, x, y, z) & masks[rangelength]
}

function hirange(w, x, y, z, rangelength) {
  return lowrange(w, x, y, z, ,rangelength) + ipmap(255,255,255,255) - masks[rangelength];
}

That ought to do it.

To find whether a particular ip falls in any of the ranges, convert it to an integer and do:

SELECT COUNT(*) FROM ipranges WHERE lowrange <= 1234567 AND 1234567 <= highrange

The query optimizer should be able to speed this up greatly.

于 2009-02-02T12:42:24.097 回答
2

功能IPnumberIPmask很好,但我宁愿测试:

(IPnumber('123.123.49.123') & IPmask('22')) == (IPnumber('123.123.48.0')  & IPmask('22'))

因为对于每个地址,你只需要考虑地址的网络部分。因此,这样做IPmask('22')会将地址的计算机部分归零,您应该对网络地址执行相同的操作。

于 2017-03-21T08:47:15.570 回答
1

关键词:二分查找、预处理、排序

我遇到了类似的问题,如果您可以预处理子网列表并对其进行排序,那么二进制搜索似乎非常有效。然后你可以实现O(log n)的渐近时间复杂度。

这是我的代码(MIT 许可证,原始位置:https ://github.com/iBug/pac/blob/854289a674578d096f60241804f5893a3fa17523/code.js ):

function belongsToSubnet(host, list) {
  var ip = host.split(".").map(Number);
  ip = 0x1000000 * ip[0] + 0x10000 * ip[1] + 0x100 * ip[2] + ip[3];

  if (ip < list[0][0])
    return false;

  // Binary search
  var x = 0, y = list.length, middle;
  while (y - x > 1) {
    middle = Math.floor((x + y) / 2);
    if (list[middle][0] < ip)
      x = middle;
    else
      y = middle;
  }

  // Match
  var masked = ip & list[x][1];
  return (masked ^ list[x][0]) == 0;
}

以及一个示例用法:

function isLan(host) {
  return belongsToSubnet(host, LAN);
}

var LAN = [
  [0x0A000000, 0xFF000000], // 10.0.0.0/8
  [0x64400000, 0xFFC00000], // 100.64.0.0/10
  [0x7F000000, 0xFF000000], // 127.0.0.0/8
  [0xA9FE0000, 0xFFFF0000], // 169.254.0.0/16
  [0xAC100000, 0xFFF00000], // 172.16.0.0/12
  [0xC0A80000, 0xFFFF0000]  // 192.168.0.0/16
];
isLan("127.12.34.56"); // => true
isLan("8.8.8.8"); // => false (Google's Public DNS)

您可以获取一个 PAC 脚本*,并查看它如何针对 5000 个子网执行(它从其他地方加载中国 IP 列表,对它们进行排序并适当地格式化它们)。在实践中,它的速度令人惊讶地令人满意。

可以使用上述页面上的 F12 开发工具检查预处理代码。简而言之,您需要将 IP 地址和网络掩码转换1.2.3.4/16[0x01020304, 0xFFFF0000],即 32 位无符号整数。

* 链接到我的个人网站。

于 2020-02-17T03:54:11.023 回答