В 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 указывает на один адрес.)
4 ответа
В моем последнем ответе были ошибки, которые возникли, когда первый октет 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<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();
}
}
Вы должны понимать двоичные числа, не более того.
Блок 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 в качестве суффикса размера блока. Таким образом, вам нужно только найти следующий кратный некоторой степени двух.
Java-библиотека с открытым исходным кодом IPAddress может сделать это для вас. Отказ от ответственности: я руководитель проекта библиотеки IPAddress.
Вот пример метода, чтобы сделать это:
static void toPrefixBlocks(String str1, String str2) {
IPAddressString string1 = new IPAddressString(str1);
IPAddressString string2 = new IPAddressString(str2);
IPAddress one = string1.getAddress(), two = string2.getAddress();
IPAddressSeqRange range = one.toSequentialRange(two);
System.out.println("starting with range " + range);
IPAddress blocks[] = range.spanWithPrefixBlocks();
System.out.println("prefix blocks are " + Arrays.asList(blocks));
}
Используя ваш пример:
toPrefixBlocks("1.1.1.111","1.1.1.120");
Выход:
starting with range 1.1.1.111 -> 1.1.1.120
prefix blocks are [1.1.1.111/32, 1.1.1.112/29, 1.1.1.120/32]
Следующие блоки 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
и т.п.
Я закончил тем, что изменил некоторый код 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<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;
}
}
У меня есть несколько вспомогательных методов, которые в целях этого вопроса загромождают вещи. Но вы можете получить общее представление.