有没有一个工具可以将 IP 列表转换为固定网络 CIDR形式x.x.x.0/24
?x.x.0.0/16
例如,为了演示这一点,我有可能可以转换为这些固定形式的 IP 列表,如下所示:
首先,如果我想将下面的 IP 列表转换为可能的 CIDR 24,但具有固定形式x.x.x.0/24
:
./list2cidr 24 iplist.txt
1.22.3.4
1.28.3.5
1.211.3.7
1.211.3.2
1.211.3.1
Output:
1.22.3.4
1.28.3.5
1.211.3.0/24
它是如何工作的?
首先,它扫描具有相同网络的 IP 列表,如果它们位于同一网络中,它会查看第 3 个八位字节,我们将其组合起来成为x.x.x.0/24
。在本例中,第 3 个八位字节中有 3 个具有相同网络的 IP:
1.211.3.7
1.211.3.2
1.211.3.1
所以这将是:1.211.3.0/24
另一个例子,如果我想将下面的 IP 列表转换为可能的 CIDR 16,其固定形式为x.x.0.0/16
:
./list2cidr 16 iplist.txt
1.1.4.1
1.1.4.2
1.22.44.1
1.22.3.2
1.22.1.9
Output:
1.1.0.0/16
1.22.0.0/16
当我传递包含它的参数时,16
将查看第二个八位字节是否位于同一网络中,因此我们将其组合起来成为x.x.0.0/16
我应该开始为此编写脚本吗?或者是否有用于此目的的工具?
编辑:
我有兴趣使 CIDR 表单看起来像以下之一:1.1.1.0/24
和1.1.0.0/16
。所以,1.1.0.0/24
还是1.1.1.0/16
不是我想要的。
这意味着,如果通过list2cidr 24 iplist.txt
,它必须形成这个输出
x.x.x.0/24
如果我通过了list2cidr 16 iplist.txt
,它必须形成这个输出
x.x.0.0/16
目前,我已经使用 bash 脚本完成了输出的第一部分,但我还没有完全测试它
答案1
我不知道,所以我写了一个(用 Perl)。我还认为你的工具设计不完整。
- 它应该只提供所请求级别的 CIDR 块,即使其中只有一个地址。
- 它也应该在没有目标水平的情况下工作。
因此:
$ cat sample
1.22.3.4
1.28.3.5
1.211.3.7
1.211.3.2
1.211.3.1
$ list2cidr 24 sample
1.22.3.0/24
1.28.3.0/24
1.211.3.0/24
$ list2cidr 16 sample
1.22.0.0/16
1.28.0.0/16
1.211.0.0/16
$ list2cidr sample
1.0.0.0/8
1.16.0.0/12
1.22.3.4
1.28.3.5
1.211.3.0/29
1.211.3.0/30
1.211.3.1
1.211.3.2
1.211.3.7
$
我当前的实现仅支持 IPv4。代码是:
#!/usr/bin/perl -w
use strict;
my $target;
if ($ARGV[0] =~ m{^\d+}) {
$target = + shift @ARGV;
}
my $map = [];
sub record($)
{
my $v = shift;
my $m = $map;
for my $i ( 0 .. 31 ) {
my $k = $v & (1 << (31-$i));
$m = $m->[!!$k] ||= (($i == 31) ? $v : []);
}
}
while (<>) {
chomp;
if (m{^\s*(\d+)\.(\d+)\.(\d+)\.(\d+)\s*\z}) {
if (($1 < 256) && ($2 < 256) && ($3 < 256) && ($4 < 256)) {
record(($1<<24) | ($2<<16) | ($3 << 8) | $4);
next;
}
}
printf("Invalid: %s\n", $_);
}
sub output($$$) {
my ($addr, $bits, $indent) = @_;
printf "%*s%d.%d.%d.%d",
$indent*4, '',
0xff & ($addr >> 24),
0xff & ($addr >> 16),
0xff & ($addr >> 8),
0xff & ($addr );
printf("/%d", $bits) if $bits < 32;
print "\n";
}
sub walk($$$$);
sub walk($$$$) {
my ($prefix, $bits, $indent, $m) = @_;
#printf ("%d %d %d ...\n", $prefix, $bits, $indent);
if ($bits == ($target//-1)) {
output $prefix<<(32-$bits), $bits, 0;
} elsif ($bits == 32) {
warn 'mismatch '.$prefix.' != '.$m unless $prefix == $m;
output $prefix, $bits, $indent unless defined $target;
} elsif (defined $m->[0]) {
if (defined $m->[1]) {
output $prefix<<(32-$bits), $bits, $indent unless defined $target;
walk($prefix*2, $bits+1, $indent+1, $m->[0]);
walk($prefix*2+1, $bits+1, $indent+1, $m->[1]);
} else {
walk($prefix*2, $bits+1, $indent, $m->[0]);
}
} else {
if (defined $m->[1]) {
walk($prefix*2+1, $bits+1, $indent, $m->[1]);
} else {
warn sprintf('Empty node at prefix=%x bits=%d indent=%d', $prefix, $bits, $indent);
}
}
}
walk (0, 0, 0, $map);
答案2
以下使用命令行实用程序ipcalc
(来自https://github.com/pyr/ipcalc)计算文件中给定的每个地址的网络地址,称为file
给定网络掩码$mask
。
xargs -I {} ipcalc {} / "$mask" <file |
awk -F: '$1 ~ /^network/ && !seen[$2]++ { gsub(" ","",$2); print $2 }'
最后,awk
解析ipcalc
输出并提取唯一的网络地址。
给定您的示例数据,运行它mask=24
给出
1.22.3.0/24
1.28.3.0/24
1.211.3.0/24
和mask=12
:
1.16.0.0/12
1.208.0.0/12
稍微修改一下,让我们可以在 Ubuntu 上使用ipcalc
这个包:ipcalc
xargs -I {} ipcalc -b {}/"$mask" <file |
awk -F: '$1 ~ /^Network/ && !seen[$2]++ { gsub(" ","",$2); print $2 }'