Расчёт маски подсети (обычная + CIDR)
Доработал один из своих старых скриптов, добавив в него поддержу CIDR-формата маски. Возможно, кому-то понадобится для решения сходной задачи, поэтому решил поделиться с сообществом.
Использование:
netmask <начальный IP диапазона> <конечный IP диапазона>
netmask <начальный IP диапазона> <конечный IP диапазона> cidr
Примеры:
netmask 195.189.120.0 195.189.123.255 = 255.255.252.0
netmask 195.189.120.0 195.189.123.255 CIDR = /22
Код:
@Echo Off
:: #
:: # Subnet mask from IP range v2.1
:: # Inquisitor, 2012-2013
:: # Distributed under GNU GPL v2 license
:: # http://www.gnu.org/licenses/gpl-2.0.html
:: #
:: # USAGE: NetMask.cmd first_ip_from_range last_ip_from_range
:: # or, for display in CIDR format
:: # NetMask.cmd first_ip_from_range last_ip_from_range cidr
:: #
SetLocal EnableDelayedExpansion
Set NetworkHostBoundary=false
Set NetMask=
:: Если вызывается не из консоли, а из другого батника,
:: вызывать нужно из цикла for для корректной работы
:: Пример:
rem For /F %%A In ('netmask.cmd 192.168.1.0 192.168.8.255') Do Echo %%A
:: Проверка на запуск с аргументами
:: Если без аргументов - пример использования и выход
If "%*"=="" (
For /F "tokens=1,* delims=#" %%A In ('FindStr /R /B "::.#" "%~dpnx0"') Do (
Echo.%%B
)
GoTo :EOF
)
:: Разбор входящих аргументов, cравнение начального и конечного
:: адресов по октетам
For /F "tokens=1-9 delims=. " %%A In ("%*") Do (
Call :Mask %%A %%E
Call :Mask %%B %%F
Call :Mask %%C %%G
Call :Mask %%D %%H
If /I "%%I"=="CIDR" Call :CIDR
)
:: Вывод в stdout результата и выход, раскомментировать нужное
:: Вывод _БЕЗ_ перевода строки
<Nul Set /P Echo=!NetMask!
:: Вывод _С_ переводом строки
rem Echo !NetMask!
GoTo :EOF
:DEC2BIN
:: Функция перевода числа из десятеричной системы в двоичную
:: Принимает один аргумент, результат возвращается в %Result%
Set Result=
Set Data=%~1
:d2b_loop
:: Проверяем, есть ли остаток от деления
Set /A x1=Data/2
Set /A x2=x1*2
If "%x2%"=="%Data%" (Set Mod=0) Else (Set Mod=1)
:: Устанавливаем в качестве входных данных результат деления
Set Data=%x1%
:: Пишем результат поразрядно, в обратном порядке
Set Result=%Mod%%Result%
:: Следующая итерация или выход после окончания
If Not "%x1%"=="0" GoTo d2b_loop
:: Перед выходом - дополнение ведущими нулями до двух в восьмой
:: Требуется для корректного сравнения двух чисел без сдвига
Call :LeadingZero %Result%
GoTo :EOF
:BIN2DEC
:: Функция перевода числа из двоичной системы в десятеричную
:: Принимает один аргумент, результат возвращается в %Result%
Set Result=
:: Значение -1 для начала значения счетчика с нуля
Set i=-1
Set Data=%1
:b2d_loop
:: Получаем текущий разряд числа
Set /A i+=1
Set x1=%Data:~-1%
:: Используем бинарный сдвиг для получения степени двойки
Set /A x2="x1 * (1 << i)"
:: Добавляем к результату
Set /A Result+=%x2%
:: Следующая итерация или выход после окончания
If Not "%Data:~,-1%"=="" (Set Data=%Data:~,-1%&GoTo b2d_loop)
GoTo :EOF
:LeadingZero
:: Дополнение ведушими нулями до двух в восьмой (один байт)
:: Принимает один аргумент, результат возвращается в %Result%
Set Result=
Set Data=%~1
For /L %%A In (1,1,8) Do (
If Not "!Data!"=="" (
Set Result=!Data:~-1!!Result!
Set Data=!Data:~,-1!
) Else (
Set Result=0!Result!
)
)
GoTo :EOF
:FastCompare
:: Быстрое сравнение, если найдены различия - вызывается функция
:: более детального сравнения для поиска самого различия
:: Принимает два аргумента, результат возвращается в %Result%
If Not !NetworkHostBoundary!==true (
If "%1"=="%2" (
Set Result=11111111
GoTo :EOF
) Else (
Call :Compare %1 %2
)
) Else (
Set Result=00000000
)
GoTo :EOF
:Compare
:: Сравнение двух однобайтных чисел поразрядно, локализация начала различий
:: и забивание всего после первого различия нулями
:: Принимает два аргумента, результат возвращается в %Result%
:: Ведущие нули у обоих чисел обязательны
Set x1=%1&Set x2=%2
Set Result=
For /L %%A In (1,1,8) Do (
:: Получение ведущего разряда от каждого из чисел
Set n1=!x1:~,1!
Set n2=!x2:~,1!
:: Сравнение двух чисел и инвертирование бита
Set /A Data="1 ^ (n1 ^ n2)"
:: Установка флага начала различия
If !Data!==0 Set NetworkHostBoundary=true
:: Обнуление всех последующих после различия разрядов
If !NetworkHostBoundary!==true Set Data=0
:: Поразрядная запись результата
Set Result=!Result!!Data!
:: Устанавливаем остаток в качестве входных данных для следующей итерации
Set x1=!x1:~1!
Set x2=!x2:~1!
)
GoTo :EOF
:Mask
:: Генерация одного октета маски
:: Принимает два аргумента, результат дописывается к %NetMask%
Call :DEC2BIN %1&&Set From=!Result!
Call :DEC2BIN %2&&Set To=!Result!
Call :FastCompare !From! !To!
Call :BIN2DEC !Result!
If "!NetMask!"=="" (Set NetMask=!Result!) Else (Set NetMask=!NetMask!.!Result!)
GoTo :EOF
:CIDR
:: Трансформация маски в формат CIDR
Set BinMask=
Set i=0
:: Разбираем маску обратно на октеты
Set NetMask=!NetMask:.= !
For %%A In (!NetMask!) Do (
Call :DEC2BIN %%A
:: И собираем бинарную маску
Set BinMask=!BinMask!!Result!
)
:: Удаляем из неё незначащие нули, а единицы разделяем пробелами
Set BinMask=!BinMask:0=!
Set BinMask=!BinMask:1=1 !
:: Считаем единицы
For %%A In (!BinMask!) Do (Set /A i+=1)
:: Устанавливаем значение
Set NetMask=/!i!
Exit /B
|