use strict; use warnings; sub parse_ipv4 { local *_ = \(@_ ? $_[0] : $_); return unpack('N', pack('C4', split(/\./))); } sub format_ipv4 { local *_ = \(@_ ? $_[0] : $_); return join('.', unpack('C4', pack('N', $_))); } my $member = parse_ipv4('10.20.40.183'); my $mask = parse_ipv4('255.255.0.0'); my $first = $member & $mask; my $broadcast = $first | ~$mask; # my $num_machines = $broadcast - $first + 1 - 2; # which simplifies to: my $num_machines = ~$mask + 1 - 2; printf("first = %s\n", format_ipv4($first)); printf("mask = %s\n", format_ipv4($mask)); printf("broadcast = %s\n", format_ipv4($broadcast)); printf("first usable = %s\n", format_ipv4($first+1)); printf("last usable = %s\n", format_ipv4($broadcast-1)); printf("This subnet can handle %d machines:\n", $num_machines); # 128.x.x.x and higher pack into a negative number on # 32-bit machines, so the following won't work: # # print(format_ipv4, $/) # for ($first+1 .. $broadcast-1); if ($mask & 0x7FFFFFFF == 0) { print(format_ipv4($_), $/) for (1 .. 0x7FFFFFFF); print(format_ipv4($_|0x80000000), $/) for (0 .. 0x7FFFFFFE); } else { print(format_ipv4($_+$first), $/) for ($first+1-$first .. $broadcast-1-$first); } __END__ first = 10.20.0.0 mask = 255.255.0.0 broadcast = 10.20.255.255 first usable = 10.20.0.1 last usable = 10.20.255.254 This subnet can handle 65534 machines: 10.20.0.1 10.20.0.2 . . . 10.20.255.253 10.20.255.254