Маска подсети: считаем на bash

Маска подсети на bashМаска подсети — это набор битов, определяющий, какое количество битов используется для указания адреса подсети, а какое для указания адреса компьютера в этой подсети. Более привычно маску подсети видеть в десятичном виде, но на самом деле она представляется в бинарном виде. Если вы понимаете, как представляется адрес подсети и адрес компьютера в этой подсети, то можете легко определить по адресу компьютера и маске подсети адрес этой самой подсети и маску подсети в таком виде:

<адрес-подсети>/<количество-бит>.

Пример маски подсети

Предположим, у вас есть некоторая сеть 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».

 

Маска подсети: считаем на bash: 4 комментария

  1. Василий (iklife.ru)

    Открою маленький секрет, админы используют ipcalc почти все поголовно. Для меня перевод dec->bin в уме — высший пилотаж.

    1. Maxim Norin Автор записи

      Это не секрет. А перевод dec->bin — это просто навык, который очень несложно тренируется.
      Статья не о том, кто что использует, а как это сделать на bash.

  2. Andrey Karpov

    @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})

    1. Maxim Norin Автор записи

      Спасибо. Оно так и не должно работать, опечатка :-) с долларом результат раскрытия скобок попытается выполниться, но, поскольку команды 00000000 нет, результат выполнения будет пустой.
      Поправил.

Обсуждение закрыто.