如何根据起始IP和网络掩码长度计算范围内的结束IP

问题描述 投票:0回答:3

我有一个起始 IPv4 IP 地址

5.39.28.128
(或
::ffff:5.39.28.128
)并且我有 IPv6 网络掩码长度
122
,我如何计算该范围内的最后一个 IP?

我相信我需要将起始IP转换为二进制,我正在这样做,如下所示,我不知道从哪里去获取最终IP。

$ipNumber = ip2long('5.39.28.128');
$ipBinary = decbin($ipNumber);

echo $ipBinary; // 101001001110001110010000000

原因是我将 CSV 格式的 MaxMind GeoIP 数据库导入到 MySQL 数据库中(因此如果需要,可以使用 MySQL 函数)。 MaxMind 不再提供结束 IP,而是提供起始 IP 和 IPv6 网络掩码长度。

php ip-address
3个回答
4
投票

你在这里。我已从

this response to another question
复制了 inet_to_bits 函数。

<?php

function inet_to_bits($inet) {
   $inet = inet_pton($inet);
   $unpacked = unpack('A16', $inet);
   $unpacked = str_split($unpacked[1]);
   $binaryip = '';
   foreach ($unpacked as $char) {
             $binaryip .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
   }
   return $binaryip;
}

function bits_to_inet($bits) {
    $inet = "";
    for($pos=0; $pos<128; $pos+=8) {
        $inet .= chr(bindec(substr($bits, $pos, 8)));
    }
    return inet_ntop($inet);
}

$ip = "::ffff:5.39.28.128";
$netmask = 122;

// Convert ip to binary representation
$bin = inet_to_bits($ip);

// Generate network address: Length of netmask bits from $bin, padded to the right
// with 0s for network address and 1s for broadcast
$network = str_pad(substr($bin, 0, $netmask), 128, '1', STR_PAD_RIGHT);

// Convert back to ip
print bits_to_inet($network);

输出:

::ffff:5.39.28.191

3
投票

解决方案非常简单:

// Your input data
$networkstart = '5.39.28.128';
$networkmask = 122;

// First find the length of the block: IPv6 uses 128 bits for the mask
$networksize = pow(2, 128 - $networkmask);

// Reduce network size by one as we really need last IP address in the range,
// not first one of subsequent range
$networklastip = long2ip(ip2long($networkstart) + $networksize - 1);

$networklastip 将具有该范围内的最后一个 IP 地址。

现在,这仅适用于 IPv6 地址空间中的 IPv4 地址。否则,您需要使用 IPv6 往返 128 位整数函数,而不是 ip2long/long2ip。然而,对于 MaxMind 数据代码的使用来说,上面已经足够了,因为我还没有看到任何实际的 IPv6 数据。


0
投票

有点奇怪,每个人都忽略了在提出问题 2 分钟后发布的建议重复内容。 接受的答案在C中提供了一个非常简单的解决方案:

broadcast = ip | (~ subnet);

显然我们不能用人类可读的字符串来做到这一点,所以需要一些数学才能到达相同的地方:

$network = '::ffff:5.39.28.128/122';
[$address, $bits] = explode('/', $network);
$address = inet_pton($address);
$mask = str_repeat('f', $bits / 4) . match($bits % 4) {
    0 => '',
    1 => '8',
    2 => 'c',
    3 => 'e',
};
$mask = pack('H*', str_pad($mask, 32, '0'));

$first_in_net = inet_ntop($address & $mask);
$last_in_net = inet_ntop($address | ~$mask);

echo "Network range is $first_in_net – $last_in_net";

输出:

Network range is ::ffff:5.39.28.128 – ::ffff:5.39.28.191

注释

只需调用一次

inet_pton()
,即可将 IP 转换为数字。但面具的事情有点复杂。 122 位掩码表示具有 122 个 1 和 6 个 0 的二进制数。 PHP 无法处理这么大的数字,因此我们必须将该二进制字符串转换为十六进制数字。

每 4 个二进制数字变成一个十六进制数字,我们首先将所有开头的

1111
转换为
f
。复杂性出现在 1 和 0 之间的边界处,这就是
match
语句处理的内容(
1000
8
1100
c
,以及
1110
e
。)然后我们使用
 str_pad
填写剩余的零。

我们的 122 位掩码变成

ffffffffffffffffffffffffffffffc0
,然后将其打包成一个数字,以匹配
inet_pton
的输出。一旦我们将两个元素都设为数字,我们就可以进行简单的位数学计算来计算范围。

© www.soinside.com 2019 - 2024. All rights reserved.