Noosrep
Noosrep

Reputation: 416

Find all IPs from subnet in perl

I have an IP with subnetmask and want to create a table in which every row is an IP.

Kind of like this:

IP          | MAC  | Alive 
192.168.1.1 | ...  | yes
192.168.1.2 | ...  | no
192.168.1.3 | ...  | yes
...

For a range with subnet /24, this is easy because I can generate a for loop from 0 to 254 but I'm having trouble with generating the table with a /21 subnet. Is there a way to find in perl what the minimum host, the maximum host and everything in between is?

I have checked the Net::IP and the NetAddr::IP module but don't seem a solution for my problem. However, I'm a perl newbie so it could be that I overlooked it.

edit: found how to find first and last address:

my $range = "IP/subnet" ;
my $ip = new Net::IP ($range) || die;
print $ip->ip (), "\n";
print $ip->last_ip (), "\n";

Now I need to find the IPs in between

Upvotes: 0

Views: 3682

Answers (2)

Mat
Mat

Reputation: 206727

The docs for Net::IP has an example for how to do this in the looping section.

Demo:

use Net::IP;

my $ip = new Net::IP("192.168.42.16/28");
do {
  print $ip->ip(), "\n";
} while (++$ip);
192.168.42.16
192.168.42.17
192.168.42.18
192.168.42.19
192.168.42.20
192.168.42.21
192.168.42.22
192.168.42.23
192.168.42.24
192.168.42.25
192.168.42.26
192.168.42.27
192.168.42.28
192.168.42.29
192.168.42.30
192.168.42.31

If you only have an IP in the desired range, not the prefix, you can compute the prefix from the address and netmask. Try something like this (there probably is a more clever way of achieving this with Net::IP though, but that's eluding me):

use Net::IP;

my $addr = "192.168.42.23";
my $len  = 28;

my $mid = new Net::IP($addr, 4);
my $mask = Net::IP::ip_get_mask($len, 4);
my $first = $mid->binip() & $mask;
my $ip = new Net::IP(Net::IP::ip_bintoip($first, 4)."/$len", 4);
do {
  print $ip->ip(), "\n";
} while (++$ip);

Upvotes: 4

simbabque
simbabque

Reputation: 54333

You can do that with NetAddr::IP and the nth method. I created a helper sub that makes use of a singleton to create an iterator. You pass in any IP from the subnet and it gives you a code reference that you can to receive the next IP address object. It will do that until the range is exhausted, at which point it returns undef.

use strict;
use warnings 'all';
use NetAddr::IP;
use feature 'say';

sub make_ip_iterator {
    my $ip = shift;

    my $mask = NetAddr::IP->new($ip);

    my $i = 0;
    return sub {
        return $mask->nth($i++);
    }
}

my $iterator = make_ip_iterator('192.168.1.1/21');
while (my $ip = $iterator->()) {
    say $ip;
}

Because $mask and $i are lexical within the make_ip_iterator sub, you can call it multiple times and get distinct iterators back for different subnets that you use at the same time.

You could of course implement it with only a while loop and a counter variable because nth() will return undef when it's called with an index outside of the subnet, but this solution is more portable.

Upvotes: 4

Related Questions