...我的 5 美分:
恕我直言,根本问题只是“如何检查 IP 地址是否属于网络?”。
答案是简单的二进制:IP_address AND network_mask EQUALS network_address。
例如,IP 地址 10.1.2.3 是否属于网络 10.0.0.0,网络掩码为 255.0.0.0?10.1.2.3 & 255.0.0.0 是 10.0.0.0,所以答案是:是的。
更容易在二进制中看到它:
00001010 00000001 00000010 00000011 ( 10.1.2.3) ip address
& 11111111 00000000 00000000 00000000 (255.0.0.0) network mask
= 00001010 00000000 00000000 00000000 ( 10.0.0.0) network address
只需检查您需要的网络(包括或不包括环回、链接本地等):
function _isPrivate($long_ip) {
return ( ($long_ip & 0xFF000000) === 0x0A000000 ) || //Private A network: 00001010 ....
( ($long_ip & 0xFFF00000) === 0xAC100000 ) || //Private B network: 10101100 0001....
( ($long_ip & 0xFFFF0000) === 0xC0A80000 ) || //Private C network: 11000000 10101000 ....
//Link-local and loopback are NOT private range, so the function in the question yield right results to "is in private range?". Seems it was not the desired behaviour... Those cases can also be checked:
( ($long_ip & 0xFFFF0000) === 0xA9FE0000 ) || //Link-local : 10101001 11111110 ....
( ($long_ip & 0xFFFF0000) === 0x7F000000 ) || //Loopback : 01111111 ....
//...and add all the fancy networks that you want...
( ($long_ip & 0xFFFFFF00) === 0xC0AF3000 ) || //Direct Delegation AS112 Service 192.175.48.0/24...
( ($long_ip & 0xF0000000) === 0xF0000000 ); //Reserved 240.0.0.0/4
}
有趣的一点是返回值的否定。返回的值并不真正意味着给定的 IP 在专用网络中,但它的否定确实意味着给定的 IP 是“公共 IP 地址”(普通/普通 IP 地址),正如 user4880112 的解决方案所表明的那样。
IPv6
这同样适用于 IPv6。“专用网络”地址(正式名称为“Unique-Local”,RFC 4193)是“fc00::/7”。所以,ip_address & 0xFE00.. === 0xFC00.. 是“私有网络”
采用上述答案并包括来自 IANA 的最新信息......
http://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
http://www.iana.org/assignments/iana-ipv4-special-registry/iana -ipv4-special-registry.xhtml
...我们可以像这样做一个更通用的函数:
function isPublicAddress($ip) {
// returns false on failure.
// negative if it's a private or special address (-4:IPv4, -16:IPv6)
// positive if it's a common IP public address (4:IPv4, 16:IPv6)
$networks = array(
'4' => array('0.0.0.0/8',
'10.0.0.0/8',
'100.64.0.0/10',
'127.0.0.0/8',
'169.254.0.0/16',
'172.16.0.0/12',
'192.0.0.0/24',
'192.0.0.0/29',
'192.0.0.8/32',
'192.0.0.9/32',
'192.0.0.170/32',
'192.0.0.170/32',
'192.0.2.0/24',
'192.31.196.0/24',
'192.52.193.0/24',
'192.88.99.0/24',
'192.168.0.0/16',
'192.175.48.0/24',
'198.18.0.0/15',
'198.51.100.0/24',
'203.0.113.0/24',
'240.0.0.0/4',
'255.255.255.255/32')
,
'16' => array('::1/128',
'::/128',
'::ffff:0:0/96',
'64:ff9b::/96',
'100::/64',
'2001::/23',
'2001::/32',
'2001:1::1/128',
'2001:2::/48',
'2001:3::/32',
'2001:4:112::/48',
'2001:5::/32',
'2001:10::/28',
'2001:20::/28',
'2001:db8::/32',
'2002::/16',
'2620:4f:8000::/48',
'fc00::/7',
'fe80::/10')
);
$ip = inet_pton($ip);
if( $ip === false ) return false;
$space='16';
if (strlen($ip) === 4) {
$space='4';
}
//Is the IP in a private or special range?
foreach($networks[$space] as $network) {
//split $network in address and mask
$parts=explode('/',$network);
$network_address = inet_pton($parts[0]);
$network_mask = inet_pton( _mask( $ip , $parts[1] ) );
if (($ip & $network_mask) === $network_address){
return -1*$space;
}
}
//Success!
return $space;
}
function _mask($ip,$nbits){
$mask='';
$nibble=array('0','8','C','E');
$f_s= $nbits >> 2 ;
if( $f_s > 0 ) $mask.=str_repeat('F',$f_s);
if( $nbits % 4 ) $mask.= $nibble[$nbits % 4];
if( strlen($ip) === 4 ){
if( strlen($mask) < 8 ) $mask.=str_repeat('0', 8 - strlen($mask) );
long2ip('0x'.$mask);
$mask=long2ip('0x'.$mask);
}else{
if( strlen($mask) < 32 ) $mask.=str_repeat('0', 32 - strlen($mask) );
$mask=rtrim(chunk_split($mask,4,':'),':');
}
return $mask;
}
我现在想知道的是:“IPv4 映射地址”中的 IPv6 地址是 IPv6 中的“特殊”地址,即使它是 IPv4 中的“普通”IP 地址。我们是否应该考虑“私人使用” ::ffff:0:0/96 中匹配 IPv4 私人使用网络的子网?
编辑解释最后一条评论:
IPv6 网络 ::ffff:0:0/96 映射到每个 IPv4 地址的 IPv6 地址。这些 IPv6 地址在 IANA 注册中心(“特殊用途”)中的单个集合中,但映射的 IPv4 地址在 IPv4 中的所有类型的集合中(私有网络、环回、广播、公共......)“通用 IPv4地址”总是一个“特殊的 IPv6 地址”。如果我们使用与 IPv4 专用网络匹配的 ::ffff:0:0/96 范围内的 IPv6 地址设置网络...我们使用的是专用网络地址吗?