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

How do I itterate to the next subnet of an IP address?

Ie, if I have 172.168.0.0/16, I'd like the next itteration to be 172.169.0.0/16

Or, if I have 10.0.0.0/24, the next should be 10.0.1.0/24

This should print 192.168.1.0 the second time - it errors:

#!/usr/bin/env perl use strict; use warnings; use Net::IP; my $ip = Net::IP->new('192.168.0.0/24'); print "Start ip [" . $ip->ip . "]\n"; print "start mask [" . $ip->prefixlen . "]\n"; $ip->set($ip->last_ip); $ip++; $ip->set($ip->ip . "/" . $ip->prefixlen); print "Start ip [" . $ip->ip . "]\n"; print "start mask [" . $ip->prefixlen . "]\n";

## ERROR

% ./t2.pl Start ip [192.168.0.0] start mask [24] Can't call method "ip" on an undefined value at ./t2.pl line 15.

Cross posted from beginners@perl.org: http://www.nntp.perl.org/group/perl.beginners/2013/05/msg122907.html

Replies are listed 'Best First'.
Re: Next subnet
by Athanasius (Archbishop) on May 25, 2013 at 07:45 UTC

    You can add 0.0.0.1 using the binadd method :

    #! perl use strict; use warnings; use Net::IP; my $one = Net::IP->new('0.0.0.1'); my $ip1 = Net::IP->new('192.168.0.0/24'); my $ip2 = Net::IP->new('172.168.0.0/16'); my $ip3 = Net::IP->new('10.0.0.0/24'); for my $ip ($ip1, $ip2, $ip3) { print "\nStart IP [", $ip->ip(), "]\n"; print "Start mask [", $ip->prefixlen(), "]\n"; $ip->set($ip->last_ip()); $ip = $ip->binadd($one); print "Next subnet [", $ip->ip(), "]\n"; }

    Output:

    17:41 >perl 629_SoPW.pl Start IP [192.168.0.0] Start mask [24] Next subnet [192.168.1.0] Start IP [172.168.0.0] Start mask [16] Next subnet [172.169.0.0] Start IP [10.0.0.0] Start mask [24] Next subnet [10.0.1.0] 17:41 >

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Thanks, that works.
Re: Next subnet
by FloydATC (Deacon) on May 25, 2013 at 09:21 UTC
    Subnets aren't really that complicated if you convert them to integers. I haven't really tested this code a lot but it should work for any contiguous netmask and valid subnet/netmask combination. You may want to add some error-checking for that.

    use Socket; sub next_subnet { my ($network, $subnet) = @_; my $inet = unpack('N', inet_aton($network)); my $mask = unpack('N', inet_aton($netmask)); my $ones = unpack('N', inet_aton('255.255.255.255')); my $size = ($ones ^ $mask) + 1; my $next = $inet + $size; return inet_ntoa(pack('N', $next)); }

    You may find this handy too:

    sub prefix_to_mask { my ($bits) = @_; return inet_ntoa(pack('N', (2**32) - (2**(32-$bits)))); }

    Converting from netmask to prefix length is left as an exercise :-)

    Edit: I almost forgot, you have to use Socket; for inet_aton/inet_ntoa.

    -- Time flies when you don't know what you're doing
      Greetings ag4ve,
      I'm not sure if this is something you are looking to code all on your own;
      But either way, Net::CIDR might be of some help here. If your just looking for ideas,
      you can have a look at CIDR.pm itself.

      HTH

      --chris

      #!/usr/bin/perl -Tw
      use perl::always;
      my $perl_version = "5.12.4";
      print $perl_version;
Re: Next subnet
by jakeease (Friar) on May 26, 2013 at 08:00 UTC

    Try another way of expressing the range. Off the top of my head, something like

    #!/usr/bin/env perl use warnings; use strict; use Net::IP; my $ip = new Net::IP ('192.168.0/23') || die ; # Loop do { print $ip->ip(), "\n"; } while (++$ip);

    where the prefix is bumped up in this case to /23.

      or this:

      #!/usr/bin/env perl use warnings; use strict; use Net::IP; my $ip = new Net::IP ('192.168.0.0 - 192.168.3.255' ) || die ; # Loop do { print $ip->ip(), "\n"; } while (++$ip);

      Same example, different range; here it crosses two byte boundaries.

      #!/usr/bin/env perl use warnings; use strict; use Net::IP; my $ip = new Net::IP ('10.168.254.0 - 10.169.1.255' ) || die ; # Loop do { print $ip->ip(), "\n"; } while (++$ip);