Объяснение преобразователя cidr в сетевую маску в оболочке linux netmask2cdir и cdir2netmask

Я нашел следующие функции оболочки из этого topic

mask2cdr ()
{
   # Assumes there no "255." after a non-255 byte in the mask
   local x=${1##*255.}
   set -- 0^^^128^192^224^240^248^252^254^ $(( (${#1} - ${#x})*2 )) ${x%%.*}
   x=${1%%$3*}
   echo $(( $2 + (${#x}/4) ))
}


cdr2mask ()
{
   # Number of args to shift, 255..255, first non-255 byte, zeroes
   set -- $(( 5 - ($1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0
   [ $1 -gt 1 ] && shift $1 || shift
   echo ${1-0}.${2-0}.${3-0}.${4-0}
}

Не могли бы вы объяснить, как эти функции преобразуют cidr в сетевую маску и сетевую маску в cidr? Я не понимаю код.

Ответ 1

mask2cdr()

Чтобы получить префикс CIDR из десятичной сетевой маски, такой как этот:

255.255.192.0

вам сначала нужно преобразовать четыре октета в двоичный, а затем подсчитать наиболее значимые биты (например, количество ведущих):

11111111.11111111.11000000.00000000  # 18 ones = /18 in CIDR

Эта функция делает это довольно творчески. Во-первых, мы снимаем все ведущие октеты 255 (т.е. Октеты, все из которых находятся в двоичном виде) и сохраняем результаты в переменной x:

local x=${1##*255.}

Этот шаг использует расширение параметра, которое весь script опирается довольно сильно. Если мы продолжим с нашей примерной сетевой маской 255.255.192.0, теперь мы имеем следующие значения:

$1: 255.255.192.0
$x: 192.0

Затем мы устанавливаем три переменные: $1, $2 и $3. Они называются позиционные параметры; они очень похожи на обычные именованные переменные, но обычно устанавливаются, когда вы передаете аргументы функции script или. Мы можем установить значения непосредственно с помощью set --, например:

set -- foo bar  # $1 = foo, $2 = bar

Я предпочитаю использовать именованные переменные по позиционным параметрам, поскольку упрощает чтение и отладку скриптов, но конечный результат тот же. Положим $1 на:

0^^^128^192^224^240^248^252^254^

Это действительно просто таблица для преобразования определенных десятичных значений в двоичный код и подсчета количества бит 1. Мы вернемся к этому позже.

Положим $2 на

$(( (${#1} - ${#x})*2 ))

Это выглядит сложным, но на самом деле это просто подсчет количества бит 1, который мы разделили в первой команде. Это ломается до этого:

(number of chars in $1 - number of chars in $x) * 2

который в нашем случае работает

(13 - 5) * 2 = 16

Мы разделили два октета, чтобы мы получили 16. Имеет смысл.

Положим $3 в:

${x%%.*}

который является значением $x со всем, после снятия первого .. В нашем случае это 192.

Нам нужно преобразовать это число в двоичный код и подсчитать количество бит 1, поэтому вернемся к нашей "таблице преобразования". Мы можем разделить таблицу на равные куски по четыре символа:

0^^^  128^  192^  224^  240^  248^  252^  254^

В двоичном выражении вышеуказанные числа:

00000000 10000000 11000000 11100000 11110000 11111000 11111100 11111110
# 0 ones 1 one    2 ones   3 ones   ...

Если подсчитать слева, каждый четырехсимвольный блок в таблице соответствует дополнительному биту 1 в двоичном формате. Мы пытаемся преобразовать 192, поэтому сначала откройте крайнюю часть таблицы, от 192 и сохраните ее в x:

x=${1%%$3*}

Значение $x теперь

0^^^128^

который содержит два четырехсимвольных блока или два бита 1 в двоичном формате.

Теперь нам просто нужно добавить биты 1 из наших ведущих октетов 255 (всего 16, сохраненных в переменной $2) и бит 1 с предыдущего шага (всего 2):

echo $(( $2 + (${#x}/4) ))

где

${#x}/4

- количество символов в $x, деленное на четыре, т.е. количество четырехсимвольных блоков в $x.

Вывод:

18

cdr2mask()

Продолжайте работать с нашим предыдущим примером, у которого был префикс CIDR 18.

Мы используем set -- для установки позиционных параметров от $1 до $9:

$1: $(( 5 - ($1 / 8) ))  # 5 - (18 / 8) = 3 [integer math]
$2: 255
$3: 255
$4: 255
$5: 255
$6: $(( (255 << (8 - ($1 % 8))) & 255 ))  # (255 << (8 - (18 % 8))) & 255 = 192
$7: 0
$8: 0
$9: 0

Давайте рассмотрим формулы, используемые для установки $1 и $6 немного ближе. $1:

$(( 5 - ($1 / 8) ))

Максимальные и минимально возможные значения для префикса CIDR равны 32 для сетевой маски

11111111.11111111.11111111.11111111

и 0 для сетевой маски

00000000.00000000.00000000.00000000

В приведенной выше формуле используется целочисленное деление, поэтому возможные результаты варьируются от 1 до 5:

5 - (32 / 8) = 1
5 - ( 0 / 8) = 5

$6 установлено значение:

$(( (255 << (8 - ($1 % 8))) & 255 ))

Позвольте сломать это для нашего примера префикса CIDR 18. Сначала мы берем модуль и делаем некоторое вычитание:

8 - (18 % 8) = 6

Затем мы побитно сдвигаем 255 на это значение:

255 << 6

Это то же самое, что нажать шесть бит 0 на конец 255 в двоичном формате:

11111111000000

Наконец, мы побитовое И это значение с 255:

11111111000000 &
00000011111111  # 255

который дает

00000011000000

или просто

11000000

Посмотрите знакомый? Это третий октет в нашей сетевой маске в двоичном формате:

11111111.11111111.11000000.00000000
                  ^------^

В десятичном значении значение равно 192.

Затем мы сдвигаем позиционные параметры на основе значения $1:

[ $1 -gt 1 ] && shift $1 || shift

В нашем случае значение $1 равно 3, поэтому мы сдвигаем позиционные параметры 3 влево. Предыдущее значение $4 становится новым значением $1, предыдущее значение $5 становится значением $2 и так далее:

$1: 255
$2: 255
$3: 192
$4: 0
$5: 0
$6: 0

Эти значения должны выглядеть знакомыми: это десятичные октеты из нашей сетевой маски (с добавлением пары дополнительных нулей в конце). Чтобы получить сетевую маску, мы просто распечатываем первые четыре с точками между ними:

echo ${1-0}.${2-0}.${3-0}.${4-0}

-0 после каждого параметра говорит использовать 0 как значение по умолчанию, если параметр не установлен.

Вывод:

255.255.192.0