В Java, учитывая диапазон IP-адресов, вернуть минимальный список блоков CIDR, который охватывает диапазон - PullRequest
5 голосов
/ 16 февраля 2011

У меня возникли проблемы с некоторой логикой при преобразовании диапазона 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, указывающим один адрес.)

Ответы [ 5 ]

11 голосов
/ 17 февраля 2011

В моем последнем ответе были ошибки, которые возникли, когда первый октет 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();     
    } 
}
5 голосов
/ 16 февраля 2011

Вам нужно понимать двоичные числа, не более того.

Блок 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 в качестве суффикса размера блока. Таким образом, вам нужно только найти следующий кратный некоторой степени двойки.

0 голосов
/ 09 марта 2019

Java-библиотека с открытым исходным кодом и открытым исходным кодом может сделать это за вас.Отказ от ответственности: я руководитель проекта библиотеки 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]
0 голосов
/ 26 февраля 2011

Следующие блоки 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

и т. Д.

0 голосов
/ 17 февраля 2011

Я закончил тем, что изменил некоторый код 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;
    }
}

У меня есть несколько вспомогательных методов, которые в целях этого вопроса загромождают вещи.Но вы можете получить общее представление.

...