Extraia programaticamente endereços IP privados

3

Estou procurando uma maneira fácil de extrair programaticamente os endereços IPv4 privados de um computador.

Algo semelhante a esta questão , mas restrito a IPs privados.

Como exemplo, posso extrair all os endereços IPv4 com o seguinte comando:

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

Exemplo de saída:

6.11.71.78
10.0.2.15
127.0.0.1

De maneira semelhante, gostaria de obter apenas IPs no espaço de endereço privado. Então, referindo-se ao mesmo exemplo, a saída deve ser:

10.0.2.15
    
por Emyl 05.11.2013 / 09:39

4 respostas

10

Qualquer coisa no espaço IP privado sempre começará com um dos três blocos de endereços IP.

  • bloco de 24 bits - 10.X.X.X
  • bloco de 20 bits - 172.16.X.X - 172.31.X.X
  • bloco de 16 bits - 192.168.X.X

Então, apenas grep para os tipos acima de endereços 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

Detalhes

O grep que estou usando faz uso de expressões regulares. Neste caso, estamos procurando os seguintes padrões:

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

Além disso, estamos sendo explícitos apenas em números correspondentes que começam com um desses padrões. A âncora ( ^ ) está fornecendo essa capacidade para nós.

Mais exemplos

Se adicionarmos as seguintes linhas a um arquivo apenas para testar o grep out.

$ cat afile 
192.168.0.1
10.11.15.3
1.23.3.4
172.16.2.4

Podemos então testá-lo da seguinte forma:

$ 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
    
por 05.11.2013 / 10:12
1

O IPv4 foi criado no momento em que os sistemas de 32 bits eram predominantes. O endereço decimal pontilhado IPv4 pode ser armazenado em um inteiro não assinado de 32 bits e as operações bit a bit são desempenhadas com eficiência pelo hardware de rede. Uma máscara de bits para o CIDR 172.16.0.0/12 pode ser formada a partir de um único deslocamento à esquerda e verificada em relação a um endereço com um único bit a bit e.

Existem três intervalos de endereços de rede 'privados' definidos pelo RFC-1918.

  • CIDR / 8, (A) rede grande única, intervalo de endereços (24 bits, 16M) em 10.x.y.z/8
  • CIDR / 12, (B) 16 intervalos de endereços de redes contíguas (20 bits, 1M) em 172.16+x.y.z/12 , em que x in [0..15]
  • CIDR / 16, (C) 256 intervalos de endereços de redes contíguas (16 bits, 64K) em 192.168.y.z/16

Além disso, para subdivisão da rede de operadoras,

  • CIDR / 10, (A) rede grande e única, intervalo de endereços (24 bits, 16M) em 100.64+x.y.z/10 , em que x in [0..63]

E para endereços locais de links,

  • CIDR / 16, (B) um único intervalo de endereços de rede (16 bits, 64K) em 169.254.y.z/16

Com uma linguagem que suporta operações bit a bit, você pode converter um endereço decimal pontuado em um inteiro facilmente,

//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]

Suponha que você tenha definido constantes para os endereços listados acima,

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

Verificar se seu endereço IPv4 é um desses endereços é simples,

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

Em vez de verificar 16 diferentes redes 172.16.0.0/12, pode-se usar a abordagem de máscara de bits acima para verificar diretamente se um endereço ipv4 faz parte de uma dessas redes privadas (NAT). Escolhendo perl (python ou ruby também funcionam), em vez de shell ou awk, e usando um único bit-wise e a operação reduz consideravelmente o trabalho.

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;
};

Como você deseja classificar os endereços?

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";
    }
};

Execute o ifconfig dentro de um script 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); }
    
por 06.11.2013 / 03:43
0

A saída (uma linha por IP) pode ser filtrada com o seguinte script:

#!/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

Uso por exemplo:

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

Mostrar IPs privados

ip -o addr show | \
  grep -v 'inet6' | \
  grep -v 'scope host' | \
  awk '{print $4}' | \
  cut -d '/' -f 1 | \
  grep -E '^(192\.168|10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.)'

Mostrar IPs públicos

ip -o addr show | \
  grep -v 'inet6' | \
  grep -v 'scope host' | \
  awk '{print $4}' | \
  cut -d '/' -f 1 | \
  grep -vE '^(192\.168|10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.)'
    
por 04.10.2018 / 22:14