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

I've got a very stripped-down perl in an embedded system. Basically, no modules. I have a list of network interfaces, their network, netmask, and broadcast addresses. I need to enumerate all the possible IP addresses local to each network interface. I'll bet an efficient algorithm for this has been covered here at the monastery somewhere before --- any pointers to where I might find one? Thanks, Dave
  • Comment on algorithm for network address enumeration

Replies are listed 'Best First'.
Re: algorithm for network address enumeration
by gaal (Parson) on Feb 10, 2005 at 18:30 UTC
    first = member AND mask last = first OR (NOT mask)

    You probably want to remove the broadcast address from this range?

    Also, read perlop to prevent confusion about bitwise operators; and grep/perldoc -q around for packing and unpacking an IP address in a binary.

      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
        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.

        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 $/;
Re: algorithm for network address enumeration
by dragonchild (Archbishop) on Feb 10, 2005 at 18:19 UTC
    1. Figure out how you'd do this task with modules
    2. Read the code for the modules you'd use
    3. Start to strip out the modules by incorporating the code from the modules into your code
    4. Optimize for your specific situation

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.