在Java中,给定IP地址范围,返回覆盖范围的最小CIDR块列表

我在将IP地址范围转换为CIDR块列表时遇到一些逻辑问题。 我相信这个网站做得很好: http : //ip2cidr.com/

我想传入一个起始IP地址和一个结束IP地址,并让java吐出所需的最小CIDR块列表,仅覆盖传入的范围,仅此而已。

例如,如果我传入1.1.1.111的起始地址和1.1.1.120的结束地址,我希望得到回报:1.1.1.111/32 1.1.1.112/29 1.1.1.120/32

(/ 32表示单个地址。)

我的最后一个答案有一些错误,当IP地址的第一个八位字节太大时就会出现这些错误。 这个更好用。 完全取自这里: http : //facedroid.blogspot.com/2010/06/ip-range-to-cidr.html

import java.util.ArrayList; import java.util.List; public class RangeToCidr { public static List range2cidrlist( String startIp, String endIp ) { long start = ipToLong(startIp); long end = ipToLong(endIp); ArrayList pairs = new ArrayList(); 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(); } } 

您需要了解二进制数,仅此而已。

CIDR块只不过是一系列具有公共前缀所有可能后缀的二进制数。 假设下面的例子我们有8位IP地址,类/1 ,…到/8

在您的情况下(暂时忽略1.1.1),我们将您的数字写为二进制数:

  1101111 - 111 1110000 - 112 1110001 - 113 ... 1110110 - 118 1110111 - 119 1111000 - 120 

您将看到所有数字都有一个共同的11前缀,但我们的列表不包含所有这些数字。 所以我们必须把它分成两个列表 – 一个用110 ,一个用111 。 第一个只包含一个数字,所以我们从中得到一个/8块( 111/8 )。

另一个列表(从112到120)不包含111所有数字(从那以后它将达到127),所以我们再次拆分 – 一个列表1110 ,另一个列表1111 。 第一个现在是完整的块1110???? (或112/4 ),第二个只有一个地址,即11111000 (或120/8 )。

所以,现在只扩展到32位而不是8位,并在Java中实现,你已经准备好了。

在数学术语中,一个块总是从x * 2 ^ n(x + 1)* 2 ^ n – 1 ,然后我们使用32-n作为块大小后缀。 所以你只需要找到两个幂的下一个倍数。

我最终重新调整了一些我找到的PHP代码并调整了我的需求。 以下是我最终的课程。

 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 rangeToCidrList(String istart, String iend) { int start = toInteger(istart); int end = toInteger(iend); List result = new ArrayList(); 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; } } 

我有一些帮助方法,为了这个问题的目的,混乱的东西。 但是你可以得到一般的想法。

以下CIDR块包含(不限于)地址范围1.1.1.111 – 1.1.1.120

/ 1 – / 27

 address prefix network DirectedBroadcast 1.1.1.111 /27 1.1.1.96 1.1.1.127 1.1.1.111 /26 1.1.1.64 1.1.1.127 1.1.1.111 /25 1.1.1.0 1.1.1.127 1.1.1.111 /24 1.1.1.0 1.1.1.255 

等等

Interesting Posts