Преобразование из нотации CIDR в IP-адрес/маску подсети (десятичное число)

 /*
 * RFC 1518, 1519 - Classless Inter-Domain Routing (CIDR)
 * This converts from "prefix + prefix-length" format to
 * "address + mask" format, e.g. from xxx.xxx.xxx.xxx/yy
 * to xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy.
 */
static private String normalizeFromCIDR(final String netspec)
{
    final int bits = 32 - Integer.parseInt(netspec.substring(netspec.indexOf('/')+1));
    final int mask = (bits == 32) ? 0 : 0xFFFFFFFF - ((1 << bits)-1); 

    return netspec.substring(0, netspec.indexOf('/') + 1) +
            Integer.toString(mask >> 24 & 0xFF, 10) + "." +
            Integer.toString(mask >> 16 & 0xFF, 10) + "." +
            Integer.toString(mask >>  8 & 0xFF, 10) + "." +
            Integer.toString(mask >>  0 & 0xFF, 10);
}

Это функция в apache james для преобразования ip в указанный формат. Не могли бы вы объяснить, что происходит внутри функции. Запутался с этим сдвигом битов и преобразованием. Заранее спасибо.


person Jobin    schedule 30.04.2014    source источник


Ответы (2)


Битовые операции, возможно, на первый взгляд не самые интуитивные, но как только вы их освоите, вы увидите, что их довольно легко понять. Я попытаюсь объяснить, что делает этот код, на примере 172.16.0.1/23 в виде строки netspec.

Часть 1 — CIDR в двоичный файл

Цель состоит в том, чтобы создать двоичное представление маски подсети из заданной длины префикса CIDR. Длина префикса CIDR — это всего лишь 1 бита в маске подсети. Первая линия

final int bits = 32 - Integer.parseInt(netspec.substring(netspec.indexOf('/')+1));

находит длину префикса CIDR, получая индекс / и анализируя целое число, следующее за ним (23 в моем примере). Это число вычитается из 32, чтобы получить число 0 в маске подсети — эти биты также называются битами хоста.

В этом примере мы знаем, что имеем дело с префиксом /23 и что его маска подсети должна выглядеть так:

n представляет сеть (16 бит для сети класса B), s представляет подсеть, h представляет хост. Для нас биты сети и подсети функционально одинаковы, но я сделал различие, чтобы быть точным. Нас интересуют только биты хоста (их количество).

nnnnnnnn nnnnnnnn sssssssh hhhhhhhh
11111111 11111111 11111110 00000000

Самый простой способ сделать это - иметь 32-битное двоичное число всех 1 и «заполнить» последние 9 бит 0. Здесь появляется вторая строка:

Вы можете игнорировать проверку bits == 32, так как она не так актуальна и, вероятно, используется только для оптимизации.

//final int mask = (bits == 32) ? 0 : 0xFFFFFFFF - ((1 << bits)-1); 
final int mask = 0xFFFFFFFF - ((1 << 9)-1); 

0xFFFFFFFF даст вам 32-битное двоичное число всех 1. 1 сдвинутый влево на 9 бит (1 << bits) даст вам 512, а 512 - 1 в двоичном виде будет 111111111:

  1 << 9                               10 00000000
-      1                                         1
--------------------------------------------------
                                        1 11111111

Когда вы вычтете эти значения, вы получите маску подсети в двоичном виде:

  0xFFFFFFFF = 11111111 11111111 11111111 11111111
- (1 << 9)-1 =                          1 11111111
--------------------------------------------------
               11111111 11111111 11111110 00000000

Это именно та маска сети, которую мы хотели.

Примечание. Возможно, это не самый интуитивный способ вычисления двоичного значения. Мне нравится начинать с двоичного числа из всех единиц, и это число в формате int со знаком имеет десятичное значение -1. Затем я просто сдвигаю количество бит хоста влево, и все. (Кроме того, если вы имеете дело с целыми числами размером более 32 бит, вы можете замаскировать их с помощью 0xFFFFFFFF):

(-1 << 9) & 0xFFFFFFFF

Часть 2. Двоичный код в десятичный с точками

Остальная часть кода преобразует двоичное значение в десятичное представление с точками — 255.255.254.0.

return netspec.substring(0, netspec.indexOf('/') + 1) +  // part of the netspec string before '/' -> IP address
        Integer.toString(mask >> 24 & 0xFF, 10) + "." +  //                         11111111 & 0xFF = 0xFF
        Integer.toString(mask >> 16 & 0xFF, 10) + "." +  //                 1111111111111111 & 0xFF = 0xFF
        Integer.toString(mask >>  8 & 0xFF, 10) + "." +  //         111111111111111111111110 & 0xFF = 0xFE
        Integer.toString(mask >>  0 & 0xFF, 10);         // 11111111111111111111111000000000 & 0xFF = 0x00

Оператор return состоит из нескольких соединенных строк, начиная с IP-адреса и заканчивая десятичным представлением каждого октета. Двоичная маска сдвигается вправо на (4-n)*8 бит (где n — номер октета), и, используя двоичное И с 0xFF, вы получаете только последние 8 бит, которые затем анализируются Integer.toString.

Результат 172.16.0.1/255.255.254.0.

person pajaja    schedule 02.02.2015

другой способ получить cidr-нотацию в двоичном виде:

input = '1.2.3.4/5'
cidr = input.split('/') 

bin_mask = '1' * cidr + '0' * (32 - cidr)
person tipkopf    schedule 15.09.2019