diff --git a/IP.pm b/IP.pm index fac167f..4822f28 100644 --- a/IP.pm +++ b/IP.pm @@ -32,7 +32,7 @@ ); require Exporter; -@EXPORT_OK = qw(Compact Coalesce Zero Zeros Ones V4mask V4net netlimit); +@EXPORT_OK = qw(Compact Coalesce Zero Zeros Ones V4mask V4net Exclude netlimit); @EXPORT_FAIL = qw($_netlimit); @ISA = qw(Exporter NetAddr::IP::Lite); @@ -58,6 +58,7 @@ Ones V4mask V4net + Exclude netlimit :aton DEPRECATED :lower @@ -421,6 +422,28 @@ return &coalesce; } +sub Exclude { + my @low; + my @up; + return ([],[]) if $_[0] == $_[1]; + my ($s1,$s2) = $_[0]->split($_[0]->masklen+1); + while ($s1 != $_[1] && $s2 != $_[1]) { + if ($_[1]->within($s1)) { + push @up, $s2; + ($s1,$s2) = $s1->split($s1->masklen+1); + } elsif ($_[1]->within($s2)) { + push @low, $s1; + ($s1,$s2) = $s2->split($s2->masklen+1); + } else { + croak("exclude error: $_[1] is not contained in $_[0]"); + } + } + push @up, $s2 if $s1 == $_[1]; + push @low, $s1 if $s2 == $_[1]; + @up = reverse @up; + return (\@low, \@up); +} + sub hostenumref($) { my $r = _splitref(0,$_[0]); unless ((notcontiguous($_[0]->{mask}))[1] == 128 || diff --git a/t/v4-exclude.t b/t/v4-exclude.t new file mode 100644 index 0000000..1fb6c37 --- /dev/null +++ b/t/v4-exclude.t @@ -0,0 +1,41 @@ +use NetAddr::IP qw(Exclude); + +my @r = ( + ['10.0.0.0/8','10.0.0.0/9', [], ['10.128.0.0/9']], + ['10.0.0.0/8','10.0.0.0/8', [], []], + ['10.0.0.0/8','10.128.0.0/9', ['10.0.0.0/9'], []], + ['10.0.0.0/8','10.128.0.0/10', ['10.0.0.0/9'], ['10.192.0.0/10']], + ['10.0.0.0/8','10.0.0.0/32', [], ['10.0.0.1/32','10.0.0.2/31','10.0.0.4/30','10.0.0.8/29','10.0.0.16/28','10.0.0.32/27','10.0.0.64/26','10.0.0.128/25','10.0.1.0/24','10.0.2.0/23','10.0.4.0/22','10.0.8.0/21','10.0.16.0/20','10.0.32.0/19','10.0.64.0/18','10.0.128.0/17','10.1.0.0/16','10.2.0.0/15','10.4.0.0/14','10.8.0.0/13','10.16.0.0/12','10.32.0.0/11','10.64.0.0/10','10.128.0.0/9']], + ['192.168.0.0/24','192.168.0.255/32',['192.168.0.0/25','192.168.0.128/26','192.168.0.192/27','192.168.0.224/28','192.168.0.240/29','192.168.0.248/30','192.168.0.252/31','192.168.0.254/32'],[]], + ['192.168.0.0/23','192.168.0.127/32',['192.168.0.0/26','192.168.0.64/27','192.168.0.96/28','192.168.0.112/29','192.168.0.120/30','192.168.0.124/31','192.168.0.126/32'],['192.168.0.128/25','192.168.1.0/24']], + ['1.2.3.4/32','1.2.3.4/32',[],[]], + ['0.0.0.0/0','0.0.0.0/32',[],['0.0.0.1/32','0.0.0.2/31','0.0.0.4/30','0.0.0.8/29','0.0.0.16/28','0.0.0.32/27','0.0.0.64/26','0.0.0.128/25','0.0.1.0/24','0.0.2.0/23','0.0.4.0/22','0.0.8.0/21','0.0.16.0/20','0.0.32.0/19','0.0.64.0/18','0.0.128.0/17','0.1.0.0/16','0.2.0.0/15','0.4.0.0/14','0.8.0.0/13','0.16.0.0/12','0.32.0.0/11','0.64.0.0/10','0.128.0.0/9','1.0.0.0/8','2.0.0.0/7','4.0.0.0/6','8.0.0.0/5','16.0.0.0/4','32.0.0.0/3','64.0.0.0/2','128.0.0.0/1']], +); + +print "1..9\n"; + +my $num = 0; +foreach my $case (@r) { + $num++; + my ($low, $up) = Exclude(NetAddr::IP->new($case->[0]), NetAddr::IP->new($case->[1])); + my $ex_low = $case->[2]; + my $ex_up = $case->[3]; + + unless (scalar(@$low) == scalar(@$ex_low) && scalar(@$up) == scalar(@$ex_up)) { + print "not ok $num\n"; + next; + } + for (my $i=0; $i < @$low; $i++) { + if (@$low[$i] != NetAddr::IP->new(@$ex_low[$i])) { + print "not ok $num\n"; + next; + } + } + for (my $i=0; $i < @$up; $i++) { + if (@$up[$i] != NetAddr::IP->new(@$ex_up[$i])) { + print "not ok $num\n"; + next; + } + } + print "ok $num\n"; +}