Home  >  Article  >  Backend Development  >  IpToCidr函数

IpToCidr函数

WBOY
WBOYOriginal
2016-06-23 13:51:201102browse

function ip2cidr($ip_start,$ip_end) {  if(long2ip(ip2long($ip_start))!=$ip_start or long2ip(ip2long($ip_end))!=$ip_end) return !trigger_error('ip 不合法', E_USER_NOTICE);   $ipl_start = ip2long($ip_start);  $ipl_end = ip2long($ip_end);  if($ipl_start>0 && $ipl_end<0) $delta = ($ipl_end + 4294967296) - $ipl_start;  else $delta = $ipl_end - $ipl_start;  $netmask = sprintf('%032b', $delta);  if(ip2long($ip_start)==0 && substr_count($netmask,"1")==32) return "0.0.0.0/0";  if($delta<0 or ($delta>0 && $delta%2==0)) return !trigger_error("区间数量不合法 $delta", E_USER_NOTICE);  for($mask=0; $mask<32; $mask++) if($netmask[$mask]==1) break;  if(substr_count($netmask,"0")!=$mask) {    $w = strrpos($netmask, '0') + 1;    $m = pow(2, 32-$w) - 1;    $ip_start = long2ip(($ipl_start & ~$m)+$m+1);    return long2ip($ipl_start & ~$m) . "/$w," . ip2cidr($ip_start,$ip_end);  };  return "$ip_start/$mask";} 


上面是版主大大给写的函数
ip2cidr("36.96.0.0","36.223.255.255")
执行结果36.96.0.0/9

反查
http://www.itmop.com/tool/ipaddress.php
36.96.0.0/9 -> 36.0.0.1 -36.127.255.254  与36.96.0.0 - 36.223.255.255 不一致

有个网站,但是无法看到转换代码
http://ip2cidr.com/
36.96.0.0-36.223.255.255
转化为36.96.0.0/11
36.128.0.0/10
36.192.0.0/11  

拆分区间不好掌握。。。


回复讨论(解决方案)

结果结果:可用地址:	  8388606掩码:	      255.128.0.0网络:	      36.96.0.0第一个可用:	36.0.0.1   最后可用:	 36.127.255.254  广播:	      36.223.255.255

怎么不对啦?

????有??啊。


36.96.0.0/9 -> 36.0.0.1 -36.127.255.254  
                     与36.96.0.0 - 36.223.255.255 不一致

1楼 2楼的 兄弟说完 我以为我手误  结果自己又测试了下  确实ip2cidr("36.96.0.0","36.223.255.255") 执行结果36.96.0.0/9
而36.96.0.0/9 生成IP的区间为 36. 0.0.1 -36. 127.255.254    

他那个计算和你的需求是不一样的
他算出的是,在给定的掩码条件下,一个 ip 可以隶属于哪个网段

需求不同,结果也不同

那可能我之前表述错误了 我的需求就是 用函数实现首IP地址和尾IP地址的区间转化为CIDR格式  这样就可以用来查询某个IP地址是否属于这个CIDR区域,其中检验的方法就是用CIDR再反生成首IP地址和尾IP地址的区间。
函数功能类似于http://ip2cidr.com 上的



其实这是 ip2cidr 的 bug
函数的原型来自 php 手册,只是增加了分段的功能
可以看到,分段是在原函数发现做为一段不行的时候进行的
但由于先天不足,他并没有把你的传入不能作为一段判别出来

不知道 这个网站http://ip2cidr.com/  它是怎么实现的  网上的资源好少

一篇国外的文章用JAVA实现了

import java.util.ArrayList;import java.util.List;import java.util.regex.Matcher;import java.util.regex.Pattern;public class RangeToCidr {    private static final String IP_ADDRESS = "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})";    private static final Pattern addressPattern = Pattern.compile(IP_ADDRESS);    public static List<String> rangeToCidrList(String istart, String iend)  {               int start = toInteger(istart);        int end = toInteger(iend);        List<String> result = new ArrayList<String>();        while (end >= start) {            int maxsize = imaxblock( start, 32);            double x = (Math.log(end - start + 1) / Math.log(2) ) ;            int maxdiff = (int) (Math.floor(32 - Math.floor(x)));            String ip = intToIP(start);            if (maxsize < maxdiff) {                maxsize = maxdiff;            }            result.add( ip + "/" + (int)maxsize );            start += Math.pow(2, (32-maxsize));        }        return result;    }    private static int toInteger(String address) {        Matcher matcher = addressPattern.matcher(address);        if (matcher.matches()) {            return matchAddress(matcher);        }        else            throw new IllegalArgumentException("Could not parse [" + address + "]");    }    private static int matchAddress(Matcher matcher) {        int addr = 0;        for (int i = 1; i <= 4; ++i) {             int n = (rangeCheck(Integer.parseInt(matcher.group(i)), -1, 255));            addr |= ((n & 0xff) << 8*(4-i));        }        return addr;    }    private static int rangeCheck(int value, int begin, int end) {        if (value > begin && value <= end) // (begin,end]            return value;        throw new IllegalArgumentException("Value [" + value + "] not in range ("+begin+","+end+"]");    }    private static String intToIP(int val) {        int octets[] = new int[4];        for (int j = 3; j >= 0; --j)            octets[j] |= ((val >>> 8*(3-j)) & (0xff));        StringBuilder str = new StringBuilder();        for (int i =0; i < octets.length; ++i){            str.append(octets[i]);            if (i != octets.length - 1) {                str.append(".");             }        }        return str.toString();    }    private static long imask(int t)    {        return (long)(Math.pow(2, 32) - Math.pow(2, 32-t) ) ;    }    private static int imaxblock(long ibase, int tbit)  {        while (tbit > 0)    {            long im = imask(tbit-1);            long imand = ibase & im ;            if (imand != ibase) {                break;            }            tbit--;        }        return tbit;    }}

$r = ip2cidr("36.96.0.1","36.223.255.255");print_r($r);function ip2cidr($ip_start,$ip_end) {  $res = array();  if(long2ip(ip2long($ip_start))!=$ip_start or long2ip(ip2long($ip_end))!=$ip_end)    return !trigger_error('ip 不合法', E_USER_NOTICE);   $ipl_start = ip2long($ip_start);  if($ipl_start < 0) $ipl_start += 0x100000000;  $ipl_end = ip2long($ip_end);  if($ipl_end<0) $ipl_end += 0x100000000;  $ipl=$ipl_start;  do {    $k = strrpos(sprintf('%032b', $ipl), '1');    $cidr = $k + 1;    $dk = pow(2, 32-$k-1);    $mask = $dk - 1;    $res[] = sprintf("%s/%d", long2ip($ipl & ~$mask), $cidr);    $ipl += $dk;  }while($ipl < $ipl_end);  return $res;} 
Array(    [0] => 36.96.0.1/32    [1] => 36.96.0.2/31    [2] => 36.96.0.4/30    [3] => 36.96.0.8/29    [4] => 36.96.0.16/28    [5] => 36.96.0.32/27    [6] => 36.96.0.64/26    [7] => 36.96.0.128/25    [8] => 36.96.1.0/24    [9] => 36.96.2.0/23    [10] => 36.96.4.0/22    [11] => 36.96.8.0/21    [12] => 36.96.16.0/20    [13] => 36.96.32.0/19    [14] => 36.96.64.0/18    [15] => 36.96.128.0/17    [16] => 36.97.0.0/16    [17] => 36.98.0.0/15    [18] => 36.100.0.0/14    [19] => 36.104.0.0/13    [20] => 36.112.0.0/12    [21] => 36.128.0.0/9)

还有个更简单的 其中2个函数PHP都有库函数  实测也可以

import java.util.ArrayList;import java.util.List;public class RangeToCidr {    public static List<String> range2cidrlist( String startIp, String endIp ) {                 long start = ipToLong(startIp);                 long end = ipToLong(endIp);                   ArrayList<String> pairs = new ArrayList<String>();                 while ( end >= start ) {                         byte maxsize = 32;                         while ( maxsize > 0) {                                 long mask = CIDR2MASK[ maxsize -1 ];                                 long maskedBase = start & mask;                                 if ( maskedBase != start ) {                                         break;                                 }                                 maxsize--;                         }                           double x = Math.log( end - start + 1) / Math.log( 2 );                         byte maxdiff = (byte)( 32 - Math.floor( x ) );                         if ( maxsize < maxdiff) {                                 maxsize = maxdiff;                         }                         String ip = longToIP(start);                         pairs.add( ip + "/" + maxsize);                         start += Math.pow( 2, (32 - maxsize) );                 }                 return pairs;         }           public static final int[] CIDR2MASK = new int[] { 0x00000000, 0x80000000,                     0xC0000000, 0xE0000000, 0xF0000000, 0xF8000000, 0xFC000000,                     0xFE000000, 0xFF000000, 0xFF800000, 0xFFC00000, 0xFFE00000,                     0xFFF00000, 0xFFF80000, 0xFFFC0000, 0xFFFE0000, 0xFFFF0000,                     0xFFFF8000, 0xFFFFC000, 0xFFFFE000, 0xFFFFF000, 0xFFFFF800,                     0xFFFFFC00, 0xFFFFFE00, 0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0,                     0xFFFFFFE0, 0xFFFFFFF0, 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE,                     0xFFFFFFFF };           private static long ipToLong(String strIP) {                 long[] ip = new long[4];                 String[] ipSec = strIP.split("\\.");                 for (int k = 0; k < 4; k++) {                         ip[k] = Long.valueOf(ipSec[k]);                 }                 return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3];         }           private static String longToIP(long longIP) {                 StringBuffer sb = new StringBuffer("");                 sb.append(String.valueOf(longIP >>> 24));                 sb.append(".");                 sb.append(String.valueOf((longIP & 0x00FFFFFF) >>> 16));                 sb.append(".");                 sb.append(String.valueOf((longIP & 0x0000FFFF) >>> 8));                 sb.append(".");                 sb.append(String.valueOf(longIP & 0x000000FF));           return sb.toString();         } }

版主大大 看12楼的 JAVA 函数思路 是否可以优化您的代码 以生成3个CIDR 36.96.0.0/11  36.128.0.0/10 36.192.0.0/11  
最终让这个函数在PHP沉淀下来 为大家造福

$r = ip2cidr("36.96.0.0","36.223.255.255");print_r($r);

Array(    [0] => 36.96.0.0/11    [1] => 36.128.0.0/9)

你给的那个链接,最后一节的结果是错的

您说的是http://ip2cidr.com/   还是12楼的函数?


确实 36.128.0.0/9  已经超出了36.223.255.255

$r = ip2cidr("36.96.0.0","36.223.255.255");print_r($r);function ip2cidr($ip_start,$ip_end) {  $res = array();  if(long2ip(ip2long($ip_start))!=$ip_start or long2ip(ip2long($ip_end))!=$ip_end)    return !trigger_error('ip 不合法', E_USER_NOTICE);   $ipl_start = ip2long($ip_start);  if($ipl_start < 0) $ipl_start += 0x100000000;  $ipl_end = ip2long($ip_end);  if($ipl_end<0) $ipl_end += 0x100000000;  $ipl=$ipl_start;  do {    $k = strrpos(sprintf('%032b', $ipl), '1');    $cidr = $k + 1;    $dk = pow(2, 32-$k-1);    if($ipl + $dk > $ipl_end) { $dk /= 2; $cidr++; }    $mask = $dk - 1;    $res[] = sprintf("%s/%d", long2ip($ipl & ~$mask), $cidr);    $ipl += $dk;  }while($ipl < $ipl_end);  return $res;} 
Array(    [0] => 36.96.0.0/11    [1] => 36.128.0.0/10    [2] => 36.192.0.0/11)

我试试不同的IP地址段 做个测试

拆的好开呀。。。 版主大大 看看 还能优化下吗

$ip_st = "103.233.183.0";$ip_end = "103.233.183.255";print $ip_st."-".$ip_end."<br>";$cidr_arr =  ip2cidr($ip_st,$ip_end );print_r($cidr_arr);print "<br>";for($i = 0 ; $i < count($cidr_arr); $i++){	print $cidr_arr[$i]." : ";	print cidr_conv($cidr_arr[$i])."<br>";	}


103.233.183.0-103.233.183.255
Array ( [0] => 103.233.183.0/25 [1] => 103.233.183.128/26 [2] => 103.233.183.192/27 [3] => 103.233.183.224/28 [4] => 103.233.183.240/29 [5] => 103.233.183.248/30 [6] => 103.233.183.252/31 [7] => 103.233.183.254/32 ) 
103.233.183.0/25 : 103.233.183.0 - 103.233.183.127
103.233.183.128/26 : 103.233.183.128 - 103.233.183.191
103.233.183.192/27 : 103.233.183.192 - 103.233.183.223
103.233.183.224/28 : 103.233.183.224 - 103.233.183.239
103.233.183.240/29 : 103.233.183.240 - 103.233.183.247
103.233.183.248/30 : 103.233.183.248 - 103.233.183.251
103.233.183.252/31 : 103.233.183.252 - 103.233.183.253
103.233.183.254/32 : 103.233.183.254 - 103.233.183.254

function cidr_conv($cidr_address) {  $first = substr($cidr_address, 0, strpos($cidr_address, "/"));  $netmask = substr(strstr($cidr_address, "/"), 1);  $first_bin = str_pad(decbin(ip2long($first)), 32, "0", STR_PAD_LEFT);  $netmask_bin = str_pad(str_repeat("1", (integer)$netmask), 32, "0", STR_PAD_RIGHT);    for ($i = 0; $i < 32; $i++) {    if ($netmask_bin[$i] == "1")       $last_bin .= $first_bin[$i];    else      $last_bin .= "1";  }  $last = long2ip(bindec($last_bin));  return "$first - $last";}

$r = ip2cidr("103.233.183.0", "103.233.183.255");print_r($r);function ip2cidr($ip_start,$ip_end) {  $res = array();  if(long2ip(ip2long($ip_start))!=$ip_start or long2ip(ip2long($ip_end))!=$ip_end)    return !trigger_error('ip 不合法', E_USER_NOTICE);   $ipl_start = ip2long($ip_start);  if($ipl_start < 0) $ipl_start += 0x100000000;  $ipl_end = ip2long($ip_end);  if($ipl_end<0) $ipl_end += 0x100000000;  $ipl=$ipl_start;  do {    $k = strrpos(sprintf('%032b', $ipl), '1');    $cidr = $k + 1;    $dk = pow(2, 32-$k-1);    if($ipl + $dk > $ipl_end + 1) { $dk /= 2; $cidr++; }    $mask = $dk - 1;    $res[] = array(      'cidr' => sprintf("%s/%d", long2ip($ipl & ~$mask), $cidr),      'mask' => long2ip(~$mask),      'net' => long2ip($ipl & ~$mask),      'usable' => $mask - 1,      'start' => long2ip(($ipl & ~$mask) + 1),      'end' => long2ip(($ipl | $mask) - 1),      'broadcast' => long2ip($ipl | $mask),    );    $ipl += $dk;  }while($ipl < $ipl_end);  return $res;}
Array(    [0] => Array        (            [cidr] => 103.233.183.0/24            [mask] => 255.255.255.0            [net] => 103.233.183.0            [usable] => 254            [start] => 103.233.183.1            [end] => 103.233.183.254            [broadcast] => 103.233.183.255        ))

$r = ip2cidr("210.80.0.0", "210.80.63.255");
print_r($r);

Array

      [0] => Array 
      (
              [cidr] => 210.80.0.0/13
              [mask] => 255.248.0.0
              [net] => 210.80.0.0 
              [usable] => 524286
              [start] => 210.80.0.1 
              [end] => 210.87.255.254
              [broadcast] => 210.87.255.255 
       )
 ) 
这块就存在BUG了  您看下12楼的方法是否合适

最终函数:

$CIDR2MASK = array( 0x00000000, 0x80000000,                     0xC0000000, 0xE0000000, 0xF0000000, 0xF8000000, 0xFC000000,                     0xFE000000, 0xFF000000, 0xFF800000, 0xFFC00000, 0xFFE00000,                     0xFFF00000, 0xFFF80000, 0xFFFC0000, 0xFFFE0000, 0xFFFF0000,                     0xFFFF8000, 0xFFFFC000, 0xFFFFE000, 0xFFFFF000, 0xFFFFF800,                     0xFFFFFC00, 0xFFFFFE00, 0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0,                     0xFFFFFFE0, 0xFFFFFFF0, 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE,                     0xFFFFFFFF );        function ipTocidr($ip_start,$ip_end) { 	global $CIDR2MASK;       $StartIp = ip2long($ip_start);        $EndIp = ip2long($ip_end);   $pairs = array();             while ( $EndIp >= $StartIp )   {                   $maxsize = 32;               while ( $maxsize > 0)       {                      $mask = $CIDR2MASK[$maxsize-1];                        $maskedBase = $StartIp & $mask ;                           if ( $maskedBase != $StartIp )           {                                  break;                           }                           $maxsize--;                   }                 $x = log( $EndIp -  $StartIp + 1) / log(2);                   $maxdiff = 32 - floor($x) ;                   if ( $maxsize < $maxdiff)       {                           $maxsize = $maxdiff;                   }                   $ip = long2ip($StartIp);       $pairs[] = $ip."/".$maxsize;                                      $StartIp += pow( 2, (32 - $maxsize) );         }           return $pairs;     }   

谢谢版主大大的悉心指导,最后还是将JAVA的函数翻译了

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn