Skip to content

Instantly share code, notes, and snippets.

@djpadz
Created April 13, 2020 20:18
Show Gist options
  • Save djpadz/6f78d650f5ffe5b507f98376c3b6a1f8 to your computer and use it in GitHub Desktop.
Save djpadz/6f78d650f5ffe5b507f98376c3b6a1f8 to your computer and use it in GitHub Desktop.
PERL script that takes a list of CIDRs and reduces them to a minimal set
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
my @ips;
sub ipCmp($$) {
my $ip1 = shift;
my $ip2 = shift;
my $r = ($ip1->[0] <=> $ip2->[0]);
return $r ? $r : ($ip2->[1] <=> $ip1->[1]);
}
sub ipStr($) {
my $i = shift;
my @ip;
foreach (0..3) {
unshift @ip, $i & 0xff;
$i >>= 8;
}
return join('.', @ip);
}
sub reduce($$) {
my $start = shift;
my $end = shift;
my @ret;
while ($start <= $end) {
my $nmb = 1;
while (1) {
my $nm = (2 ** $nmb) - 1;
last if (($start & ~$nm) != $start) || (($start | $nm) > $end);
++$nmb;
}
--$nmb;
push @ret, ipStr($start) . '/' . (32 - $nmb);
$start += 2 ** $nmb;
}
return @ret;
}
print STDERR "Reading...\n";
while (<>) {
my ($ip, $nm) = /(?:\s|^)((?:\d+\.){0,3}(?:\d+))(?:\/(\d+))(?:\s|$)/;
next unless $ip;
$nm = 32 unless $nm;
my @ipa = split(/\./, $ip);
unshift @ipa, 0, 0, 0 if @ipa == 1;
splice @ipa, 1, 0, 0 while @ipa < 4;
my $start = 0;
foreach (@ipa) {
$start = ($start << 8) | ($_ & 0xff);
}
my $nmb = ((2 ** $nm) - 1) << (32 - $nm);
$start &= $nmb;
my $end = $start | (~$nmb & 0xffffffff);
push @ips, [ $start, $end, "$ip/$nm" ];
}
print STDERR "Sorting...\n";
@ips = sort { ipCmp($a, $b) } @ips;
print STDERR "Reducing...\n";
my $start = $ips[0]->[0];
my $end = $ips[0]->[1];
foreach my $ip (@ips) {
if ($ip->[0] <= $end + 1) {
$end = $ip->[1] if $ip->[1] > $end;
next;
}
my @r = reduce($start, $end);
print join("\n", @r), "\n";
$start = $ip->[0];
$end = $ip->[1];
}
my @r = reduce($start, $end);
print join("\n", @r), "\n";
exit 0;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment