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

I am trying to write a perl script for generating mac address for a specific project and i am stuck at a point.

Project: generate mac addresses with in a given range where the base OUI is predefined and the end range is 2f:ff:ff. the start mac address would be: 00:96:14:00:00:00 and the end mac address would be: 00:96:14:2f:ff:ff

I am able to generate a random mac address but the 4 bit is not normally within the "2f" range.

my random ff:ff:ff code is
sub mac { $cnt = 3; $mac[0] = "00"; $mac[1] = "96"; $mac[2] = "14"; while ($cnt < 6) { $rand = rand(255); if ($rand < 5) { $rand += 2; $hex = sprintf("%X", $rand); } else { $hex = sprintf("%X", $rand); } $mac[$cnt] = $hex; $cnt++; }
my conditional code for 2f:ff:ff is:
sub mac { $cnt = 3; $mac[0] = "00"; $mac[1] = "96"; $mac[2] = "14"; while ($cnt < 6) { if ($cnt ==4) { $rand = rand(47); } else { $rand = rand(255); if ($rand < 5) { $rand += 2; $hex = sprintf("%X", $rand); } else { $hex = sprintf("%X", $rand); } $mac[$cnt] = $hex; $cnt++; } }
The problem is that the above conditional loop gets stuck when executing. any help is higly appreciated.

Note: i am newbie and have been researching perl docs online and the above has been written from various similar online snippets and reference perl scripts.

Replies are listed 'Best First'.
Re: how to use conditional loops when using while loop
by ikegami (Patriarch) on Jul 28, 2005 at 15:56 UTC

    In the second snippet, $cnt++ is only executed when $cnt is not 4. Learn how to indent properly to avoid these errors.

    The arguments for rand are off by one. Random returns a number from 0 (inclusively) to arg (exclusively). You need 48 and 256. Better yet, use hex (0x30 and 0x100) since it's more meaningful here.

    I have no idea what if ($rand < 5) { $rand += 2; ... is all about. If you want to avoid '00' and '01', use

    rand(0x100 - 2) + 2</p> <p>Why not just use</p> <c> sub mac { return join ':', '00:96:14', sprintf('%02X', rand(0x30)), sprintf('%02X', rand(0x100)), sprintf('%02X', rand(0x100)); }

    or

    sub mac { return sprintf('00:96:14:%02X:%02X:%02X', rand(0x30), rand(0x100), rand(0x100), ); }

    A loop isn't appropriate here due to the lack of commonality between the octects

Re: how to use conditional loops when using while loop
by Random_Walk (Prior) on Jul 28, 2005 at 16:16 UTC

    I would use pack and unpack here, a MAC is a hex number.

    pack the start and end of range of MACs. Unpack again to get integers.
    Pick random numbers in the range between the two integers then pack an unpack to hex again.

    Cheers,
    R.

    Pereant, qui ante nos nostra dixerunt!
      Careful. MAC addresses require 48 bits, but many perls use 32 bits ints. Also, rand seems to only return numbers between 0 and 65536 for me. (The docs imply this can be changed when compiling perl.)

        Yeh I have been fighting with the 48/64 bit issue, I added a couple of Fs to pad the hex out to 64 bytes but I am still having problems that I guess are to do with endianisms.

        I must admit that is the reason I did not post code yet and now I have to run to the airport. Perhaps I'll get a chance to play later.

        The rand issue is solved by rand*rand or similar, I guess the requirement is not for crypto quality randomness here.

        Must fly, Cheers,
        R.

        Pereant, qui ante nos nostra dixerunt!
Re: how to use conditional loops when using while loop
by Forsaken (Friar) on Jul 28, 2005 at 16:37 UTC
Re: how to use conditional loops when using while loop
by wink (Scribe) on Jul 29, 2005 at 02:46 UTC
    I know I'm a little late on this thread, seeing as how there are already viable answers, but I have an alternate solution. Instead of worrying about generating each byte individually, why not generate the number as a whole:
    my $pre = '00:96:14:'; my ($minhex,$maxhex) = qw(0 2fffff); # Randomize a number between $minhex and $maxhex, inclusive # Accounts for a min value other than 0 my $hostiddec = int(rand(hex($maxhex) - hex($minhex) + 1)) + hex($minh +ex); # Convert to hex again $hostidhex = unpack("H*",pack("i",$hostiddec)); # Format, getting rid of leading 0's and adding colons $hostidhex =~ s/^0*(\w{2})(\w{2})(\w{2})$/$1:$2:$3/; # Output (return here rather than print if you're in a subroutine) print $pre . $hostidhex;
    I don't know why it seems clunky to me to generate the bytes individually. For instance, I find it easier and more efficient to int(rand(16)) and convert it to binary than int(rand(2)) 4 times and concatenate it. Seems to me like your output would be more varied, but that's probably just me. Mathematically it seems like it would be the same (16 possibilities for the former, 2*2*2*2=16 possibilities for the latter), but I just feel like the former offers more variety. When you only have 2 choices, even when repeating it 4 times, I just see repetition of the final pattern being more common. I'll stop babbling now.