以编程方式提取私有 IP 地址

以编程方式提取私有 IP 地址

我正在寻找一种简单的方法来以编程方式提取私人的计算机的 IPv4 地址。

类似的东西这个问题,但仅限于私有 IP。

举个例子,我可以提取全部使用以下命令:

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

答案1

任何东西都在私有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结果。

$ 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

答案2

显示私有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]\.)'

答案3

IPv4 是在 32 位系统盛行的时候创建的。 IPv4点分十进制地址可以存储为32位无符号整数,并且网络硬件可以有效地执行按位运算。 172.16.0.0/12 CIDR 的位掩码可以由单个左移形成,并使用单个按位与对地址进行检查。

RFC-1918 定义了三个“专用”网络地址范围。

  • CIDR/8,(A)单个大型网络,(24位,16M)地址范围为10.x.y.z/8
  • CIDR/12,(B) 16 个连续网络(20 位,1M)地址范围172.16+x.y.z/12,其中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) 单个网络(16 位,64K)地址范围169.254.y.z/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

可以使用上述位掩码方法来直接检查 ipv4 地址是否属于这些专用 (NAT) 网络之一,而不是检查 16 个不同的 172.16.0.0/12 网络。选择 perl(python 或 ruby​​ 也可以),而不是 shell 或 awk,并使用单个按位 and 操作大大减少了工作量。

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

答案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

用法例如:

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

相关内容