calculate IP range using PHP and CIDR

后端 未结 5 738
故里飘歌
故里飘歌 2021-01-03 08:47

I\'ve seen various questions and answers around this site and I\'m still having difficulty wrapping my head around this problem (could be because I\'ve got a cold). Regardle

5条回答
  •  傲寒
    傲寒 (楼主)
    2021-01-03 08:50

    I created this php function in order to get the subnet splits of a network/range. It works with ipv4 or ipv6.
    If php<5.4 then use this hex2bin function 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;
    }
    

提交回复
热议问题