While if you look, there are tools to calculate a IP subnet/address range, they all seem to rely on string splitting and converting the address to binary manually. Which I think is bulky, hard to maintain, and more importantly, not that elegant. So pulling to mind my Cisco CCNA classes where my instructor would drill into us the binary ands and ors of IP and subnet addressing, I got down to work.
All examples are in Powershell, however they could be translated to C#, any .NET language or even some other language quite easily.
Examples
Including System.net. This may not be necessary, but I’ve mucked with my Powershell settings way too much to know about a vanilla install.
#Powershell v1.0
[reflection.assembly]::LoadWithPartialName("system.net");
#Powershell v2.0
Add-Type -AssemblyName System.Net;
A common plight for me is finding out if two different IP addresses are on the same local network.
$router = [net.ipaddress]::Parse("192.168.1.254");
$subnet = [net.ipaddress]::Parse("255.255.255.0");
$IP_one = [net.ipaddress]::Parse("192.168.1.23");
$IP_two = [net.ipaddress]::Parse("192.168.1.55");
$IP_three = [net.ipaddress]::Parse("33.21.9.1");
#get the network ID
#in this case it's 192.168.1.0
$network = $router.address -band $subnet.Address
($network -band $IP_one.Address) -eq $network #True
($network -band $IP_two.Address) -eq $network #True
($network -band $IP_three.Address) -eq $network #False
Of course there are also times where I need to know how many addresses there are in the network or what they are.
$seed = [net.ipaddress]::Parse("192.168.23.45");
$subnet = [net.ipaddress]::Parse("255.255.252.0");
$begin = ($seed.address -band $subnet.address)
$end = (([net.ipaddress]::Broadcast.Address -bxor $subnet.Address) -bor $seed.address)
#It's the same for both start and ending IP's
#We convert from a 64 to 32 bit integer (8 to 4 bytes) and then reverse it so we can do addition/subtraction math to later on.
$start_bytes = new-object byte[] 4;
[array]::copy([bitconverter]::getBytes(($seed.address -band $subnet.address)), $start_bytes, 4);
[array]::Reverse($start_bytes);
$start_int = [bitconverter]::ToInt32($start_bytes,0);
$end_bytes = new-object byte[] 4;
[array]::copy([bitconverter]::getBytes((([net.ipaddress]::Broadcast.Address -bxor $subnet.Address) -bor $seed.address)), $end_bytes, 4);
[array]::Reverse($end_bytes);
$end_int = [bitconverter]::ToInt32($end_bytes,0);
#Entire range including the network ID and Broadcast addresses
[math]::Abs($start_int - $end_int) + 1
#Just the number of usable addresses
[math]::Abs($start_int - $end_int) - 1
#Address list!
for($i=0; $i -le [math]::abs($start_int - $end_int); $i++) {
$tba = [bitconverter]::getbytes($i + $start_int);
[array]::Reverse($tba)
(new-object net.ipaddress ([bitconverter]::ToInt64($tba + [byte[]]@(0,0,0,0),0))).tostring();
}
Conclusion
As you can see, using bitwise operations greatly reduces the complexity required to calculate network addresses. By simplifying the process, it makes it much easier to maintain the code and build on it later!