in reply to Subnet Overlap

Here's a solution that not only addresses all the fixes, but uses a much better algorithm. (It's speed is bound by sort instead of O(N2).)

#!/usr/bin/perl # # The name of the file containing the data is specified as an argument +. # Alternatively, the data may passed via STDIN. # The data consist of mulitple records, one per line. # Each record has two columns: a network address and its mask. # The columns are seperated by whitespace. # Lines containing only whitespace are ignored. # Comments (anything after a "#") are ignored. # # # Example # ------- # # This is a sample data file. # # 10.0.0.0 255.255.255.0 # Overlaps with 4, 5 & 6 # 10.0.0.0 255.255.255.128 # Overlaps with 5 & 6 # 10.0.0.0 255.255.255.192 # 10.0.0.64 255.255.255.192 # # 11.0.0.0 255.255.255.0 # 11.0.1.0 255.255.255.0 # 11.0.2.0 255.255.254.0 # Overlaps with 11 # 11.0.3.0 255.255.255.0 # 11.0.4.0 255.255.255.0 # # 12.0.0.0 255.255.255.0 # Overlaps with 15 # 12.0.0.1 255.255.255.0 # use strict; use warnings; sub packip4 { return unpack('N', (pack 'C4', split(/\./, shift))); } sub unpackip4 { return join('.', unpack('C4', pack('N', shift))); } sub format_rec { my $rec = @_ ? $_[0] : $_; return sprintf('%s/%s (line %d)', unpackip4($rec->[0]), unpackip4($rec->[1]), $rec->[2], ); } sub overlap { my ($overlaps, $r1, $r2) = @_; if ($r1->[2] > $r2->[2]) { ($r1, $r2) = ($r2, $r1) } push @$overlaps, [ $r1, $r2 ]; } { my @recs; { while (<>) { s/#.*//; s/\s+\z//; next if !length; my ($net, $mask) = split /\s+/; $net = packip4($net); $mask = packip4($mask); my $first = $net & $mask; my $last = $net | ~$mask; push @recs, [ $first, $last, $. ]; } } my @overlaps; if (@recs) { @recs = sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] } @rec +s; my $crushed = shift @recs; my @components = ( $crushed ); while (@recs) { my $rec = shift @recs; if ($rec->[0] gt $crushed->[1]) { $crushed = $rec; @components = ( $rec ); next; } foreach my $component (@components) { next if $component->[0] gt $rec->[1] || $component->[1] lt $rec->[0]; overlap(\@overlaps, $rec, $component); } $crushed = [ $crushed->[0], ( $crushed->[1] gt $rec->[1] ? $crushed->[1] : $rec->[1] ) ]; push @components, $rec; } } print map { sprintf("%s overlaps %s\n", map format_rec, @$_) } sort { $a->[0][2] <=> $b->[0][2] || $a->[1][2] <=> $b->[1][2] + } @overlaps; }

Replies are listed 'Best First'.
Re^2: Subnet Overlap (fixes)
by bfarley (Initiate) on Aug 22, 2007 at 03:16 UTC
    Thanks for submitting your fixes. I don't quite understand all the code, so it will give me something to review and learn. Thanks again. -B
      The key tidbit is that the addresses are stored as a 4-byte string, which is why it's appropriate (and required) to use gt instead of > (etc).