use strict; use warnings; use Test::More 0.88; use List::AllUtils qw( each_array ); use Math::Int128 qw(uint128); use Net::Works::Network; { _test_remove_reserved_subnets( [ '0.0.0.1', '1.2.3.4' ] => [ [ '0.0.0.1', '1.2.3.4' ] ] ); _test_remove_reserved_subnets( [ '9.10.11.12', '9.255.255.255' ] => [ [ '9.10.11.12', '9.255.255.255' ] ] ); _test_remove_reserved_subnets( [ '9.0.0.0', '12.0.0.0' ] => [ [ '9.0.0.0', '9.255.255.255' ], [ '11.0.0.0', '12.0.0.0' ], ] ); _test_remove_reserved_subnets( [ '9.0.0.0', '10.255.255.255' ] => [ [ '9.0.0.0', '9.255.255.255' ], ] ); _test_remove_reserved_subnets( [ '10.2.3.4', '12.255.255.255' ] => [ [ '11.0.0.0', '12.255.255.255' ], ] ); _test_remove_reserved_subnets( [ '10.0.0.0', '10.10.10.10' ] => [] ); _test_remove_reserved_subnets( [ '0.0.0.0', '255.255.255.255' ] => [ [ '0.0.0.0', '9.255.255.255' ], [ '11.0.0.0', '126.255.255.255' ], [ '128.0.0.0', '169.253.255.255' ], [ '169.255.0.0', '172.15.255.255' ], [ '172.32.0.0', '192.0.1.255' ], [ '192.0.3.0', '192.88.98.255' ], [ '192.88.100.0', '192.167.255.255' ], [ '192.169.0.0', '223.255.255.255' ], [ '240.0.0.0', '255.255.255.255' ], ] ); _test_remove_reserved_subnets( [ '::', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' ] => [ map { [ map { Net::Works::Address->new_from_string( string => $_, version => 6 )->as_string() } @{$_} ] } ( [ '::', '::9.255.255.255' ], [ '::11.0.0.0', '::126.255.255.255' ], [ '::128.0.0.0', '::169.253.255.255' ], [ '::169.255.0.0', '::172.15.255.255' ], [ '::172.32.0.0', '::192.0.1.255' ], [ '::192.0.3.0', '::192.88.98.255' ], [ '::192.88.100.0', '::192.167.255.255' ], [ '::192.169.0.0', '::223.255.255.255' ], [ '::240.0.0.0', '2000:ffff:ffff:ffff:ffff:ffff:ffff:ffff', ], [ '2001:1::', 'fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', ], [ 'fe00::', 'fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff', ], [ 'fec0::', 'feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', ], ), ], ); } { my $start = '0.0.0.1'; my $end = '0.0.0.255'; my @expect = ( map { Net::Works::Network->new_from_string( string => $_ ) } qw( 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 ) ); _test_range_as_subnets( $start, $end, \@expect ); } { my $start = '10.0.0.0'; my $end = '12.0.0.0'; my @expect = ( map { Net::Works::Network->new_from_string( string => $_ ) } qw( 11.0.0.0/8 12.0.0.0/32 ) ); _test_range_as_subnets( $start, $end, \@expect ); } { my $start = '0.0.0.1'; my $end = '19.255.255.255'; my @expect = ( map { Net::Works::Network->new_from_string( string => $_ ) } qw( 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/7 11.0.0.0/8 12.0.0.0/6 16.0.0.0/6 ) ); _test_range_as_subnets( $start, $end, \@expect ); } { my $start = '::1'; my $end = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'; my @subnets = do { map { Net::Works::Address->new_from_integer( integer => uint128(2)**$_, version => 6 )->as_string() . '/' . ( 128 - $_ ) } 0 .. 26; }; my @expect = ( map { Net::Works::Network->new_from_string( string => $_, version => 6 ) } @subnets, qw( ::8.0.0.0/103 ::11.0.0.0/104 ::12.0.0.0/102 ::16.0.0.0/100 ::32.0.0.0/99 ::64.0.0.0/99 ::96.0.0.0/100 ::112.0.0.0/101 ::120.0.0.0/102 ::124.0.0.0/103 ::126.0.0.0/104 ::128.0.0.0/99 ::160.0.0.0/101 ::168.0.0.0/104 ::169.0.0.0/105 ::169.128.0.0/106 ::169.192.0.0/107 ::169.224.0.0/108 ::169.240.0.0/109 ::169.248.0.0/110 ::169.252.0.0/111 ::169.255.0.0/112 ::170.0.0.0/103 ::172.0.0.0/108 ::172.32.0.0/107 ::172.64.0.0/106 ::172.128.0.0/105 ::173.0.0.0/104 ::174.0.0.0/103 ::176.0.0.0/100 ::192.0.0.0/119 ::192.0.3.0/120 ::192.0.4.0/118 ::192.0.8.0/117 ::192.0.16.0/116 ::192.0.32.0/115 ::192.0.64.0/114 ::192.0.128.0/113 ::192.1.0.0/112 ::192.2.0.0/111 ::192.4.0.0/110 ::192.8.0.0/109 ::192.16.0.0/108 ::192.32.0.0/107 ::192.64.0.0/108 ::192.80.0.0/109 ::192.88.0.0/114 ::192.88.64.0/115 ::192.88.96.0/119 ::192.88.98.0/120 ::192.88.100.0/118 ::192.88.104.0/117 ::192.88.112.0/116 ::192.88.128.0/113 ::192.89.0.0/112 ::192.90.0.0/111 ::192.92.0.0/110 ::192.96.0.0/107 ::192.128.0.0/107 ::192.160.0.0/109 ::192.169.0.0/112 ::192.170.0.0/111 ::192.172.0.0/110 ::192.176.0.0/108 ::192.192.0.0/106 ::193.0.0.0/104 ::194.0.0.0/103 ::196.0.0.0/102 ::200.0.0.0/101 ::208.0.0.0/100 ::240.0.0.0/100 ::1:0:0/96 ::2:0:0/95 ::4:0:0/94 ::8:0:0/93 ::10:0:0/92 ::20:0:0/91 ::40:0:0/90 ::80:0:0/89 ::100:0:0/88 ::200:0:0/87 ::400:0:0/86 ::800:0:0/85 ::1000:0:0/84 ::2000:0:0/83 ::4000:0:0/82 ::8000:0:0/81 ::1:0:0:0/80 ::2:0:0:0/79 ::4:0:0:0/78 ::8:0:0:0/77 ::10:0:0:0/76 ::20:0:0:0/75 ::40:0:0:0/74 ::80:0:0:0/73 ::100:0:0:0/72 ::200:0:0:0/71 ::400:0:0:0/70 ::800:0:0:0/69 ::1000:0:0:0/68 ::2000:0:0:0/67 ::4000:0:0:0/66 ::8000:0:0:0/65 0:0:0:1::/64 0:0:0:2::/63 0:0:0:4::/62 0:0:0:8::/61 0:0:0:10::/60 0:0:0:20::/59 0:0:0:40::/58 0:0:0:80::/57 0:0:0:100::/56 0:0:0:200::/55 0:0:0:400::/54 0:0:0:800::/53 0:0:0:1000::/52 0:0:0:2000::/51 0:0:0:4000::/50 0:0:0:8000::/49 0:0:1::/48 0:0:2::/47 0:0:4::/46 0:0:8::/45 0:0:10::/44 0:0:20::/43 0:0:40::/42 0:0:80::/41 0:0:100::/40 0:0:200::/39 0:0:400::/38 0:0:800::/37 0:0:1000::/36 0:0:2000::/35 0:0:4000::/34 0:0:8000::/33 0:1::/32 0:2::/31 0:4::/30 0:8::/29 0:10::/28 0:20::/27 0:40::/26 0:80::/25 0:100::/24 0:200::/23 0:400::/22 0:800::/21 0:1000::/20 0:2000::/19 0:4000::/18 0:8000::/17 1::/16 2::/15 4::/14 8::/13 10::/12 20::/11 40::/10 80::/9 100::/8 200::/7 400::/6 800::/5 1000::/4 2000::/16 2001:1::/32 2001:2::/31 2001:4::/30 2001:8::/29 2001:10::/28 2001:20::/27 2001:40::/26 2001:80::/25 2001:100::/24 2001:200::/23 2001:400::/22 2001:800::/21 2001:1000::/20 2001:2000::/19 2001:4000::/18 2001:8000::/17 2002::/15 2004::/14 2008::/13 2010::/12 2020::/11 2040::/10 2080::/9 2100::/8 2200::/7 2400::/6 2800::/5 3000::/4 4000::/2 8000::/2 c000::/3 e000::/4 f000::/5 f800::/6 fe00::/9 fec0::/10 ) ); _test_range_as_subnets( $start, $end, \@expect ); } done_testing(); sub _test_remove_reserved_subnets { my $range = shift; my $expect = shift; my $version = $range->[0] =~ /:/ ? 6 : 4; # All these conversions are gross but normally this is handled by the # range_as_subnets method. my $first = Net::Works::Address->new_from_string( string => $range->[0], version => $version, )->as_integer(); my $last = Net::Works::Address->new_from_string( string => $range->[1], version => $version )->as_integer(); my @got = map { [ map { Net::Works::Address->new_from_integer( integer => $_, version => $version ) } @{$_} ] } Net::Works::Network->_remove_reserved_subnets_from_range( $first, $last, $version, ); is_deeply( \@got, $expect, "_remove_reserved_subnets_from_range returned expected result for $range->[0] - $range->[1]" ); } sub _test_range_as_subnets { my $start = shift; my $end = shift; my $expect_subnets = shift; my @subnets = Net::Works::Network->range_as_subnets( $start, $end ); is( scalar @subnets, scalar @{$expect_subnets}, 'got the same number of subnets as we expect' ); my $iter = each_array( @subnets, @{$expect_subnets} ); while ( my ( $got, $expect ) = $iter->() ) { last unless $got && $expect; my $first = eval { $expect->first()->as_ipv4_string() } // $expect->first()->as_string(); is( $got->first()->as_string(), $expect->first()->as_string(), "subnet first matches expected first - split $start - $end ($first)" ); my $last = eval { $expect->last()->as_ipv4_string() } // $expect->last()->as_string(); is( $got->last()->as_string(), $expect->last()->as_string(), "subnet last matches expected last - split $start - $end ($last)" ); my $netmask = $expect->mask_length(); is( $got->mask_length(), $netmask, "netmask matches expected netmask - split $start - $end ($netmask)" ); } }