Home > Article > Backend Development > IpToCidr函数
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";}
结果结果:可用地址: 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>"; }
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; }