我有一个起始 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 网络掩码长度。
你在这里。我已从
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
解决方案非常简单:
// 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 数据。
有点奇怪,每个人都忽略了在提出问题 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
的输出。一旦我们将两个元素都设为数字,我们就可以进行简单的位数学计算来计算范围。