Category: | Networking Code |
Author/Contact Info | runrig |
Description: | Inspired by Dominus' Challenge Problem: Merging Network Addresses, I posted a reply script which was (about 20 times) faster than the Net::CIDR solution, and thought I'd make a module out of it. Also looking for comments on whether it ought to be on CPAN, and under what name. I'd never before heard of let alone used the Socket::inet_* functions, so I couldn't have done it this way without that thread. It might be interesting to get this to work optionally with IPv6 addresses, but then you'd probably have to use some big integer library like Bit::Vector, so I'm open to suggestions on that :)
Updated with tye's recommendation. Update: Net::CIDR::Lite has been on CPAN for awhile now and updated several times over. Consider the code on this page obsolete. |
Sample usage: use Net::CIDR::Lite; my $cidr = Net::CIDR::Lite->new; $cidr->add("209.152.214.112/30"); $cidr->add("209.152.214.116/31"); $cidr->add("209.152.214.118/31"); print "$_\n" for $cidr->list; And the module: ################################## package Net::CIDR::Lite; use strict; use Socket qw(inet_aton inet_ntoa); use vars qw($VERSION); $VERSION = '0.01'; my @masks = (0,0,map { pack("B*", substr("1" x $_ . "0" x 32, 0, 32)) +} 2..32); my @bits2rng = (0,0,map { 2**(32 - $_) } 2..32); my %rng2bits = map { $bits2rng[$_] => $_ } 0..32; sub new { my $proto = shift; my $class = ref($proto) || $proto; bless {}, $class; } sub add { my $self = shift; local $_ = shift; my ($ip, $mask) = split "/"; my $start = inet_aton($ip) & $masks[$mask]; my $end = pack("N", unpack("N", $start) + $bits2rng[$mask]); $$self{$start}++; $$self{$end}--; } sub clean { my $self = shift; %$self = map { $$self{$_} ? ($_ => $$self{$_}) : () } keys %$self; } sub list { my $self = shift; my ($start, $total); my @results; for my $ip (sort keys %$self) { $start = $ip unless $total; $total += $$self{$ip}; unless ($total) { my $diff = unpack("N", $ip) - unpack("N", $start); while ($diff) { (my $zeros = unpack("B*", $start)) =~ s/^.*1//; my $range; for my $i (32-length($zeros)..32) { $range = $bits2rng[$i], last if $bits2rng[$i] <= $ +diff; } push @results, inet_ntoa($start)."/".$rng2bits{$range} +; $diff -= $range; $start = pack("N", unpack("N", $start)+$range); } } } wantarray ? @results : \@results; } 1; __END__ =head1 NAME Net::CIDR::Lite - Perl extension for merging CIDR addresses =head1 SYNOPSIS use Net::CIDR::Lite; my $cidr = Net::CIDR::Lite->new; $cidr->add($cidr_address); @cidr_list = $cidr->list; =head1 DESCRIPTION Faster alternative to Net::CIDR::cidradd. Limited for the time being to IPv4 addresses. =head1 METHODS =item new() $cidr = Net::CIDR::Lite->new Creates an object to represent a list of CIDR address ranges. =item add() $cidr->add($cidr_address) Adds a CIDR address range to the list. =item $cidr->clean() $cidr->clean; If you are going to call the list method more than once on the same data, then for optimal performance, you can call this to purge null nodes from the list. =item $cidr->list() @cidr_list = $cidr->list; Returns a list of the merged CIDR addresses. =head1 CAVEATS Garbage in/garbage out. This module makes no attempt to validate the format of your data. =head1 AUTHORrunrig =head1 COPYRIGHT This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L<Net::CIDR>. =cut |
Back to
Code Catacombs