Маска подсети - это набор битов, определяющий, какое количество битов используется для указания адреса подсети, а какое для указания адреса компьютера в этой подсети. Более привычно маску подсети видеть в десятичном виде, но на самом деле она представляется в бинарном виде. Если вы понимаете, как представляется адрес подсети и адрес компьютера в этой подсети, то можете легко определить по адресу компьютера и маске подсети адрес этой самой подсети и маску подсети в таком виде:
<адрес-подсети>/<количество-бит>
Пример маски подсети
Предположим, у вас есть некоторая сеть 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”.