in reply to Re: algorithm for network address enumeration
in thread algorithm for network address enumeration

An implementation:

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

Update: Tweaks.

Update 2: Fixed for ($first+1 .. $broadcast-1).

Note: Requires a perl with 32-bit integers.

Note: I didn't write code to check if IP address and the mask are in the valid format.

sub parse_ipv4 { local *_ = \(@_ ? $_[0] : $_); /^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/ && $1 < 256 && $2 < 256 && $3 < 256 && $4 < 256 or return undef; return unpack('N', pack('C4', $1, $2, $3, $4)); }

Note: The mask isn't validated. The binary representation of the mask must matched (length == 32 && /^1*0*$/) || $_ == 0.

Note: Keep in mind the above code can print over 4 billion addresses!

Mask Number of addresses printed /32 255.255.255.255 0 /31 255.255.255.254 0 /30 255.255.255.252 2 /29 255.255.255.248 6 /28 255.255.255.240 14 /27 255.255.255.240 30 /26 255.255.255.240 62 /25 255.255.255.240 126 /24 255.255.255.0 254 ... /16 255.255.0.0 65,534 ... /8 255.0.0.0 16,777,214 ... /3 224.0.0.0 536,870,910 /2 192.0.0.0 1,073,741,822 /1 128.0.0.0 2,147,483,646 /0 0.0.0.0 4,294,967,294

Replies are listed 'Best First'.
Re^3: algorithm for network address enumeration
by gaal (Parson) on Feb 10, 2005 at 19:47 UTC
    You're right to subtract both the first and last addresses that fall under the mask, but if the input contains an explicit broadcast address like the OP says it's better to stick with that for output; I believe some old equipment uses 0 instead of -1 for broadcast.

    (And your regexp works, but "\." is a bit better than ".".)

    Nifty implementation though.

      You're right to subtract both the first and last addresses that fall under the mask, but if the input contains an explicit broadcast address like the OP says it's better to stick with that for output; I believe some old equipment uses 0 instead of -1 for broadcast.

      I provided an excessive amount of info, so that he can easily customize it to his needs.

      And your regexp works, but "\." is a bit better than ".".)

      Thanks. Noticed and fixed. Actually, I replaced the regexp with split(/\./). It's probably a tad slower, but it mirrors the join.

Re^3: algorithm for network address enumeration
by Jaap (Curate) on Feb 11, 2005 at 10:32 UTC
    What does this do?
    print(format_ipv4, $/) for ($first+1 .. $broadcast-1);
    I would expect $_ there.

    Edit: never mind. i get it:
    print format_ip4($_); print $/;
      I wrote format_ipv4 to use $_ if no arguments are supplied (like length, split and a number of other builtins do), so
      print(format_ipv4, $/)
      is the same as
      print(format_ipv4($_), $/)