Программно извлекать частные IP-адреса (ы)

Я ищу простой способ программно извлечь частные IPv4-адреса (ы) компьютера.

Что-то похожее на этот вопрос , но ограничивается частными IP-адресами.

В качестве примера я могу извлечь все адреса IPv4 с помощью следующей команды:

ifconfig | grep 'inet addr' | cut -d ':' -f 2 | awk '{ print $1 }' 

Пример вывода:

 6.11.71.78 10.0.2.15 127.0.0.1 

Аналогичным образом я хотел бы получить только IP-адреса в частном адресном пространстве. Итак, ссылаясь на тот же пример, вывод должен быть:

 10.0.2.15 

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

  • 24-битный блок – 10.XXX
  • 20-битный блок – 172.16.XX – 172.31.XX
  • 16-разрядный блок – 192.168.XX

Так что просто grep для вышеуказанных типов IP-адресов.

 $ ifconfig | grep 'inet addr' | cut -d ':' -f 2 | awk '{ print $1 }' | \ grep -E '^(192\.168|10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.)' 192.168.1.20 

Детали

Используемый grep использует регулярные выражения. В этом случае мы ищем следующие шаблоны:

  • 192,168
  • 10.
  • 172,1 [6789].
  • 172,2 [0-9].
  • 172,3 [01].

Кроме того, мы явно указываем только совпадающие числа, которые начинаются с одного из этих шаблонов. Якорь ( ^ ) предоставляет нам эту возможность.

Другие примеры

Если мы добавим следующие строки в файл, чтобы проверить grep out.

 $ cat afile 192.168.0.1 10.11.15.3 1.23.3.4 172.16.2.4 

Затем мы можем проверить его так:

 $ cat afile | grep -E '^(192\.168|10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.)' 192.168.0.1 10.11.15.3 172.16.2.4 

Вывод (одна строка для каждого IP) может быть отфильтрован с помощью следующего сценария:

 #!/bin/sh PATTERN='^10\.' # 10.0.0.0/8 PATTERN+='|^192\.168\.' # 192.168.0.0/16 PATTERN+='|^169\.254\.' # not strictly private range, but link local for i in $(seq 16 31) ; do # 172.16.0.0/12 PATTERN+="|^172\.$i\." done egrep "$PATTERN" exit 0 . #!/bin/sh PATTERN='^10\.' # 10.0.0.0/8 PATTERN+='|^192\.168\.' # 192.168.0.0/16 PATTERN+='|^169\.254\.' # not strictly private range, but link local for i in $(seq 16 31) ; do # 172.16.0.0/12 PATTERN+="|^172\.$i\." done egrep "$PATTERN" exit 0 

Использование, например:

 ifconfig | grep 'inet addr' | cut -d ':' -f 2 | awk '{ print $1 }' | ./filter_private_ips 

IPv4 был создан в то время, когда были распространены 32-битные системы. Предельный десятичный адрес IPv4 может быть сохранен в 32-разрядное целое без знака, а побитовые операции эффективно выполняются сетевым оборудованием. Битовая маска для CIDR 172.16.0.0/12 может быть сформирована из одного сдвига влево и проверена на адрес с одним побитовым и.

Существует три «частных» диапазона сетевых адресов, определенных RFC-1918.

  • CIDR / 8, (A) один большой сетевой, (24-разрядный, 16M) диапазон адресов на 10.xyz/8
  • CIDR / 12, (B) 16 непрерывных сетей (20-разрядный, 1M) диапазон адресов 172.16+xyz/12 , где x in [0..15]
  • CIDR / 16, (C) 256 последовательных сетей (16 бит, 64 КБ), диапазон адресов 192.168.yz/16

Кроме того, для подразделения несущей сети,

  • CIDR / 10, (A) один большой сетевой (24-битный, 16M) адресный диапазон при 100.64+xyz/10 , где x in [0..63]

А для локальных адресов ссылок,

  • CIDR / 16, (B) диапазон адресов одной сети (16 бит, 64 КБ) на уровне 169.254.yz/16

С языком, поддерживающим побитовые операции, вы можете легко конвертировать десятичный адрес с точками в целое число,

 //assume x[0],x[1],x[2],x[3] are the parts of a dotted ip address unsigned int ipv4 = (( (( (x[0]<<8) |x[1])<<8) |x[2])<<8) |x[3] 

Предположим, что вы определили константы для указанных выше адресов,

 CIDR8 = (( (( (10<<8) |0xff)<<8) |0xff)<<8) |0xff CIDR12 = (( (( (172<<8) |16 |0xf)<<8) |0xff)<<8) |0xff CIDR16 = (( (( (192<<8) |168)<<8) |0xff)<<8) |0xff CIDR10 = (( (( (100<<8) |64 |0x3f)<<8) |0xff)<<8) |0xff CIDRLL = (( (( (169<<8) |254)<<8) |0xff)<<8) |0xff 

Проверка того, является ли ваш адрес ipv4 одним из этих адресов простым,

 ipv4 == (ipv4 & CIDR8) //10.0.0.0/8 ipv4 == (ipv4 & CIDR12) //172.16.0.0/12 ipv4 == (ipv4 & CIDR16) //192.168.0.0/16 ipv4 == (ipv4 & CIDR10) //100.64.0.0/10 ipv4 == (ipv4 & CIDRLL) //169.254.0.0/16 

Вместо того, чтобы проверять наличие 16 различных сетей 172.16.0.0/12, можно использовать описанный выше подход к битовой маске, чтобы непосредственно проверить, является ли адрес ipv4 частью одной из этих частных (NAT) сетей. Выбор perl (python или ruby ​​также работает) вместо оболочки или awk, а использование одного бита и операции значительно сокращает работу.

 sub isprivate { my($inet) = @_; if( $inet =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/ ) { if( $1==10 ) { return 10; } if( $1==172 && (($2 & 0x1f) == $2) ) { return 172; } if( $1==192 && ($2==168) ) { return 192; } } return 0; }; sub iscarrier { my($inet) = @_; if( $inet =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/ ) { if( $1==100 && (($2 & 0x7f) == $2) ) { return 100; } } return 0; }; sub islinklocal { my($inet) = @_; if( $inet =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/ ) { if( $1==169 && ($2==254) ) { return 169; } } return 0; }; 

Как вы хотите классифицировать адреса?

 sub ipaddr { my($inet) = @_; { if( isprivate($inet)>0 ) { $kind = "private"; } elsif( isloop($inet)>0 ) { $kind = "loopback"; } elsif( iscarrier($inet)>0 ) { $kind = "carrier"; } elsif( islinklocal($inet)>0 ) { $kind = "linklocal"; } else { $kind = ""; } print "$iface: $inet $netmask $broadcast ($flagsdesc) $kind\n"; } }; 

Запустите ifconfig из скрипта perl,

 $found = 0; open($fh,"/sbin/ifconfig|"); while($line=<$fh>) { chomp($line); $line =~ s/^\s+//; if( $line =~ /(\w+):\s+flags=(\d+)\s*\<(.*)\>\s+mtu\s+(\d+)\b/ ) { if( $found ) { ipaddr($inet); } $found = 1; ($iface,$flags,$flagsdesc,$mtu) = ($1,$2,$3,$4); } if( $line =~ /inet\s+(\d+\.\d+\.\d+\.\d+)\b/ ) { ($inet,$netmask,$broadcast) = ($1,"",""); if( $line =~ /netmask\s+([\d+\.]+)\b/ ) { ($netmask) = ($1); } if( $line =~ /broadcast\s+([\d\.]+)\b/ ) { ($broadcast) = ($1); } } } if( $found ) { ipaddr($inet); } в $found = 0; open($fh,"/sbin/ifconfig|"); while($line=<$fh>) { chomp($line); $line =~ s/^\s+//; if( $line =~ /(\w+):\s+flags=(\d+)\s*\<(.*)\>\s+mtu\s+(\d+)\b/ ) { if( $found ) { ipaddr($inet); } $found = 1; ($iface,$flags,$flagsdesc,$mtu) = ($1,$2,$3,$4); } if( $line =~ /inet\s+(\d+\.\d+\.\d+\.\d+)\b/ ) { ($inet,$netmask,$broadcast) = ($1,"",""); if( $line =~ /netmask\s+([\d+\.]+)\b/ ) { ($netmask) = ($1); } if( $line =~ /broadcast\s+([\d\.]+)\b/ ) { ($broadcast) = ($1); } } } if( $found ) { ipaddr($inet); }