My focus on networking in the IT industry has shown to me that a lot of (but certainly not all) network engineers lack
scripting and automation skills, especially when it comes to manipulation and documentation of IP addresses, as well
as their underlying representation in memory. I’ve observed Excel spreadsheets where the IPs were simply represented as
text. Or ones where an IPv4 address was simply divided into a separate column for each octet and otherwise treated
as set of unrelated integers mashed across four separate columns. Even if it’s inside a script, typically using a
dynamically typed language, they are most often just stored as a string.
This, I believe, is entirely the wrong approach to store them. In memory, they are stored as an array of bytes. So
why shouldn’t they just be handled as byte arrays in your script?
The one important piece of information, if you plan to do this, is the byte order in memory (on most modern
architectures) is little endian. However, network byte order (aka big endian) is how bytes are ordered for use in
various network protocols. Let’s look at the difference, given the IP 10.20.30.75
:
Little endian for 10.20.30.75
:
01001011.00011110.00010100.00001010
Network byte order for 10.20.30.75
:
00001010.00010100.00011110.01001011
Notice the difference for each of these byte-ordering schemes is the reverse of the other.
Knowing this, it’s time to see if we can store an IP address in a reasonable way inside a scripting language. For
this, I will be using PowerShell, which provides great support via the System.Net.IPAddress
class. A simple
way to instantiate one is as follows:
$ip = [IPAddress] "10.20.30.75"
However, in order to display the System.Net.IPAddress
as a string, we can use the ToString()
method:
PS C:\windows\system32> $ip = [IPAddress] "10.20.30.75"
PS C:\windows\system32> $ip.ToString()
10.20.30.75
Interestingly, if we try to get the value of Address
property of a System.Net.IPAddress
, we find it’s
returning an integer:
PS C:\windows\system32> $ip.Address
1260262410
PS C:\windows\system32> $ip.Address.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int64 System.ValueType
An interesting point is that if we want to cast an integer to a System.Net.IPAddress
, it needs to be a 32-bit
signed integer (UInt32
) instead of a 64-bit signed integer (Int64
).
So, how is this useful? If we combine the IP address with a subnet mask, we can calculate a whole bunch of useful
addresses. First, the subnet mask will be stored as another System.Net.IPAddress
:
PS C:\windows\system32> $subnetMask = [IPAddress] "255.255.255.224"
Next, we can calculate the network address by performing a binary AND
operation on the IP and the subnet mask.
PS C:\windows\system32> [IPAddress]$networkAddress = $ip.Address -band $subnetMask.Address
PS C:\windows\system32> $networkAddress.ToString()
10.20.30.64
Address |
Binary Representation |
IP |
00001010.00010100.00011110.01001011 |
Subnet Mask |
11111111.11111111.11111111.11100000 |
Network Address |
00001010.00010100.00011110.01000000 |
Another useful address to know would be the broadcast address of the network we’re describing. This can be
calculated by first calculating a wildcard address by performing a binary NOT
of the subnet mask and then
performing a binary OR
operation against the network address and wildcard address:
PS C:\windows\system32> [IPAddress]$wildCardAddress = -bnot [UInt32]$subnetMask.Address
PS C:\windows\system32> [IPAddress]$broadcastAddress = $networkAddress.Address -bor $wildCardAddress.Address
PS C:\windows\system32> $wildCardAddress.ToString()
0.0.0.31
PS C:\windows\system32> $broadcastAddress.ToString()
10.20.30.95
Address |
Binary Representation |
Subnet Mask |
11111111.11111111.11111111.11100000 |
Wildcard Address |
00000000.00000000.00000000.00011111 |
Network Address |
00001010.00010100.00011110.01000000 |
Broadcast Address |
00001010.00010100.00011110.01011111 |
Finally, as you know, a gateway can be any valid host address on the network. Usually it’s one up from the network
address or one down from the broadcast address. Here’s two quick options to do either:
PS C:\windows\system32> [IPAddress]$gatewayAddressOptionOne = $networkAddress.Address + ([IPAddress] "0.0.0.1").Address
PS C:\windows\system32> [IPAddress]$gatewayAddressOptionTwo = $broadcastAddress.Address - ([IPAddress] "0.0.0.1").Address
PS C:\windows\system32> $gatewayAddressOptionOne.ToString()
10.20.30.65
PS C:\windows\system32> $gatewayAddressOptionTwo.ToString()
10.20.30.94
Finally, I will leave you some functions for easily display a System.Net.IPAddress
object as string of binary
digits nicely formatted with a delimiter between each octet (as your have already seen in this article):
function ByteToBinaryString {
param (
[Byte] $byte
)
return [Convert]::toString($byte,2)
}
function IpAddresstoBinary {
param (
[IPAddress] $ipAddress
)
[Byte[]] $bytes = $ipAddress.GetAddressBytes()
# Convert from little endian to network byte order...
[Array]::Reverse($bytes)
[String] $return = $null
[Bool] $firstPass = $true
ForEach ($byte in $bytes) {
$binaryAsString = ByteToBinaryString($byte)
For ($i = $binaryAsString.length; $i -lt 8; $i++) {
$binaryAsString = '0' + $binaryAsString
}
if (!$firstpass) {
$return = '.' + $return
}
$return = $binaryAsString + $return
$firstPass = $false
}
return $return
}
comments