Building better scripts Part IV

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 !