这个 PHP 函数检查 IPV6 是否在给定前缀中是否正确?

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

我需要检查访客 IPv6 是否在给定前缀中(我有一些列入白名单的 ipv6 前缀)。

我使用 StackOverflow 中的两个不同的函数调整了下面的函数(一个是 将 ipv6 前缀转换为第一个和最后一个地址,另一个答案是测试给定的 IP 是否在给定的第一个 + 最后一个 ip 范围内),但是看看代码里有很多我不熟悉的功能,加上ipv6让我很困惑。

有人可以告诉我这段代码是否正确吗?根据我的测试,它验证了我发送给它的 IP:

<?php if(!function_exists('is_ipv6_in_prefix')){ function is_ipv6_in_prefix($ipv6_ip_to_check, $ipv6_prefix_to_check){ $ipv6_ip_to_check = inet_pton($ipv6_ip_to_check); // Split in address and prefix length list($firstaddrstr, $prefixlen) = explode('/', $ipv6_prefix_to_check); // Parse the address into a binary string $firstaddrbin = inet_pton($firstaddrstr); // Convert the binary string to a string with hexadecimal characters if(function_exists('bin2hex')){ $firstaddrhex = bin2hex($firstaddrbin); } else { // older php versions can use pack/unpack $firstaddrhex = reset(unpack('H*', $firstaddrbin)); } // Overwriting first address string to make sure notation is optimal $firstaddrstr = inet_ntop($firstaddrbin); // Calculate the number of 'flexible' bits $flexbits = 128 - $prefixlen; // Build the hexadecimal string of the last address $lastaddrhex = $firstaddrhex; // We start at the end of the string (which is always 32 characters long) $pos = 31; while ($flexbits > 0) { // Get the character at this position $orig = substr($lastaddrhex, $pos, 1); // Convert it to an integer $origval = hexdec($orig); // OR it with (2^flexbits)-1, with flexbits limited to 4 at a time $newval = $origval | (pow(2, min(4, $flexbits)) - 1); // Convert it back to a hexadecimal character $new = dechex($newval); // And put that character back in the string $lastaddrhex = substr_replace($lastaddrhex, $new, $pos, 1); // We processed one nibble, move to previous position $flexbits -= 4; $pos -= 1; } // Convert the hexadecimal string to a binary string if(function_exists('hex2bin')){ $lastaddrbin = hex2bin($lastaddrhex); } else { // older PHP versions can use pack/unpack $lastaddrbin = pack('H*', $lastaddrhex); } // And create an IPv6 address from the binary string $lastaddrstr = inet_ntop($lastaddrbin); // print string values for user // echo "\nPrefix: $ipv6_prefix_to_check"; // echo "\nFirst: $firstaddrstr"; // echo "\nLast: $lastaddrstr"; if ((strlen($ipv6_ip_to_check) == strlen($firstaddrbin)) && ($ipv6_ip_to_check >= $firstaddrbin && $ipv6_ip_to_check <= $lastaddrbin)) { // In range return true; } else { // Not in range return false; } } } if(is_ipv6_in_prefix('2a03:2880:f800:4::', '2a03:2880:f800::/48')){ echo "\nTRUE"; } else { echo "\nFALSE"; } ?>
我不确定,因为

另一个答案带有更正并说:

这是对已接受答案的修复,该答案错误地假设“第一个地址”应与输入的字符串相同。相反,它需要通过 AND 运算符针对其掩码修改其值。

为了演示该问题,请考虑以下示例输入:

2001:db8:abc:1403::/54


预期结果:

First: 2001:db8:abc:1400:: Actual result:


First: 2001:db8:abc:1403::


[...]修复的完整代码[...]

当我尝试应用上面“更正”中的代码时,它无法匹配此前缀内的 IP

2a03:2880:f800:4::

2a03:2880:f800::/48
。
也许我错误地应用了修复程序,但是最后提到的这个修正是否适用于我上面的最终代码?

php ip ip-address ipv6 cidr
1个回答
0
投票
你的整个过程是倒退的。检查可以完成

无需计算前缀的第一个和最后一个地址,甚至不需要“灵活位”计算 - 您需要做的就是比较给定地址和前缀的初始 X 位(二进制形式) .

二进制到十六进制的转换通常也是毫无意义的 - 如果您需要 hex2bin() 每个半字节对其进行操作,那么您最好将整个字符串二进制放在第一位,因为这对无论您使用的是 4 位还是 8 位单元(或 32 位单元),请遵循以下步骤。

示例:

function ip_cidr($host, $mask) { list ($net, $len) = explode("/", $mask, 2); $host = inet_pton($host); $net = inet_pton($net); if ($host === false || $net === false) throw new \InvalidArgumentException(); elseif (strlen($host) !== strlen($net)) return false; /* mismatching address families not an error */ $nbits = strlen($host) * 8; $len = strlen($len) ? intval($len) : $nbits; if ($len < 0 || $len > $nbits) throw new \DomainException(); elseif ($len == 0) return true; /* an /0 always matches */ elseif ($len == $nbits) return $host === $net; $host = unpack("C*", $host); $net = unpack("C*", $net); /* loop over each byte and compare them */ for ($i = 1; $i <= count($net) && $len > 0; $i++) { $bits = min($len, 8); /* if we have less than 8 bits left to check, generate an 'AND' mask for them */ $bmask = (0xFF00 >> $bits) & 0xFF; $len -= 8; //printf("pos = %d, bits = %d, bmask = %02x, host[%d] = %02x, net[%d] = %02x\n", // $i, $bits, $bmask, $i, $host[$i], $i, $net[$i]); if (($host[$i] & $bmask) !== ($net[$i] & $bmask)) return false; } return true; }
    
© www.soinside.com 2019 - 2024. All rights reserved.