Проверка, находится ли IP в диапазоне массива белого списка

Вопрос или проблема

#!/bin/bash

MAXCDN_ARRAY=”108.161.176.0/20 94.46.144.0/20 146.88.128.0/20 198.232.124.0/22 23.111.8.0/22 217.22.28.0/22 64.125.76.64/27 64.125.76.96/27 64.125.78.96/27 64.125.78.192/27 64.125.78.224/27 64.125.102.32/27 64.125.102.64/27 64.125.102.96/27 94.31.27.64/27 94.31.33.128/27 94.31.33.160/27 94.31.33.192/27 94.31.56.160/27 177.54.148.0/24 185.18.207.65/26 50.31.249.224/27 50.31.251.32/28 119.81.42.192/27 119.81.104.96/28 119.81.67.8/29 119.81.0.104/30 119.81.1.144/30 27.50.77.226/32 27.50.79.130/32 119.81.131.130/32 119.81.131.131/32 216.12.211.59/32 216.12.211.60/32 37.58.110.67/32 37.58.110.68/32 158.85.206.228/32 158.85.206.231/32 174.36.204.195/32 174.36.204.196/32″

$IP = 108.161.184.123

if [ $IP in $MAXCDN_ARRAY ];
then:
echo “$IP находится в диапазоне MAXCDN”
else:
echo “$IP не находится в диапазоне MAXCDN”
fi

У меня есть список IP-адресов в MAXCDN_ARRAY, который я хочу использовать в качестве белого списка. Я хочу проверить, находится ли конкретный IP-адрес в этом диапазоне.

Как мне структурировать код, чтобы он мог сравнить все IP-адреса в массиве и сказать, находится ли конкретный IP в диапазоне этого списка или нет?

Вы можете использовать grepcidr, чтобы проверить, находится ли IP-адрес в списке сетей CIDR.

#! /bin/bash

NETWORKS="108.161.176.0/20 94.46.144.0/20 146.88.128.0/20 198.232.124.0/22
          23.111.8.0/22 217.22.28.0/22 64.125.76.64/27 64.125.76.96/27
          64.125.78.96/27 64.125.78.192/27 64.125.78.224/27 64.125.102.32/27
          64.125.102.64/27 64.125.102.96/27 94.31.27.64/27 94.31.33.128/27
          94.31.33.160/27 94.31.33.192/27 94.31.56.160/27 177.54.148.0/24
          185.18.207.65/26 50.31.249.224/27 50.31.251.32/28 119.81.42.192/27
          119.81.104.96/28 119.81.67.8/29 119.81.0.104/30 119.81.1.144/30
          27.50.77.226/32 27.50.79.130/32 119.81.131.130/32 119.81.131.131/32
          216.12.211.59/32 216.12.211.60/32 37.58.110.67/32 37.58.110.68/32
          158.85.206.228/32 158.85.206.231/32 174.36.204.195/32
          174.36.204.196/32"

for IP in 108.161.184.123 108.161.176.123 192.168.0.1 172.16.21.99; do
    grepcidr "$NETWORKS" <(echo "$IP") >/dev/null && \
        echo "$IP находится в диапазоне MAXCDN" || \
        echo "$IP не находится в диапазоне MAXCDN"
done

ПРИМЕЧАНИЕ: grepcidr ожидает, что IP-адрес(а), с которыми он сравнивает, будут в файле, а не просто аргументом в командной строке. Вот почему мне пришлось использовать <(echo "$IP") выше.

Вывод:

108.161.184.123 находится в диапазоне MAXCDN
108.161.176.123 находится в диапазоне MAXCDN
192.168.0.1 не находится в диапазоне MAXCDN
172.16.21.99 не находится в диапазоне MAXCDN

grepcidr доступен в предварительно упакованном виде для нескольких дистрибутивов, включая Debian:

Package: grepcidr
Version: 2.0-1
Description-en: Фильтрация IP-адресов, соответствующих спецификации IPv4 CIDR/сети
 grepcidr можно использовать для фильтрации списка IP-адресов по одной или
 нескольким спецификациям классовой междоменной маршрутизации (CIDR) или
 произвольным сетям, указанным диапазоном адресов. Как и grep, есть расширения для инверсии совпадений и загрузки шаблонов из файла.
 grepcidr способен сравнивать тысячи или даже миллионы IP-адресов
 с сетями с небольшим использованием памяти и в разумные сроки вычислений.
 .
 grepcidr имеет бесконечное использование в сетевом ПО, включая: фильтрацию и обработку почты,
 сетевую безопасность, анализ журналов и многие пользовательские приложения.
 Домашняя страница: http://www.pc-tools.net/unix/grepcidr/

В противном случае исходный код доступен по приведенной выше ссылке.


Другой альтернативой является написание скрипта на perl или python с использованием одной из многих библиотек/модулей для манипулирования и проверки IPv4-адресов с помощью этих языков.

Например, модуль perl Data::Validate::IP имеет функцию is_innet_ipv4($ip, $network); Net::CIDR::Lite имеет очень похожий метод $cidr->find($ip);; и Net::IPv4Addr имеет функцию ipv4_in_network().

python имеет сопоставимые библиотеки, включая ipy, ipaddr и ipcalc, среди прочих.

Я понимаю, что это старое, и уже есть принятый ответ, но это решение использует функцию, которую я написал для портативности на нескольких серверах в среде, где grepcidr не был стандартным пакетом на всех серверах.

Комментарии должны прояснить, что именно это делает.

#! /bin/bash

# Установить DEBUG=1, чтобы увидеть итерации через вычисления.
#DEBUG=1

MAXCDN_ARRAY="108.161.176.0/20 94.46.144.0/20 146.88.128.0/20 198.232.124.0/22 23.111.8.0/22 217.22.28.0/22 64.125.76.64/27 64.125.76.96/27 64.125.78.96/27 64.125.78.192/27 64.125.78.224/27 64.125.102.32/27 64.125.102.64/27 64.125.102.96/27 94.31.27.64/27 94.31.33.128/27 94.31.33.160/27 94.31.33.192/27 94.31.56.160/27 177.54.148.0/24 185.18.207.65/26 50.31.249.224/27 50.31.251.32/28 119.81.42.192/27 119.81.104.96/28 119.81.67.8/29 119.81.0.104/30 119.81.1.144/30 27.50.77.226/32 27.50.79.130/32 119.81.131.130/32 119.81.131.131/32 216.12.211.59/32 216.12.211.60/32 37.58.110.67/32 37.58.110.68/32 158.85.206.228/32 158.85.206.231/32 174.36.204.195/32 174.36.204.196/32"

IP=108.161.184.123

function in_subnet {
    # Определите, находится ли IP-адрес в указанной подсети.
    #
    # Аргументы:
    #   sub: Подсеть в нотации CIDR.
    #   ip: IP-адрес для проверки.
    #
    # Возвращается:
    #   1|0
    #
    local ip ip_a mask netmask sub sub_ip rval start end

    # Определите битовую маску.
    local readonly BITMASK=0xFFFFFFFF

    # Установите статус DEBUG, если он еще не определен в скрипте.
    [[ "${DEBUG}" == "" ]] && DEBUG=0

    # Чтение аргументов.
    IFS=/ read sub mask <<< "${1}"
    IFS=. read -a sub_ip <<< "${sub}"
    IFS=. read -a ip_a <<< "${2}"

    # Вычислите сетевую маску.
    netmask=$(($BITMASK<<$((32-$mask)) & $BITMASK))

    # Определите диапазон адресов.
    start=0
    for o in "${sub_ip[@]}"
    do
        start=$(($start<<8 | $o))
    done

    start=$(($start & $netmask))
    end=$(($start | ~$netmask & $BITMASK))

    # Преобразуйте IP-адрес в 32-битное число.
    ip=0
    for o in "${ip_a[@]}"
    do
        ip=$(($ip<<8 | $o))
    done

    # Определите, входит ли IP в диапазон.
    (( $ip >= $start )) && (( $ip <= $end )) && rval=1 || rval=0

    (( $DEBUG )) &&
        printf "ip=0x%08X; start=0x%08X; end=0x%08X; in_subnet=%u\n" $ip $start $end $rval 1>&2

    echo "${rval}"
}

for subnet in $MAXCDN_ARRAY
do
    (( $(in_subnet $subnet $IP) )) &&
        echo "${IP} находится в ${subnet}" && break
done

Я хотел выполнить это на множестве хостов, не устанавливая grepcidr, и я пробовал скрипт от Deacon, но он не сработал, поэтому вот тот, который я написал и который был подтвержден как работающий. Надеюсь, кто-то найдет это полезным:

function in_subnet {
    # Определите, находится ли IP-адрес в указанной подсети.
    #
    # Аргументы:
    #   cidr_subnet: Подсеть в нотации CIDR.
    #   ip_addr: IP-адрес для проверки.
    #
    # Возвращаемое значение:
    #   0|1
    #
    local readonly cidr_subnet="${1}"
    local readonly ip_addr="${2}"
    local subnet_ip cidr_mask netmask ip_addr_subnet subnet rval

    subnet_ip=$(echo "${cidr_subnet}" | cut -d/ -f1)
    cidr_mask=$(echo "${cidr_subnet}" | cut -d/ -f2)

    netmask=$(( 0xFFFFFFFF << $(( 32 - ${cidr_mask} )) ))

    # Примените сетевую маску как к подсетевому IP, так и к данному IP-адресу 
    ip_addr_subnet=$(( netmask & $(ip_to_int ${ip_addr}) ))
    subnet=$(( netmask & $(ip_to_int ${subnet_ip}) ))

    # IP-адреса подсети будут совпадать, если данный IP-адрес находится в CIDR-подсети
    [ "${ip_addr_subnet}" == "${subnet}" ] && rval=0 || rval=1

    return $rval
}

function ip_to_int {
    local readonly ip_addr="${1}"
    local ip_1 ip_2 ip_3 ip_4

    ip_1=$(echo "${ip_addr}" | cut -d'.' -f1)
    ip_2=$(echo "${ip_addr}" | cut -d'.' -f2)
    ip_3=$(echo "${ip_addr}" | cut -d'.' -f3)
    ip_4=$(echo "${ip_addr}" | cut -d'.' -f4)

    echo $(( ip_1 * 256**3 + ip_2 * 256**2 + ip_3 * 256 + ip_4 ))
}
# УСТАНОВКА ДАННЫХ МАССИВА CIDR
cidrs=(10.10.10.0/24 20.20.20.0/24)
cidrarr=()
for cidr in "${cidrs[@]}"; do
    mask=$(echo $cidr | cut -d/ -f2)
    ip=$(echo $cidr | cut -d/ -f1)
    cidrdec=0;maskdec=0;p=0
    for i in {1..4}; do
        e=$((2**$((8*(4-$i)))))
        o=$(echo $ip | cut -d. -f$i)
        cidrdec=$(($cidrdec+$o*$e))
    done
    for ((i=1; i<=$mask; i++)); do
        maskdec=$(($maskdec+2**(32-$i)))
    done
    cidrarr+=($cidr)
    cidrarr+=($cidrdec)
    cidrarr+=($maskdec)
done
# КОНЕЦ УСТАНОВКИ ДАННЫХ МАССИВА CIDR

cidrtest() {
    ipdec=0
    for i in {1..4}; do
        e=$((2**$((8*(4-$i)))))
        o=$(echo $1 | cut -d. -f$i)
        ipdec=$(($ipdec+$o*$e))
    done
    for i in ${!cidrarr[@]}; do
        [ $((i%3)) -ne 0 ] && continue
        t=${cidrarr[i+1]}
        m=${cidrarr[i+2]}
        ipm=$(($ipdec & $m))
        [[ $ipm -eq $t ]] && echo "$1 находится в пределах ${cidrarr[i]}"
    done
}

cidrtest 10.10.0.1

Вот улучшенный скрипт на основе grepcidr, который отображает соответствующий CIDR.

Скрипт:

echo -e "Поиск...\n"; cat cidr.txt | while read NETWORK; do grepcidr "$NETWORK" ip.txt && echo -e "=> найдено в $NETWORK\n"; done

Пример результата:

Поиск...

172.17.1.1
172.18.1.1
=> найдено в 172.16.0.0/12

192.168.50.1
=> найдено в 192.168.50.0/24

grepcidr можно рассматривать как лучший вариант для скрипта:

$ grepcidr "`curl -sS https://www.cloudflare.com/ips-v4`" <(echo 104.24.100.200)
104.24.100.200

Но если вы хотите не только узнать результат, но и выяснить, какой CIDR совпал, вы можете использовать ipcalc:

$ ip=104.24.100.200; \
for r in `curl -sS https://www.cloudflare.com/ips-v4`; do \
    echo - $r; \
    s=${r#*/}; \
    n=`ipcalc -n "$ip/$s" | grep ^Network: | awk '{print $2}'`; \
    if [ "$r" = "$n" ]; then echo совпадение; break; fi; \
done
- 173.245.48.0/20
- 103.21.244.0/22
- 103.22.200.0/22
- 103.31.4.0/22
- 141.101.64.0/18
- 108.162.192.0/18
- 190.93.240.0/20
- 188.114.96.0/20
- 197.234.240.0/22
- 198.41.128.0/17
- 162.158.0.0/15
- 104.16.0.0/13
- 104.24.0.0/14
совпадение

Также вот некоторые другие решения, связанные с Net::IPv4Addr (пакет perl-net-ipv4addr для Alpine):

$ curl -sS https://www.cloudflare.com/ips-v4 \
| perl -MNet::IPv4Addr=ipv4_in_network -ne '
    BEGIN { $ip = shift; }
    if(ipv4_in_network($_, $ip)) { print "найдено\n"; last; }
' 104.24.100.200
найдено

Net::CIDR::Lite (perl-net-cidr-lite):

$ curl -sS https://www.cloudflare.com/ips-v4 \
| perl -MNet::CIDR::Lite -ne '
    BEGIN { $ip = shift; $cidr = Net::CIDR::Lite->new; }
    $cidr->add($_);
    END { print "найдено\n" if $cidr->find($ip); }
' 104.24.100.200
найдено

И пакет addresses для Python:

$ for net in `curl -sS https://www.cloudflare.com/ips-v4`; do \
    echo - $net; \
    if python -c 'from ipaddress import IPv4Address, IPv4Network; import sys; exit(IPv4Address(sys.argv[1]) not in IPv4Network(sys.argv[2]))' 104.24.100.200 "$net"; then \
        echo найдено; \
        break; \
    fi; \
done
...
найдено

P.S. Ладно, с grepcidr вы также можете узнать, какой CIDR совпал:

$ for net in `curl -sS https://www.cloudflare.com/ips-v4`; do \
    if grepcidr "$net" <(echo 104.24.100.200); then \
        echo "$net"; \
        break; \
    fi; \
done
104.24.100.200
104.24.0.0/14

Ответ или решение

Для проверки, находится ли заданный IP-адрес в диапазоне массива белых списков (CIDR), вам нужно написать сценарий на Bash, который будет использовать алгоритмы для проверки каждого CIDR-диапазона. В этом ответе предлагается два подхода: использование утилиты grepcidr, которая достаточно эффективна и проста в использовании, и написание функции на Bash для проверки IP адреса непосредственно.

Подход 1: Использование утилиты grepcidr

Если у вас уже установлен grepcidr, вы можете воспользоваться следующим скриптом:

#!/bin/bash

# Массовый массив IP адресов в формате CIDR
MAXCDN_ARRAY="108.161.176.0/20 94.46.144.0/20 146.88.128.0/20 198.232.124.0/22 23.111.8.0/22 217.22.28.0/22
64.125.76.64/27 64.125.76.96/27 64.125.78.96/27 64.125.78.192/27 64.125.78.224/27 64.125.102.32/27
64.125.102.64/27 64.125.102.96/27 94.31.27.64/27 94.31.33.128/27 94.31.33.160/27 94.31.33.192/27
94.31.56.160/27 177.54.148.0/24 185.18.207.65/26 50.31.249.224/27 50.31.251.32/28 119.81.42.192/27
119.81.104.96/28 119.81.67.8/29 119.81.0.104/30 119.81.1.144/30 27.50.77.226/32 27.50.79.130/32
119.81.131.130/32 119.81.131.131/32 216.12.211.59/32 216.12.211.60/32 37.58.110.67/32 37.58.110.68/32
158.85.206.228/32 158.85.206.231/32 174.36.204.195/32 174.36.204.196/32"

# IP адрес для проверки
IP="108.161.184.123"

# Проверка IP адреса
if grepcidr -q "$IP" <<< "$MAXCDN_ARRAY"; then
    echo "$IP находится в пределах базы MAXCDN"
else
    echo "$IP не находится в пределах базы MAXCDN"
fi

Подход 2: Создание функции для проверки IP

Если вы хотите избежать установки дополнительных утилит, вы можете создать свою функцию для проверки IP адреса:

#!/bin/bash

MAXCDN_ARRAY="108.161.176.0/20 94.46.144.0/20 146.88.128.0/20 198.232.124.0/22 23.111.8.0/22 217.22.28.0/22
64.125.76.64/27 64.125.76.96/27 64.125.78.96/27 64.125.78.192/27 64.125.78.224/27 64.125.102.32/27
64.125.102.64/27 64.125.102.96/27 94.31.27.64/27 94.31.33.128/27 94.31.33.160/27 94.31.33.192/27
94.31.56.160/27 177.54.148.0/24 185.18.207.65/26 50.31.249.224/27 50.31.251.32/28 119.81.42.192/27
119.81.104.96/28 119.81.67.8/29 119.81.0.104/30 119.81.1.144/30 27.50.77.226/32 27.50.79.130/32
119.81.131.130/32 119.81.131.131/32 216.12.211.59/32 216.12.211.60/32 37.58.110.67/32 37.58.110.68/32
158.85.206.228/32 158.85.206.231/32 174.36.204.195/32 174.36.204.196/32"

IP="108.161.184.123"

function in_subnet {
    local cidr="$1"
    local ip="$2"

    IFS=/ read range mask <<< "$cidr"
    local ip_dec=0
    local subnet_dec=0
    local i

    # Преобразование IP адреса в десятичный формат
    IFS=. read -r i1 i2 i3 i4 <<< "$ip"
    ip_dec=$(( (i1<<24) + (i2<<16) + (i3<<8) + i4 ))

    # Преобразование подсети в десятичный формат
    IFS=. read -r s1 s2 s3 s4 <<< "$range"
    subnet_dec=$(( (s1<<24) + (s2<<16) + (s3<<8) + s4 ))

    # Создание маски
    local mask_dec=$(( (0xFFFFFFFF << (32 - mask)) & 0xFFFFFFFF ))

    # Проверка, находится ли IP в диапазоне CIDR
    local start=$(( subnet_dec & mask_dec ))
    local end=$(( start | ~mask_dec & 0xFFFFFFFF ))

    if (( ip_dec >= start && ip_dec <= end )); then
        return 0  # IP в диапазоне
    else
        return 1  # IP не в диапазоне
    fi
}

found=0
for subnet in $MAXCDN_ARRAY; do
    if in_subnet "$subnet" "$IP"; then
        echo "$IP находится в пределах $subnet"
        found=1
        break
    fi
done

if (( found == 0 )); then
    echo "$IP не находится в пределах белого списка"
fi

Вывод

Оба предложенных подхода обеспечивают способ проверки, принадлежит ли данный IP-адрес к перечисленным в массиве CIDR диапазонам. Убедитесь, что вы используете нужный подход в зависимости от вашей среды и доступных инструментов.

Оцените материал
Добавить комментарий

Капча загружается...