I expect there are plenty of tools out there to do this but I had to resort to paper and pencil after someone unplugged the wrong wires in a datacentre ... not me I hasten to add! A script to do the job was called for, especially as I worked in a closed environment where getting any bought-in application approved was a labour of Hercules. This is a tidied up version of the original which might be useful to someone out there.
#!/usr/bin/perl
#
# Use strictures and warnings. Use Getopt::Long for options parsing
# and Socket for manipulation of IP quads.
#
use strict;
use warnings;
use Getopt::Long;
use Socket;
# Declare option scalars.
#
my $address;
my $netmask;
my $hexmask;
my $help;
# Parse command line options, die if bad.
#
my $rhOpts = {
address => \ $address,
netmask => \ $netmask,
hexmask => \ $hexmask,
help => \ $help,
};
my $getoptRes = GetOptions(
$rhOpts, q{address=s}, q{netmask=s}, q{hexmask=s}, q{help}
);
die qq{\n} unless $getoptRes;
# Show help text then terminate if requested.
#
die <<__EOT__ if $help;
User Commands tellNe
+twork
Name: tellNetwork - given an IP address and netmask, outputs
+which
network the address belongs to
Synopsis: tellNetwork --address nnn.nnn.nnn.nnn/nn
tellNetwork --address nnn.nnn.nnn.nnn --hexmask xxxxxxx
+x
tellNetwork --address nnn.nnn.nnn.nnn --netmask nnn.nnn
+.nnn.nnn
tellNetwork --help
Description: Script that takes an IPv4 address and a netmask, either
+ in
decimal quad or hexadecimal form, and calculates the
network number and low/high addresses. The script does
+not
do any validation of the netmask in relation to the IPv
+4
class of the supplied address, e.g. a /10 netmask on a
class C address would not be flagged as an error
Options: --address IP address in decimal quad form with the
possibility of slash notation netmask app
+ended
--netmask Netmask in decimal quad form
--hexmask Netmask in hexadecimal form
--help display this help text
Files: /path/to/bin/tellNetwork
this executable script file
Dependencies: No non-core dependencies
__EOT__
# Create a hash of valid netmask values keyed by slash notation mask
# width.
#
my %validNetmasks;
my $value = 0xfffffffc;
for my $maskLen ( reverse 8 .. 30 )
{
my $netmask = pack q{N}, $value;
$validNetmasks{ $maskLen } = $value;
$validNetmasks{ sprintf q{%x}, $value } ++;
$validNetmasks{ sprintf q{%X}, $value } ++;
$validNetmasks{ inet_ntoa( $netmask ) } ++;
$value = do {
no warnings qw{ portable };
( $value <<= 1 ) % 0x100000000;
};
}
# Check that we have the mandatory options, --address that includes a
# slash notation netmask on its own or --address not including netmask
# and either one or the other but not both of --netmask and --hexmask.
#
my $hasSlash;
if ( not defined $address )
{
die
qq{tellNetwork: Incorrect options: must have --address and, if\
+n},
qq{ address does not include an appended slash\n},
qq{ notation netmask, either --netmask or --hexmask
+\n};
}
elsif ( $address =~ m{^(?:\d{1,3}\.){3}\d{1,3}/\d\d?$} )
{
$hasSlash ++;
die
qq{tellNetwork: Incorrect options: --netmask or --hexmask shoul
+d\n},
qq{ not be supplied if address includes slash notat
+ion\n},
qq{ netmask\n}
if $netmask or $hexmask;
}
else
{
die
qq{tellNetwork: Incorrect options: if --address without a slash
+\n},
qq{ notation netmask is supplied then one of either
+\n},
qq{ --netmask or --hexmask must also be present\n}
unless $netmask xor $hexmask;
}
# Check that supplied options are valid.
#
my $nBitsMask;
my $binAddr;
do {
( $address, $nBitsMask ) = split m{/}, $address;
die qq{Invalid slash notation netmask\n}
unless $validNetmasks{ $nBitsMask };
} if $hasSlash;
die qq{Invalid IP address\n} unless $binAddr = validQuad( $address );
die qq{Invalid decimal quad netmask - $netmask\n}
if $netmask and not $validNetmasks{ $netmask };
die qq{Invalid hexadecimal netmask - $hexmask\n}
if $hexmask and not $validNetmasks{ $hexmask };
# Convert netmask to binary numeric values with either the
# Socket module's inet_aton() if decimal quads or pack with the
# 'H*' template if a hexadecimal string.
#
my $binMask;
if ( $hasSlash )
{
$binMask = pack q{N}, $validNetmasks{ $nBitsMask };
}
elsif ( $netmask )
{
$binMask = inet_aton( $netmask );
}
else
{
$binMask = pack q{H*}, $hexmask;
}
# Calculate the network number by binary AND'ing the address and
# netmask. Get human-readable nnn.nnn.nnn.nnn/nn form for output.
#
my $network = $binAddr & $binMask;
my $readable
= inet_ntoa( $network )
. q{/}
. ( unpack( q{B*}, $binMask ) =~ tr{1}{} );
# Calculate the lowest IP address in the range by binary or'ing the
# network with the network xor'ed with a value of 0x1.
#
my $lowMask = $network ^ pack q{H*}, q{00000001};
my $lowAddr = $network | $lowMask;
# Calculate the highest IP address in the range by binary OR'ing the
# network with the netmask XOR'ed with a value of 0xffffffff.
#
my $highMask = $binMask ^ pack q{H*}, q{ffffffff};
my $highAddr = $network | $highMask;
# Output nicely formatted results.
#
print qq{\n};
my $lineFmt = qq{%-13s: %s %s %s %s : %s\n};
printf
$lineFmt x 5,
q{IP address},
unpack( q{(B8)*}, $binAddr ), $address,
q{Netmask},
unpack( q{(B8)*}, $binMask ), inet_ntoa( $binMask ),
q{Network},
unpack( q{(B8)*}, $network ), $readable,
q{Low address},
unpack( q{(B8)*}, $lowAddr ), inet_ntoa( $lowAddr ),
q{High address},
unpack( q{(B8)*}, $highAddr ), inet_ntoa( $highAddr );
print qq{\n};
# Subroutine to check that string is a valid decimal IP quad.
#
# ---------
sub validQuad
# ---------
{
my $quad = shift;
my $packed = inet_aton( $quad );
return
defined $packed
? $packed
: 0;
}
Some example usage.
I hope this is of interest.