使用PHP和CIDR计算IP范围

时间:2013-04-12 01:18:59

标签: php cidr

我在这个网站上看到了各种各样的问题和答案,我仍然很难解决这个问题(可能是因为我感冒了)。无论如何,我正在努力想出一个小型的网络应用程序,它将为我们的每个办公室创建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将地址存储为整数但仍然存在。

5 个答案:

答案 0 :(得分:11)

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

给定CIDR前缀(例如,您的范围为$prefix = 30),您可以使用bit shift operator计算该范围内的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...
}

答案 1 :(得分:4)

我正在使用以下功能为我提供网络,第一个可用的,最后一个可用的以及广播地址以及所有主机:

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地址和子网掩码。

希望这可以帮助你或其他任何人。

答案 2 :(得分:1)

我的版本可以帮助您使用变量。

<?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>";
}

答案 3 :(得分:0)

这是基于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

答案 4 :(得分:0)

我创建了这个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;
}