Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Unsorted IP Addr list to sorted IP list with ranges

by blue_cowdawg (Monsignor)
on Sep 11, 2003 at 15:03 UTC ( [id://290709]=perlquestion: print w/replies, xml ) Need Help??

blue_cowdawg has asked for the wisdom of the Perl Monks concerning the following question:

Under the heading of "I've seen how to do this in my travels by CRS has set in" I remember seeing an algorithm (or may have actually written the thing myself) to take an unsorted list of IP addresses, sort them and then substitute ranges for sequential addresess.

Just so I'm clear as to what I'm after, here's a small sample list:

10.0.4.100 10.0.4.99 10.3.5.21 10.0.4.101 10.15.21.6
The expected output would look like:
10.0.4.99-10.0.4.101 10.3.5.21 10.15.21.6

Has anybody done written anything like this or have an idea how to efficiently do this?


Peter L. Berghold -- Unix Professional
Peter at Berghold dot Net
   Dog trainer, dog agility exhibitor, brewer of fine Belgian style ales. Happiness is a warm, tired, contented dog curled up at your side and a good Belgian ale in your chalice.

Replies are listed 'Best First'.
Re: Unsorted IP Addr list to sorted IP list with ranges
by Anonymous Monk on Sep 11, 2003 at 15:46 UTC
    use Socket; my @ip = qw( 10.0.4.100 10.0.4.99 10.3.5.21 10.0.4.101 10.15.21.6); @ip = do { my $prev; map { defined($prev) && $prev == $_ ? () : ( $prev = $_ ) } sort { $a <=> $b } map { unpack "N", inet_aton $_ } @ip; }; for my $i ( 0 .. $#ip ) { if (!$i || $i == $#ip || $ip[$i-1] != $ip[$i] - 1 || $ip[$i+1] != $ip[$i] + 1 ) { print $ip[$i-1] == $ip[$i] - 1 ? "-" : " " if $i; print inet_ntoa( pack "N", $ip[$i] ); } } print "\n";
    Re: Unsorted IP Addr list to sorted IP list with ranges (CPAN module candidates)
    by ybiC (Prior) on Sep 11, 2003 at 15:37 UTC
    Re: Unsorted IP Addr list to sorted IP list with ranges
    by jonadab (Parson) on Sep 11, 2003 at 16:19 UTC

      The sorting part is easy:

      @sortedip = map { "$$_[0].$$_[1].$$_[2]$$_[3]" } sort { ($$a[0] <=> $$b[0]) or ($$a[1] <=> $$b[1]) or ($$a[2] <=> $$b[2]) or ($$a[3] <=> $$b[3]) } map { /(\d+)[.](\d+)[.](\d+)[.](\d+)/; [$1, $2, $3, $4] } @ip;

      Personally I would leave off that (typographically) first map, so that you get a list of arrayrefs; the range checking will probably be easier that way. Speaking of the range checking... you probably want to use a foreach loop over the now sorted list, in each case keeping track of the previous IP or range and pushing that if the current IP isn't contiguous. Something along these lines...

      @sorted = sort { ($$a[0] <=> $$b[0]) or ($$a[1] <=> $$b[1]) or ($$a[2] <=> $$b[2]) or ($$a[3] <=> $$b[3]) } map { /(\d+)[.](\d+)[.](\d+)[.](\d+)/; [$1, $2, $3, $4] } @ip; my $previous = []; my @range = (); for $current (@sorted, [260, 0, 0, 0]) { if (greaterbyone($previous,$current) { $$previous[4] = $$current[3]; } else { push @range, $previous; $previous = $current; }

      greaterbyone just has to return true if the IP address in its second argument is one greater than the IP address or range in its first argument. I'll leave that part as an exercise.


      $;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$ ;->();print$/
    Re: Unsorted IP Addr list to sorted IP list with ranges
    by zengargoyle (Deacon) on Sep 12, 2003 at 07:26 UTC

      from NetAddr::IP tutorial...

      Optimising the address space

      This is one of the reason for writing NetAddr::IP in the first place. Let's say you have a few chunks of IP space and you want to find the optimum CIDR representation for them. By optimum, I mean the least amount of CIDR subnets that exactly represent the given IP address space. The code below is an example of this:

      use NetAddr::IP;
      
      push @addresses, NetAddr::IP->new($_) for <DATA>;
      print join(", ", NetAddr::IP::compact(@addresses)), "\n";
      __DATA__
      10.0.0.0/18
      10.0.64.0/18
      10.0.192.0/18
      10.0.160.0/19
      

      Which will, of course, output 10.0.0.0/17, 10.0.160.0/19, 10.0.192.0/18.

      and also in the tutorial is enough to print those combined ranges in just about any format you can come up with.

    Re: Unsorted IP Addr list to sorted IP list with ranges
    by atcroft (Abbot) on Sep 11, 2003 at 23:27 UTC

      If there is a better solution that uses established modules, I would suggest that. Barring that, here is some code I was toying with a while back to do something similar-probably not the best or most efficient way to do it, however. (Address data was in __DATA__, which I did not include here for length.) Hope maybe this might give you an idea or two (if nothing else, maybe of how not to do it).

    Re: Unsorted IP Addr list to sorted IP list with ranges
    by TStanley (Canon) on Sep 12, 2003 at 12:23 UTC
      davorg shows an interesting way of sorting IP addresses, using the Guttman-Rosler Transform in his book
      my @IP=qw(10.0.4.100 10.0.4.99 10.3.5.21 10.0.4.101 10.15.21.6); my @sorted_IP=map{substr($_,4) } sort map{pack('C4',/(\d+)\.(\d+)\.(\d+)\.(\d+)/).$_}@IP;

      TStanley
      --------
      The only thing necessary for the triumph of evil is for good men to do nothing -- Edmund Burke

    Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Domain Nodelet?
    Node Status?
    node history
    Node Type: perlquestion [id://290709]
    Approved by ybiC
    help
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this?Last hourOther CB clients
    Other Users?
    Others chanting in the Monastery: (9)
    As of 2024-04-16 08:43 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      No recent polls found