IP network aggregation

Will cope with input that includes duplicated, overlapping and adjacent networks. Pipe the input into the script one per line as “x.x.x.x/xx”.
Updated with a fix from andreww that works around a floating point glitch performing log() operations whilst printing the output.

#!/usr/bin/perl -w

use strict;

my %stage;
while (<STDIN>) {
  chomp;
  next if ($_ eq '');
  (my $ip, my $mask) = $_ =~ /^(\d+\.\d+\.\d+\.\d+)\/(\d+)/;
  $stage{2**32-(2**(32-$mask))}{unpack('N', pack('C4', split(/\./, $ip)))} = 1;
}

my %mid;
foreach my $mask (sort keys %stage) {
ADDR:  foreach my $addr (keys %{$stage{$mask}}) {
    foreach my $seek (sort keys %stage) {
      last if ($seek == $mask);
      next ADDR if (defined $stage{$seek}{$addr & $seek});
    }
    $mid{$addr} = $mask;
  }
}

my @data;
push(@data, { ip => $_, mask => $mid{$_} }) foreach (sort { $a <=> $b } keys %mid);
my $c=0;
while ($c != @data) {
  for (my $i=0; $i<$#data; $i++) {
    next if ($data[$i]->{mask} != $data[$i+1]->{mask});
    my $n = $data[$i]->{mask} & $data[$i]->{mask}-1;
    if (($data[$i]->{ip} & $n) == ($data[$i+1]->{ip} & $n)) {
      $data[$i]->{mask} = $n;
      $data[$i+++1]->{ip} = undef;
    }
  }
  $c = @data;
  @data = grep {defined $_->{ip}} @data;
}

foreach (@data) { print dec2ip($_->{ip}).'/'.(32-(int(log((2**32)-$_->{mask})/log(2))))."\n"; }
sub dec2ip { return join '.' => map { ($_[0] >> 8*(3-$_)) % 256 } 0 .. 3; }

Comments

    Leave a comment