#!/usr/bin/env perl # netmask --- compute network masks, broadcasts, and ranges # Author: Noah Friedman # Created: 2005-12-08 # Public domain # $Id: netmask,v 1.1 2006/04/25 04:17:59 friedman Exp $ # Commentary: # Todo: finish writing this. Totally broken at the moment, checking in # because I haven't had time for 4 months to work on it. # Todo: add ipv6 support. # Code: $^W = 1; # enable warnings use strict; use Socket; (my $progname = $0) =~ s=.*/==; my $ipv4_netmask = 0xffffffff ; sub fatal { my $fmt = shift; printf STDERR "%s: error: $fmt\n", $progname, @_; exit (1); } sub normalize_ip { local $_ = shift; return unpack ("N", inet_aton ($_)) if (/^\d+\.\d+\.\d+\.\d+$/); if (/\./) { return 0; } elsif (/^[a-f]|^0?x/) { s/^0?x//; $_ = hex ($_) } elsif (/^0/) { s/^0//; $_ = oct ($_) } return $_; } sub parse_ip { local $_ = shift; my @n = map { normalize_ip ($_) } split (m|/|, $_); # TODO: If no mask specified, assume A/B/C class $n[1] = 24 unless defined $n[1]; if ($n[1] > 32) { # Check validity of netmask if not a /cidr value < 32. # In practical terms what this means is that there should be no unset # bits to the left of the least significant set bit (e.g. 11111100 # is ok but 11101100 is not). # # Claim: x & (x-1) == 0 iff x == 2^n . # If x == 2^n, only nth bit in x is set. # Subtracting 1 flips all bits via a borrow; the logical AND is zero. # If x != 2^n, x-1 will flip all bits up to and including first 1, # but will not negate the entire value and an AND will not produce zero. # # $n[1] should be in the form y=~[(2^n)-1], so we flip the bits # first, then AND the result with y+1, so that if y+1 is a power of # two then the result should be zero. my $x = $ipv4_netmask & ~$n[1]; if (($x & ($x + 1)) != 0) { fatal ("%s (0x%08x) is not a valid netmask", inet_ntoa (pack ("N", $n[1])), $n[1]); } printf "\$x = 0x%08x\n", $x; $n[1] = $x; } return @n; } sub main { unless (@_) { printf STDERR "Usage: %s \n", $progname; exit (1); } for my $arg (@_) { my ($addr, $net) = parse_ip ($arg); my $netmask = $ipv4_netmask & ($ipv4_netmask << (32 - $net)); my $hostmask = $ipv4_netmask & ~$netmask; my $subnet = $addr & $netmask; my $bcast = $addr | $hostmask; map { printf "%-9s = %-15s (0x%08x)\n", $_->[0], inet_ntoa (pack ("N", $_->[1])), $_->[1]; } (["address", $addr], ["subnet", $subnet], ["netmask", $netmask], ["broadcast", $bcast]); } } main (@ARGV); # local variables: # mode: perl # end: # eof