Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

calculate subnet help

by Anonymous Monk
on Aug 26, 2011 at 14:15 UTC ( [id://922645]=perlquestion: print w/replies, xml ) Need Help??

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

I'm hoping some golfers can help me with this. I'm trying to calculate a subnet based on an IP and mask.

Calculation of subnet:

1)Find first non-255 or non-0 octet of mask.

2)Copy all octects to the left of the first non 255/0 octect of the mask to the subnet.

3) Write a 0 for remaining octects AFTER the first non 255/0 octect

4) Subtract 256 minus the octect with the first non 255/0. Then find out how many times that sum can go into that same octect of the IP address octect. Multiply the sum and number of times it can go into the IP. (this will be the result for the first non 255/0 octect).

Example:

IP: 192.168.190.128

Mask: 255.255.255.224

For the subnet, you copy over the first 3 octects of the mask because they are 255 or 0.

Subnet: 255.255.255 To find the first special octect (which is the 4th octect in this example with 224), we do: 256 - 224 = 32.

From 32 we find out how many times it can go into the same IP octect without going over the ip (32 goes into 128 evenly this time 4 times. So the result is 128).

Subnet final: 255.255.255.128

If the last IP octect was 127 in this example the math would be: 256 - 224 = 32. 32 goes into 127 3 times. 32 X 3 = 96.

Final subnet: 255.255.255.96

--------------------------------

That covered I know there's a simple way of doing this. I made it more difficult than it had to and it works for the most part but I want to make it cleaner and shorter.

Can you help me?

Note: I'm asking the IP and Mask at the command line then converting them into an array split on periods.

Ie: $ip = <STDIN>; @ip= split(/\./, $ip);

my $oct_cnt = -1; for (1 .. 4) { $oct_cnt++; if ($mask[$oct_cnt] =~ m/255/ || $mask[$oct_cnt] =~ m/0/) { #print "skipping, $oct_cnt is 255 or 0"; push(@sub, $mask[$oct_cnt]); } else { #print "found non 255 or zero at $oct_cnt with $mask[$oct_cnt]" +; my $sum = 256 - $mask[$oct_cnt]; my $rem = $ip[$oct_cnt] / $sum; $rem =~ m/([0-9]+)\./; my $clean_rem = $1; push(@sub, $clean_rem * $sum); # determine what octet we are, set remainders to 0 if($oct_cnt+1 < 4) { for ($oct_cnt+1 .. 4) { push(@sub, $ip[$oct_cnt+1]); } } last if;

Replies are listed 'Best First'.
Re: calculate subnet help
by sulfericacid (Deacon) on Aug 26, 2011 at 15:39 UTC
    Not the cleanest but it works. BTW you can't also separate on 0, all you care about is the unique octect that does not == 255. Your logic was a little off.

    My route: I dumped the entire IP address array inside the subnet array for simplicity. Then I iterated over the subnet array and did the calculations and 0 assignments.
    sub getmask { push(@sub, @ip); my $cnt = -1; foreach my $line (@mask) { $cnt++; if ($line != 255) { if ($flag > 0) { $sub[$cnt] = 0; } my $magic = 256 - $line; my $rem = $ip[$cnt] / $magic; $rem =~ s/\.(.+)//; $sub[$cnt] = $magic * $rem; } } print join(".", @sub);
Re: calculate subnet help
by fs (Monk) on Aug 27, 2011 at 17:09 UTC

    That looks a whole lot harder than it has to be. For starters, I'm assuming that there's a reason you want to do this yourself, rather than just using one of the CPAN libraries, like NetAddr::IP.

    If that's the case, the trick is just remembering that in the end, an IPv4 address or mask is really just a 32 bit int. Just use a little bit of shifting and splitting to roll your own inet_aton and inet_ntoa if you don't want to use the ones in Socket

    sub my_inet_aton { my ($string) = @_; my ($a, $b, $c, $d) = split(/\./, $string); return ( ($a << 24) + ($b << 16) + ($c << 8) + ($d) ); } sub my_inet_ntoa { my ($ip) = @_; return ( (($ip & 0xFF000000) >> 24) . '.' . (($ip & 0xFF0000) >> 16) . '.' . (($ip & 0xFF00) >> 8) . '.' . ($ip & 0xFF) ); }

    Then just use a bitwise AND between the mask and IP address to extract the subnet portion.

    my $ipstring = '192.168.190.128'; my $maskstring = '255.255.255.0'; my $ip = my_inet_aton($ipstring); my $mask = my_inet_aton($maskstring); my $subnet = $ip & $mask; print "Subnet portion of ", $ipstring, "/", $maskstring, " is ", my_inet_ntoa($subnet), "\n";

    The basic logic is the same for IPv6 addresses. However, a) parsing them is a little tricker with things like :: compact form, and b) they're 128 bits, so not all systems will be able to directly do bit masks on them. If there's any chance you need to handle v6 addresses, you're much better off using one of the pre-existing CPAN modules that will handles those cases for you automatically.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://922645]
Approved by davies
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (6)
As of 2024-03-28 09:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found