Working with IPv4 Addresses in PowerShell

Working with IPv4 Addresses in PowerShell

System administrators work with IP version 4 (IPv4) addresses on a regular basis. If you’ve ever had to work with IPv4 addresses in scripts, you’re already aware of the complications: IPv4 addresses are 32-bit numbers, not strings. In order to calculate a network ID, for example, you need to use the bitwise AND operator with the IPv4 address and a subnet mask. These calculations can be challenging to get right in shell script (batch) and VBScript or JScript due to string parsing and byte order, but fortunately things are much simpler in PowerShell, because we can use the .NET IPAddress class.

Using the IPAddress Class

A class is simply a template for an object. The full name of the .NET IPAddress class is System.Net.IPAddress. There are two main ways to create an IPAddress object. The first is to use New-Object and specify the full name of the class. For example:

$ip = New-Object System.Net.IPAddress(0x1521A8C0)

This command will create an IPAddress object containing the IPv4 address 192.168.33.21. You must specify the address in reverse byte order (I’ll explain this later). I wrote the IP address in hexadecimal (base 16) format, which uses the 0x prefix, to make it easier to see the four octets of the address: 0x15 = 21, 0x21 = 33, 0xA8 = 168, and 0xC0 = 192. We could also write the address in its decimal format (0x1521A8C0 = 354527424 in decimal).

There is an easier way to work with IP addresses in PowerShell, however: We can use the [IPAddress] type accelerator. (We could also specify the full class name in brackets, [System.Net.IPAddress], but PowerShell lets us shorten it to just [IPAddress].) The advantage of using the type accelerator rather than New-Object, other than being shorter, is that we can specify the IPv4 address as a “dotted-quad” string rather than a number. In other words, the following two commands are functionally equivalent:

$ip = New-Object System.Net.IPAddress(0x1521A8C0)
$ip = [IPAddress] "192.168.33.21"

Figure 1 illustrates some PowerShell commands that create an IPAddress object and display its properties.


Figure 1 - Using the .NET IPAddress class

There are four separate commands in Figure 1. The first command creates an IPAddress object and assigns it to the $ip variable, and the second command outputs the object. The third command outputs the object’s Address property as a hexadecimal string using the -f operator, and the fourth command outputs the object’s IPAddressToString property.

Calculating a Network ID

Now that we know how to use the IPAddress class, we can easily calculate a network ID for an IPv4 address and a subnet mask. As you may know, we can calculate a network ID by combining an IPv4 address and its subnet mask using the -band (bitwise AND) operator, as shown in Figure 2.



Figure 2 - Calculating a network ID using the -band operator

The first and second commands in Figure 2 create IPAddress objects for both the IPv4 address (192.168.33.21) and the subnet mask (255.255.255.252). The third command creates a third IPAddress object containing the network ID by combining the Address properties (i.e., the numeric values of the IPv4 addresses) using the -band operator. Finally, the fourth command displays the third IPAddress object containing the network ID (192.168.33.20).

Calculating a Computer’s Network ID

Listing 1 is a practical example: It contains a sample script that shows how to calculate the current computer’s network ID.

$params = @{
  "ComputerName" = "."
  "Class" = "Win32_NetworkAdapterConfiguration"
  "Filter" = "IPEnabled=TRUE"
}
$netConfigs = Get-WMIObject @params
foreach ( $netConfig in $netConfigs ) {
  for ( $i = 0; $i -lt $netConfig.IPAddress.Count; $i++ ) {
    if ( $netConfig.IPAddress[$i] -match '(\d{1,3}\.){3}\d{1,3}' ) {
  $ipString = $netConfig.IPAddress[$i]
  $ip = [IPAddress] $ipString
  $maskString = $netConfig.IPSubnet[$i]
  $mask = [IPAddress] $maskString
  $netID = [IPAddress] ($ip.Address -band $mask.Address)
  "IP address: {0}" -f $ip.IPAddressToString
  "Subnet mask: {0}" -f $mask.IPAddressToString
  "Network ID: {0}" -f $netID.IPAddressToString
    }
  }
}

Listing 1 - Calculating the current computer’s network ID

The script works by using the Get-WmiObject cmdlet to get all Win32_NetworkAdapterConfiguration objects that are IP enabled and uses foreach to loop through them. Next, the script uses a for loop to iterate the IPAddress property (this is a string array that is separate from the .NET IPAddress class) of the Win32_NetworkAdapterConfiguration object. It uses the -match operator and a regular expression to determine if the address is an IPv4 address. If the address is an IPv4 address, the script creates .NET IPAddress objects for the IPv4 address, the mask, and the network ID, then outputs them.

Calculating CIDR Masks

CIDR (classless inter-domain routing) is another common way of expressing a subnet mask value in a concise manner. CIDR format specifies a forward slash (/) followed by the number of bits in the subnet mask. For example, the subnet mask 255.255.252.0 is equivalent to /22. Figure 3 shows why: There are 22 bits set (i.e., the sum of ones in all four octets) in the mask.


Figure 3 - Number of bits set in a subnet mask

Unfortunately, the IPAddress class doesn’t have a direct way of converting the CIDR “number of bits” format into a dotted-quad subnet mask string or vice-versa. To remedy these shortcomings, I wrote the PowerShell functions in Listing 2.

function ConvertTo-IPv4MaskString {
  <#
  .SYNOPSIS
  Converts a number of bits (0-32) to an IPv4 network mask string (e.g., "255.255.255.0").

  .DESCRIPTION
  Converts a number of bits (0-32) to an IPv4 network mask string (e.g., "255.255.255.0").

  .PARAMETER MaskBits
  Specifies the number of bits in the mask.
  #>
  param(
    [parameter(Mandatory=$true)]
    [ValidateRange(0,32)]
    [Int] $MaskBits
  )
  $mask = ([Math]::Pow(2, $MaskBits) - 1) * [Math]::Pow(2, (32 - $MaskBits))
  $bytes = [BitConverter]::GetBytes([UInt32] $mask)
  (($bytes.Count - 1)..0 | ForEach-Object { [String] $bytes[$_] }) -join "."
}

function Test-IPv4MaskString {
  <#
  .SYNOPSIS
  Tests whether an IPv4 network mask string (e.g., "255.255.255.0") is valid.

  .DESCRIPTION
  Tests whether an IPv4 network mask string (e.g., "255.255.255.0") is valid.

  .PARAMETER MaskString
  Specifies the IPv4 network mask string (e.g., "255.255.255.0").
  #>
  param(
    [parameter(Mandatory=$true)]
    [String] $MaskString
  )
  $validBytes = '0|128|192|224|240|248|252|254|255'
  $maskPattern = ('^((({0})\.0\.0\.0)|'      -f $validBytes) +
         ('(255\.({0})\.0\.0)|'      -f $validBytes) +
         ('(255\.255\.({0})\.0)|'    -f $validBytes) +
         ('(255\.255\.255\.({0})))$' -f $validBytes)
  $MaskString -match $maskPattern
}

function ConvertTo-IPv4MaskBits {
  <#
  .SYNOPSIS
  Returns the number of bits (0-32) in a network mask string (e.g., "255.255.255.0").

  .DESCRIPTION
  Returns the number of bits (0-32) in a network mask string (e.g., "255.255.255.0").

  .PARAMETER MaskString
  Specifies the IPv4 network mask string (e.g., "255.255.255.0").
  #>
  param(
    [parameter(Mandatory=$true)]
    [ValidateScript({Test-IPv4MaskString $_})]
    [String] $MaskString
  )
  $mask = ([IPAddress] $MaskString).Address
  for ( $bitCount = 0; $mask -ne 0; $bitCount++ ) {
    $mask = $mask -band ($mask - 1)
  }
  $bitCount
}

Listing 2 - IPv4CIDRFunctions.ps1

The ConvertTo-IPv4MaskString Function

The ConvertTo-IPv4MaskString function uses the following formula to calculate the mask value as a number:

((2n)-1) -shl (32-n)

Where n is the number of bits. Since PowerShell version 2 doesn’t have the -shl operator, the function uses the following formula instead:

((2n)-1) * (2(32-n))

The function then converts the mask value into four bytes using the BitConverter class. Finally, the function retrieves the bytes in the value in reverse order separated by dots. We have to reverse the byte order because we write IPv4 addresses most-significant byte first (also called network order or big endian), but the CPU stores the bytes least-significant byte first (also called host order or little endian).

For example: Suppose we want to find the subnet mask string that corresponds to 26 bits. We calculate as follows:

(226-1) * (2(32-26)) = (0x4000000-1) * (26) = 0x03FFFFFF * 0x40 = 0xFFFFFFC0

Reading the hexadecimal values as two-digit pairs, we see FF FF FF C0, or 255 255 255 192.

Figure 4 shows the ConvertTo-IPv4MaskString function in action. The first command dot-sources the functions from the CIDRFunctions.ps1 script file to make them available in the PowerShell session, and the second command lists all subnet mask strings from 8 bits through 29 bits.


 Figure 4 - Listing of all possible subnet mask values

The Test-IPv4MaskString Function

The Test-IPv4MaskString function uses a regular expression to determine if the dotted-quad string is a valid subnet mask value. The function returns $true if the string matches a valid subnet mask string, or $false otherwise. The ConvertTo-IPv4MaskBits function requires this function.

The ConvertTo-IPv4MaskBits Function

The ConvertTo-IPv4MaskBits gets the numeric value of the subnet mask string by creating an IPAddress object and retrieving its Address property. It then uses Brian Kernighan’s algorithm to count the number of bits set in the value and returns the number of bits. For example, the command

ConvertTo-IPv4MaskBits "255.255.254.0"

outputs the number 23.

Easily Handle IPv4 Addresses

PowerShell makes it easy to work with IPv4 addresses using the IPAddress class. In addition, the ConvertTo-IPv4MaskString and ConvertTo-IPv4MaskBits functions convert subnet masks back and forth between CIDR and dotted-quad string format. Add these functions to your toolkit and you can handle any IPv4 addressing tasks with ease.

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish