web-dev-qa-db-ja.com

プログラムでプライベートIPアドレスを抽出する

コンピューターのprivateIPv4アドレスをプログラムで抽出する簡単な方法を探しています。

この質問 に似ていますが、プライベートIPに制限されています。

例として、次のコマンドでIPv4アドレスを抽出allできます。

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
8
Emyl

プライベートIPスペース のすべては、常に3つのIPアドレスブロックのいずれかで始まります。

  • 24ビットブロック-10.X.X.X
  • 20ビットブロック-172.16.X.X-172.31.X.X
  • 16ビットブロック-192.168.X.X

したがって、上記のタイプのIPアドレスに対してgrepを実行するだけです。

$ 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をテストするためだけに次の行をファイルに追加するとします。

$ 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
13
slm

IPv4は、32ビットシステムが普及したときに作成されました。 IPv4のドット付き10進アドレスは32ビットの符号なし整数に格納でき、ビット単位の演算はネットワークハードウェアによって効率的に実行されます。 172.16.0.0/12 CIDRのビットマスクは、単一の左シフトから形成でき、単一のビット単位ANDを使用してアドレスに対してチェックできます。

RFC-1918で定義された3つの「プライベート」ネットワークアドレス範囲があります。

  • CIDR/8、(A)単一の大規模ネットワーク、(24ビット、16M)アドレス範囲10.x.y.z/8
  • CIDR/12、(B)172.16+x.y.z/12での16の隣接するネットワーク(20ビット、1M)アドレス範囲、ここでx in [0..15]
  • CIDR/16、(C)256の連続したネットワーク(16ビット、64K)アドレス範囲192.168.y.z/16

また、キャリアネットワークの細分化については、

  • CIDR/10、(A)単一の大規模ネットワーク、(24ビット、16M)アドレス範囲100.64+x.y.z/10、ここでx in [0..63]

リンクローカルアドレスの場合、

  • CIDR/16、(B)169.254.y.z/16での単一ネットワーク(16ビット、64K)アドレス範囲

ビット演算をサポートする言語を使用すると、ドット付き10進アドレスを整数に簡単に変換できます。

//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アドレスがこれらのアドレスの1つであるかどうかの確認は簡単です。

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)ネットワークの1つに含まれているかどうかを直接チェックできます。 Shellまたはawkの代わりにPerl(pythonまたはRubyも機能))を選択し、単一のビット単位の操作を使用すると、作業が大幅に削減されます。

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

Perlスクリプト内からifconfigを実行します。

$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); }
1
ChuckCottrill

プライベートIPを表示

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]\.)'

パブリックIPを表示

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]\.)'
1
mikeytown2

出力(IPごとに1行)は、次のスクリプトでフィルタリングできます。

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