...我的看法:
在我看来,底层的问题只是“如何检查IP地址是否属于网络?”。
答案很简单:IP地址 AND 网络掩码 EQUALS 网络地址。
例如,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;
}
我现在想知道的是:IPv6中“IPv4映射地址”中的IPV6地址即使在IPv4中是“常规”的IP地址,它也是IPv6中的“特殊”地址。我们是否应该将匹配IPv4专用网络的::ffff:0:0/96子网视为“私有使用”?
编辑以解释最后一条评论:
IPv6网络::ffff:0:0/96将每个IPv4地址映射到一个IPv6地址。这些IPv6地址在IANA注册表中处于单个集合中(“特殊用途”),但映射的IPv4地址在IPv4中的各种集合中(私有网络、环回、广播、公共...)。 “常见IPv4地址”始终是“特殊IPv6地址”。如果我们使用匹配IPv4专用网络的::ffff:0:0/96范围内的IPv6地址设置网络...我们是否正在使用私有网络地址?
global $ip;
来访问该变量了吗? - ott--