Маска подсети — это набор битов, определяющий, какое количество битов используется для указания адреса подсети, а какое для указания адреса компьютера в этой подсети. Более привычно маску подсети видеть в десятичном виде, но на самом деле она представляется в бинарном виде. Если вы понимаете, как представляется адрес подсети и адрес компьютера в этой подсети, то можете легко определить по адресу компьютера и маске подсети адрес этой самой подсети и маску подсети в таком виде:
<адрес-подсети>/<количество-бит>.
Пример маски подсети
Предположим, у вас есть некоторая сеть 192.168.1.0 (сеть класса C), а маска подсети (netmask) представлена как 255.255.255.0 (в бинарном виде это 11111111 11111111 11111111 00000000). Это говорит о том, что для адреса подсети выделены первые 24 бита, а под адрес компьютера в подсети 8 бит, то есть, интервал значений последнего октета (последних 8 бит) будет принимать 256 различных значений (значения от 0 до 255 включительно), где 0 — это адрес подсети, а 255 — броадкаст. Соответственно, под адреса компьютеров вы сможете выделить 254 адреса (от 1 до 254). И, находясь на каком-либо компьютере под управлением операционной системы на базе ядра Linux, вы можете посмотреть адрес компьютера и сетевую маску при помощи команд «ifconfig» и «ip addr».
Иногда требуется имея адрес компьютера и маску подсети определить адрес подсети и битность сетевой маски, чтобы представить адрес подсети в виде
<адрес-подсети>/<количество-бит-в-маске>
Например:
192.168.1.0/24
Давайте попробуем решить задачу по такому преобразования на bash.
Перевод из десятичной системы в двоичную в bash
Для решения задачи нам необходимо будет перевести маску подсети в бинарный вид, поскольку маска подсети (netmask) — это последовательность бит, определяющая, какие из бит в указанном адресе выделены под адрес подсети (бит установлен в единицу), а какие — под адрес компьютера в подсети. Это очень важный момент в понимании IP адресов.
Функции, которая позволяет преобразовать десятичные числа в двоичные, в bash нет, поэтому необходимо будет использовать небольшую хитрость. Она заключается в том, что мы можем сгенерировать массив из последовательностей единиц и ноликов, каждый элемент которого будет являться двоичным представлением десятичного значения индекса этого элемента.
BINARRAY=({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1})
Эта строчка будет развернута в массив, содержащий 8-разрядные значения, что равносильно следующим операциям:
BINARRAY[0]="00000000" BINARRAY[1]="00000001" BINARRAY[2]="00000010" ... BINARRAY[254]="11111110" BINARRAY[255]="11111111"
Перевод из двоичной системы в десятичную в bash
Обратное преобразование нам потребуется, чтобы преобразовать последовательность бит обратно в десятичное число. Тут всё проще. В bash есть возможность преобразовывать число из N-ричного в десятичное, где N — это число до 64 включительно. Вот как это выглядит для системы счисления с основанием 2 (двоичной):
> A=$((2#00000010)) > echo $A 2 > B=$((2#11111110)) > echo $B 254
Как видите, в данном случае всё очень просто.
Пишем скрипт
#!/bin/bash # Массив с двоичными значениями 00000000, 00000001, ... 11111111 BARRAY=({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}) # Переменная PARAMS содержит "192.168.1.10 255.255.255.0" PARAMS=$(ifconfig wlan0 | grep netmask | awk '{print $2" "$4}') # IP_ADDRESS содержит "192.168.1.10" IP_ADDRESS=${PARAMS%% *} # Заменяем точки на пробелы IP_ADDRESS=${IP_ADDRESS//./ } # Преобразуем октеты IP-адреса в бинарный вид BINARY_IP_ADDRESS=$(for octet in $IP_ADDRESS; do echo -n ${BARRAY[octet]}" "; done) # Разделяем побитово и помещаем в массив BIN_IP_SEP1=${BINARY_IP_ADDRESS//1/1 } BINARY_IP_ARRAY=( ${BIN_IP_SEP1//0/0 } ) # Маска подсети (255.255.255.0) NETMASK=${PARAMS#* } # Заменяем точки на пробелы NETMASK=${NETMASK//./ } # Преобразуем маску подсети в бинарный вид BINARY_NETMASK=$(for octet in $NETMASK; do echo -n ${BARRAY[octet]}" "; done) # Разделяем маску подсети побитово и помещаем в массив BIN_MASK_SEP1=${BINARY_NETMASK//1/1 } BINARY_MASK_ARRAY=( ${BIN_MASK_SEP1//0/0 } ) # Считаем количество битов, установленных в 1 BITS_COUNT=0 for i in ${BINARY_MASK_ARRAY[@]} do [ "$i" == "1" ] && BITS_COUNT=$((BITS_COUNT + 1)) done # Считаем адрес подсети NEW_ADDRESS="" for i in {0..31} do # После каждых 8 бит ставим пробел [ $(($i % 8)) -ne 0 ] || NEW_ADDRESS+=" " if [ "${BINARY_MASK_ARRAY[$i]}" == "1" ] then # Если бит в маске подсети равен 1, добавляем бит из адреса NEW_ADDRESS+="${BINARY_IP_ARRAY[$i]}" else # Если бит в маске подсети равен 0, добавляем его NEW_ADDRESS+="${BINARY_MASK_ARRAY[$i]}" fi done # Ковертируем значения октетов в десятичные значения DECIMAL_ADDRESS=`echo $(for octet in $NEW_ADDRESS; do echo $((2#$octet)); done)` # Заменяем пробелы на точки DECIMAL_ADDRESS=${DECIMAL_ADDRESS// /.} # Выводим итоговый результат echo $DECIMAL_ADDRESS/$BITS_COUNT
Если вы хотите использовать команду ip для получения адреса и маски подсети, а не ifconfig, который считается устаревшим, вам нужно только поменять вычисление переменной PARAMS, так, чтобы ее значение содержало адрес компьютера и маску подсети в виде «192.168.1.10 255.255.255.0».
Открою маленький секрет, админы используют ipcalc почти все поголовно. Для меня перевод dec->bin в уме — высший пилотаж.
Это не секрет. А перевод dec->bin — это просто навык, который очень несложно тренируется.
Статья не о том, кто что использует, а как это сделать на bash.
@Maxim Norin
Спасибо за интересную статью, да и вообще сайт в закладки нужно изучить.
Теперь по делу:
BINARRAY=$({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}) — у меня так не работает, выдает ошибку и, естественно, пустой массив.
Работает вот так:
BINARRAY=({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1})
Спасибо. Оно так и не должно работать, опечатка :-) с долларом результат раскрытия скобок попытается выполниться, но, поскольку команды 00000000 нет, результат выполнения будет пустой.
Поправил.