Php 将IPv6地址匹配到CIDR子网

Php 将IPv6地址匹配到CIDR子网,php,ip,ipv6,cidr,Php,Ip,Ipv6,Cidr,是否有一种使用CIDR表示法将IPv6地址与IPv6子网匹配的好方法? 我要寻找的是与此等效的IPv6: 无法使用上面给出的示例,因为IPv6地址的长度为128位,这会阻止按位左移正常工作。你能想出别的办法吗 编辑:将我自己的解决方案添加到答案列表中。由于无法将IPv6地址转换为整数,因此应操作位,如下所示: $ip='21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A'; $cidrnet='21DA:00D3:0000:2F3B::/64'; // conve

是否有一种使用CIDR表示法将IPv6地址与IPv6子网匹配的好方法? 我要寻找的是与此等效的IPv6:

无法使用上面给出的示例,因为IPv6地址的长度为128位,这会阻止按位左移正常工作。你能想出别的办法吗


编辑:将我自己的解决方案添加到答案列表中。

由于无法将IPv6地址转换为整数,因此应操作位,如下所示:

$ip='21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A';
$cidrnet='21DA:00D3:0000:2F3B::/64';

// converts inet_pton output to string with bits
function inet_to_bits($inet) 
{
   $splitted = str_split($inet);
   $binaryip = '';
   foreach ($splitted as $char) {
             $binaryip .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
   }
   return $binaryip;
}    

$ip = inet_pton($ip);
$binaryip=inet_to_bits($ip);

list($net,$maskbits)=explode('/',$cidrnet);
$net=inet_pton($net);
$binarynet=inet_to_bits($net);

$ip_net_bits=substr($binaryip,0,$maskbits);
$net_bits   =substr($binarynet,0,$maskbits);

if($ip_net_bits!==$net_bits) echo 'Not in subnet';
else echo 'In subnet';
SELECT 
   '21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A'::inet << 
   '21DA:00D3:0000:2F3B::/64'::inet;
此外,如果您使用某个数据库来存储IP,它可能已经具有比较IP的所有功能。例如,Postgres具有inet类型,可以确定IP是否包含在子网中,如下所示:

$ip='21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A';
$cidrnet='21DA:00D3:0000:2F3B::/64';

// converts inet_pton output to string with bits
function inet_to_bits($inet) 
{
   $splitted = str_split($inet);
   $binaryip = '';
   foreach ($splitted as $char) {
             $binaryip .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
   }
   return $binaryip;
}    

$ip = inet_pton($ip);
$binaryip=inet_to_bits($ip);

list($net,$maskbits)=explode('/',$cidrnet);
$net=inet_pton($net);
$binarynet=inet_to_bits($net);

$ip_net_bits=substr($binaryip,0,$maskbits);
$net_bits   =substr($binarynet,0,$maskbits);

if($ip_net_bits!==$net_bits) echo 'Not in subnet';
else echo 'In subnet';
SELECT 
   '21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A'::inet << 
   '21DA:00D3:0000:2F3B::/64'::inet;
选择

“21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A”::inet我使用以下代码创建了自己的解决方案:

function iPv6MaskToByteArray($subnetMask) {
  $addr = str_repeat("f", $subnetMask / 4);
  switch ($subnetMask % 4) {
    case 0:
      break;
    case 1:
      $addr .= "8";
      break;
    case 2:
      $addr .= "c";
      break;
    case 3:
      $addr .= "e";
      break;
  }
  $addr = str_pad($addr, 32, '0');
  $addr = pack("H*" , $addr);
  return $addr;
}

function iPv6CidrMatch($address, $subnetAddress, $subnetMask) {
  $binMask = iPv6MaskToByteArray($subnetMask);
  return ($address & $binMask) == $subnetAddress;
}
请注意,$address和$subnetAddress是通过inet\u pton运行字符串地址获得的。按如下方式调用函数:

$subnet = inet_pton("2001:06b8::");
$mask = 32;
$addr = inet_pton("2001:06b8:0000:0000:0000:0000:1428:07ab");
$match = iPv6CidrMatch($addr, $subnet, $mask); // TRUE

如果掩码总是可以被4整除(这在ipv6中非常常见)。您可以使用:

function checkIPv6WithinRange($ipv6, $range) {
    list ($net, $mask) = preg_split("/\//", $range);

    if ($mask % 4)
        throw new NotImplementedException("Only masks divisible by 4 are supported");
    $stripChars = (128-$mask)/4;

    $hexNet = bin2hex(inet_pton($net));
    $reducedNet = substr($hexNet, 0, 0 - $stripChars);

    $hexIp = bin2hex(inet_pton($ipv6));
    $reducedIp = substr($hexIp, 0, 0 - $stripChars);

    return $reducedIp === $reducedNet;
}
您还可以使用包中的类:

这将检查IPv6有效性和范围匹配。如果不是这样,将返回false。

基本上

要使用它,只需调用

$cidr = new CIDR();
$cidr->match($ipv6, $ipv6_in_cidr);

结果是“良好”。

下面是一个示例,它通过检查IP地址与IPv4和IPv6的单个IP或CIDR列表来工作:


PHP可以对字符串执行位操作

  • IPv4或IPv6
  • 无碱基转换
  • 没有ASCII位字符串
  • 很快

Nice-出于某种原因,我从未考虑过二进制字符串。“您无法将IPv6地址转换为整数”。我猜这是因为它们是128位,整数被限制在32或64位?我不理解解包调用。就我而言,没有它似乎效果更好。网络掩码通常以0结尾,它们将被删除。因此,
foreach(str_split($inet)as$char)
似乎是正确的,我有很多面具,它们在家里不能被4整除!简化:substr($hexNet,0,$mask/4)。在ipv4上也是如此:)“如果你的掩码总是可以被四整除”可能很常见,但正是这种任意的限制注定会对你造成伤害。不推荐此解决方案。此答案比OP要求的答案要多一些,但功能齐全。