使用PHP和CIDR计算IP范围


calculate IP range using PHP and CIDR

我在这个网站上看到了各种各样的问题和答案,但我仍然很难理解这个问题(可能是因为我感冒了)。无论如何,我正在努力想出一个小的web应用程序,它将为我们每个办公室创建IP地址表。

例如,如果我为10.1.10.0/4创建一个新的作用域,它将创建一个数组(然后我可以将其打印到表中):
 10.1.10.0 network ID
 10.1.10.1 gateway
 10.1.10.2 usable
 10.1.10.3 broadcast

(不是说它会自动插入描述,但这就是我们要做的)。

我很确定我将使用ip2long/long2ip将地址存储为整数,但仍然。

正如您已经注意到的,所有IPv4地址都可以使用ip2long()转换为数字,然后使用long2ip()转换回来。我不确定您是否注意到,关键的额外位是顺序ip与顺序数字相对应,因此您可以操纵这些数字!

给定一个CIDR前缀(例如,$prefix = 30为您的范围),您可以使用位移位运算符计算该范围内的ip数量:

$ip_count = 1 << (32 - $prefix);

然后循环该范围内的所有ip,使用:

$start = ip2long($start_ip);
for ($i = 0; $i < $ip_count; $i++) {
    $ip = long2ip($start + $i);
    // do stuff with $ip...
}

下面是基于CIDR计算真实IP范围的实际方法:

上面的答案实际上是不正确的。它给出了错误的IP范围列表。

function ipRange($cidr) {
   $range = array();
   $cidr = explode('/', $cidr);
   $range[0] = long2ip((ip2long($cidr[0])) & ((-1 << (32 - (int)$cidr[1]))));
   $range[1] = long2ip((ip2long($range[0])) + pow(2, (32 - (int)$cidr[1])) - 1);
   return $range;
}
 var_dump(ipRange("207.64.1.68/28"));
 //////////////////OUTPUT////////////////////////
 // array(2) {
 //   [0]=>
 //   string(12) "207.64.1.64"
 //   [1]=>
 //   string(12) "207.64.1.79"
 // }
 /////////////////////////////////////////////////

IP: 207.64.1.68

应该给我207.64.1.64和207.64.1.79范围内的所有ip

其他答案没有减法,它们只从207.64.1.68到207.64.1.83(在IP范围块中不正确)

你可以在这里查看:https://www.ipaddressguide.com/cidr

我使用以下函数为我提供网络,第一个可用,最后一个可用和广播地址以及所有主机:

function ipv4Breakout ($ip_address, $ip_nmask) {
    $hosts = array();
    //convert ip addresses to long form
    $ip_address_long = ip2long($ip_address);
    $ip_nmask_long = ip2long($ip_nmask);
    //caculate network address
    $ip_net = $ip_address_long & $ip_nmask_long;
    //caculate first usable address
    $ip_host_first = ((~$ip_nmask_long) & $ip_address_long);
    $ip_first = ($ip_address_long ^ $ip_host_first) + 1;
    //caculate last usable address
    $ip_broadcast_invert = ~$ip_nmask_long;
    $ip_last = ($ip_address_long | $ip_broadcast_invert) - 1;
    //caculate broadcast address
    $ip_broadcast = $ip_address_long | $ip_broadcast_invert;
    foreach (range($ip_first, $ip_last) as $ip) {
            array_push($hosts, $ip);
    }
    $block_info = array(array("network" => "$ip_net"),
            array("first_host" => "$ip_first"),
            array("last_host" => "$ip_last"),
            array("broadcast" => "$ip_broadcast"),
            $hosts);
    return $block_info;
}

我还注意到你要求基于CIDR符号进行计算。下面是我用来将CIDR转换为点分十进制的函数:

function v4CIDRtoMask($cidr) {
    $cidr = explode('/', $cidr);
    return array($cidr[0], long2ip(-1 << (32 - (int)$cidr[1])));
}

我主要处理点分十进制,不使用CIDR表示法。ipv4Breakout函数通过长格式返回一个多维数组,其中包含您需要的所有信息。如果需要实际的点分十进制IP地址,则需要使用long2ip()。需要输入IP地址和子网掩码,格式为点分十进制。

我的版本帮助你使用变量。

<?php
$ip_address = "192.168.0.2";
$ip_nmask = "255.255.255.0";
ipv4Breakout($ip_address, $ip_nmask);
function ipv4Breakout ($ip_address, $ip_nmask) {
    //convert ip addresses to long form
    $ip_address_long = ip2long($ip_address);
    $ip_nmask_long = ip2long($ip_nmask);
    //caculate network address
    $ip_net = $ip_address_long & $ip_nmask_long;
    //caculate first usable address
    $ip_host_first = ((~$ip_nmask_long) & $ip_address_long);
    $ip_first = ($ip_address_long ^ $ip_host_first) + 1;
    //caculate last usable address
    $ip_broadcast_invert = ~$ip_nmask_long;
    $ip_last = ($ip_address_long | $ip_broadcast_invert) - 1;
    //caculate broadcast address
    $ip_broadcast = $ip_address_long | $ip_broadcast_invert;
    //Output
    $ip_net_short = long2ip($ip_net);
    $ip_first_short = long2ip($ip_first);
    $ip_last_short = long2ip($ip_last);
    $ip_broadcast_short = long2ip($ip_broadcast);
    echo "Network - " . $ip_net_short . "<br>";
    echo "First usable - " . $ip_first_short . "<br>";
    echo "Last usable - " . $ip_last_short . "<br>";
    echo "Broadcast - " . $ip_broadcast_short . "<br>";
}

我创建了这个php函数,以便获得网络/范围的子网分割。它适用于ipv4或ipv6。
如果php<5.4,则使用此hex2bin函数https://github.com/dompdf/dompdf/issues/1692

function divide_ip_range($cidr="2001:db8:abc:12ff::/54",$mindivs=2){ // input range 192.168.4.0/24 and returns array with the 1st range and 2nd range [0] => 192.168.4.0/25 , [1] => 192.168.4.127/25
  $outarr=array();
  list($ipaddr,$range) = explode('/', $cidr);
  for($rngsplit=1;pow(2,$rngsplit)<$mindivs;$rngsplit++){} // find to how many networks to divide
  $divs=pow(2,$rngsplit);    
  $divcidr=(int)$range+$rngsplit;
  if(preg_match("/^[0-9]{1,3}'.[0-9]{1,3}'.[0-9]{1,3}'.[0-9]{1,3}$/",$ipaddr)){ // IPv4
    $ip_address_long = ip2long($ipaddr);
    $ip_nmask_long=(-1 << (32 - $range));
    $ip_net = $ip_address_long & $ip_nmask_long;
    $ip_broadcast_invert = ~$ip_nmask_long;
    $ip_last = ($ip_address_long | $ip_broadcast_invert) - 1;
    $ip_broadcast = $ip_address_long | $ip_broadcast_invert;
    $numofhosts=pow(2,32-$range)-2;
    for ($i=0;$i<$divs;$i++){
      $outarr[]=long2ip($ip_net+$i*ceil($numofhosts/$divs)+($i*ceil($numofhosts/$divs)%2) )."/$divcidr";
    }
    //echo "Net:$ipaddr/$range'nFirst:".long2ip($ip_net)."'nLast: ".long2ip($ip_last)."'nNumOfHosts:$numofhosts 'n";
  } else if (preg_match("/^[0-9a-f:]+$/",$ipaddr)) { // IPv6 section 
    $ip_addr_bin = inet_pton($ipaddr);
    $ip_addr_hex = bin2hex($ip_addr_bin);
    $flexbits = 128 - $range; // Calculate the number of 'flexible' bits for first net
    $pos = 31; $addr_hex_first = $ip_addr_hex; $addr_hex_last = $ip_addr_hex;
    while ($flexbits > 0) {
      $orig_val = hexdec(substr($ip_addr_hex, $pos, 1)); // dec value of pos char. ex. f=15
      $mask = 0xf << (min(4,$flexbits)); // calculate the subnet mask. min() prevents comparison to be negative 
      $new_val_first = $orig_val & $mask;
      $addr_hex_first = substr_replace($addr_hex_first, dechex($new_val_first) , $pos, 1); // Put hex character in pos
      $segmask=(pow(2,min(4,$flexbits))-1); // Last address: OR it with (2^flexbits)-1, with flexbits limited to 4 at a time
      $new_val_last = $orig_val | $segmask;
      $addr_hex_last = substr_replace($addr_hex_last, dechex($new_val_last) , $pos, 1);
      $pos--; $flexbits -= 4; // Next nibble
    }
    $partpos=(4*floor($pos/4)); // The 4 digits that vary by the subnetting
    $partfirst=substr($addr_hex_first,$partpos,4);
    $partlast=substr($addr_hex_last,$partpos,4);
    $numofhosts=(hexdec($partlast)+1-hexdec($partfirst));
    for ($i=0;$i<$divs;$i++){
      $partdiv=dechex(hexdec($partfirst)+$i*$numofhosts/$divs);
      $addr_hex_div=substr_replace($addr_hex_first, $partdiv , $partpos, 4);
      $outarr[]=inet_ntop(hex2bin($addr_hex_div))."/$divcidr";
    }  
    //echo "Net:$ipaddr/$range'nFirst:".inet_ntop(hex2bin($addr_hex_first))."'nLast:".inet_ntop(hex2bin($addr_hex_last))."'nNumOfHosts:$numofhosts'nDivide at partpos:$partpos ($partlast+1-$partfirst)/$divs=$partdiv'n";
  }
  return $outarr;
}