IPv4 subnet math with PowerShell
Written to complement the VbScript version of this post. This collection of functions handles subnet math in PowerShell.
Download all the functions and examples at once: Net-SubnetMath.ps1
Convert an IP to binary
This function uses System.Convert to change each octet of an IP address into it’s binary form. PadLeft is used to add leading zero’s if required.
Function ConvertTo-BinaryIP( [String]$IP ) {
$IPAddress = [Net.IPAddress]::Parse($IP)
Return [String]::Join('.',
$( $IPAddress.GetAddressBytes() | %{
[Convert]::ToString($_, 2).PadLeft(8, '0') } ))
}
Convert an IP to a 32-bit decimal
Allows conversion of an IP Address to an unsigned 32-bit integer.
Function ConvertTo-DecimalIP( [String]$IP ) {
$IPAddress = [Net.IPAddress]::Parse($IP)
$i = 3
$IPAddress.GetAddressBytes() | %{
$DecimalIP += $_ * [Math]::Pow(256, $i); $i-- }
Return [UInt32]$DecimalIP
}
Convert a decimal number or binary IP to a dotted IP
Used to switch a decimal or binary IP back to the more common dotted decimal format. The function uses a simple pair of regular expressions to determine which format is presented.
Function ConvertTo-DottedDecimalIP( [String]$IP ) {
Switch -RegEx ($IP) {
"([01]{8}\.){3}[01]{8}" {
Return [String]::Join('.', $( $IP.Split('.') | %{
[Convert]::ToInt32($_, 2) } ))
}
"\d" {
$IP = [UInt32]$IP
$DottedIP = $( For ($i = 3; $i -gt -1; $i--) {
$Remainder = $IP % [Math]::Pow(256, $i)
($IP - $Remainder) / [Math]::Pow(256, $i)
$IP = $Remainder
} )
Return [String]::Join('.', $DottedIP)
}
default {
Write-Error "Cannot convert this format"
}
}
}
Convert a subnet mask to a mask length
Occasionally it is desirable to calculate the subnet mask bit length. This can be done using the following.
Function ConvertTo-MaskLength( [String]$Mask ) {
$IPMask = [Net.IPAddress]::Parse($Mask)
$Bits = "$( $IPMask.GetAddressBytes() | %{
[Convert]::ToString($_, 2) } )" -Replace "[\s0]"
Return $Bits.Length
}
Convert a mask length to a subnet mask
To a dotted decimal IP via binary and an unsigned 32-bit integer.
Function ConvertTo-Mask( [Byte]$MaskLength ) {
Return ConvertTo-DottedDecimalIP ([Convert]::ToUInt32(
$(("1" * $MaskLength).PadRight(32, "0")), 2))
}
Calculate the subnet network address
The functions above can be used with along with a bitwise AND operation against an IP address and subnet mask to calculate the network address.
Note that this function includes calls to both ConvertTo-DottedDecimalIP and ConvertTo-DecimalIP.
Function Get-NetworkAddress( [String]$IP, [String]$Mask ) {
Return ConvertTo-DottedDecimalIP $(
(ConvertTo-DecimalIP $IP) -BAnd
(ConvertTo-DecimalIP $Mask))
}
Calculate the subnet broadcast address
The function is right at the bottom, the explanation is, of course, entirely optional.
Getting to the Broadcast Address is a bit more complicated than the Network Address. A Bitwise Or is executed against an Inverted Subnet Mask. For example, the Inverted form of 255.255.255.0 is 0.0.0.255.
Inverting the decimal value of the subnet mask can be performed using BNot, the Complement Operator (see Get-Help About_Comparison_Operators).
Unfortunately BNot only returns a signed 64-bit integer (in the range -9223372036854775808 to 9223372036854775807).
PS C:\> $Value = ConvertTo-DecimalIP 255.255.255.0; $Value 4294967040 PS C:\> $Value.GetType() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True UInt32 System.ValueType PS C:\> $Inverted = -BNot $Value; $Inverted -4294967041 PS C:\> $Inverted.GetType() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Int64 System.ValueType PS C:\> # Convert to Binary (Base 2) PS C:\> [Convert]::ToString($Inverted, 2) 1111111111111111111111111111111100000000000000000000000011111111
The first 32 bits have are 1 (feel free to count). This happens because of the implicit conversion between UInt32 and Int64. Those 32 leading 1′s must go; Back to Binary And.
PS C:\> $Inverted -BAnd [UInt32]::MaxValue 255
The operation takes advantage of the implicit conversion between types to get [UInt32]::MaxValue and to an Int64 type. As a result, this is what BAnd did:
1: 11111111 11111111 11111111 11111111 00000000 00000000 00000000 11111111 2: 00000000 00000000 00000000 00000000 11111111 11111111 11111111 11111111 3: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 11111111
Where 1 is the Int64 value from -BNot, 2 is the implicit conversion of [Int32]::MaxValue to Int64, and 3 is the result of the And operation. Finally, the resulting value can be implicitly (or explicitly) converted back to UInt32.
A very long explanation for a very short function.
Function Get-BroadcastAddress( [String]$IP, [String]$Mask ) {
Return ConvertTo-DottedDecimalIP $(
(ConvertTo-DecimalIP $IP) -BOr
((-BNot (ConvertTo-DecimalIP $Mask)) -BAnd [UInt32]::MaxValue))
}
Get-NetworkInfo
Putting the functions above to work, providing a summary of an IP address range.
Function Get-NetworkInfo( [String]$IP, [String]$Mask ) {
If ($IP.Contains("/"))
{
$Temp = $IP.Split("/")
$IP = $Temp[0]
$Mask = $Temp[1]
}
If (!$Mask.Contains("."))
{
$Mask = ConvertTo-Mask $Mask
}
$DecimalIP = ConvertTo-DecimalIP $IP
$DecimalMask = ConvertTo-DecimalIP $Mask
$Network = $DecimalIP -BAnd $DecimalMask
$Broadcast = $DecimalIP -BOr
((-BNot $DecimalMask) -BAnd [UInt32]::MaxValue)
$NetworkAddress = ConvertTo-DottedDecimalIP $Network
$RangeStart = ConvertTo-DottedDecimalIP ($Network + 1)
$RangeEnd = ConvertTo-DottedDecimalIP ($Broadcast - 1)
$BroadcastAddress = ConvertTo-DottedDecimalIP $Broadcast
$MaskLength = ConvertTo-MaskLength $Mask
$BinaryIP = ConvertTo-BinaryIP $IP; $Private = $False
Switch -RegEx ($BinaryIP)
{
"^1111" { $Class = "E"; $SubnetBitMap = "1111" }
"^1110" { $Class = "D"; $SubnetBitMap = "1110" }
"^110" {
$Class = "C"
If ($BinaryIP -Match "^11000000.10101000") { $Private = $True } }
"^10" {
$Class = "B"
If ($BinaryIP -Match "^10101100.0001") { $Private = $True } }
"^0" {
$Class = "A"
If ($BinaryIP -Match "^00001010") { $Private = $True } }
}
$NetInfo = New-Object Object
Add-Member NoteProperty "Network" -Input $NetInfo -Value $NetworkAddress
Add-Member NoteProperty "Broadcast" -Input $NetInfo -Value $BroadcastAddress
Add-Member NoteProperty "Range" -Input $NetInfo `
-Value "$RangeStart - $RangeEnd"
Add-Member NoteProperty "Mask" -Input $NetInfo -Value $Mask
Add-Member NoteProperty "MaskLength" -Input $NetInfo -Value $MaskLength
Add-Member NoteProperty "Hosts" -Input $NetInfo `
-Value $($Broadcast - $Network - 1)
Add-Member NoteProperty "Class" -Input $NetInfo -Value $Class
Add-Member NoteProperty "IsPrivate" -Input $NetInfo -Value $Private
Return $NetInfo
}
Get-NetworkRange
Calculate each host address within the network range.
Function Get-NetworkRange( [String]$IP, [String]$Mask ) {
If ($IP.Contains("/"))
{
$Temp = $IP.Split("/")
$IP = $Temp[0]
$Mask = $Temp[1]
}
If (!$Mask.Contains("."))
{
$Mask = ConvertTo-Mask $Mask
}
$DecimalIP = ConvertTo-DecimalIP $IP
$DecimalMask = ConvertTo-DecimalIP $Mask
$Network = $DecimalIP -BAnd $DecimalMask
$Broadcast = $DecimalIP -BOr ((-BNot $DecimalMask) -BAnd [UInt32]::MaxValue)
For ($i = $($Network + 1); $i -lt $Broadcast; $i++) {
ConvertTo-DottedDecimalIP $i
}
}
Examples
# Use of Convert Functions ConvertTo-BinaryIP 192.168.1.1 ConvertTo-DecimalIP 192.168.1.1 ConvertTo-DottedDecimalIP 11000000.10101000.00000001.00000001 ConvertTo-DottedDecimalIP 3232235777 ConvertTo-MaskLength 255.255.128.0 ConvertTo-Mask 17 # Use of Network and Broadcast Address Functions Get-NetworkAddress 192.168.1.1 255.255.255.0 Get-BroadcastAddress 192.168.1.1 255.255.255.0 # Use of Network Info Get-NetworkInfo 229.168.1.1 255.255.248.0 Get-NetworkInfo 172.16.1.243 18 Get-NetworkInfo 10.0.0.3/14 # Use of Network Range Get-NetworkRange 192.168.1.5 255.255.255.248 Get-NetworkRange 172.18.0.23 30 Get-NetworkRange 172.18.0.23/29
No related posts.
Related posts brought to you by Yet Another Related Posts Plugin.
Posted by Neil Fairall on 23.01.10 at 3:35 pm
This is beautiful. I have been doing some of this from scratch and you just saved me a lot of time.
One thing I have noted however is that Int32 can’t handle some of the decimal values generated. Everything works very nicely if you use Int64 instead.
Posted by Chris on 23.01.10 at 3:35 pm
Thanks :)
PowerShell 1? It may have some issues with the unsigned integers used by the script, everything was written under PowerShell 2. Otherwise if you happen to have examples of where it’s failing I’ll fix it :)
Posted by Episode 112 – Scripting Games 2010 Roundup with Ed Wilson and Joel Bennett « PowerScripting Podcast on 23.01.10 at 3:35 pm
[...] IP subnet math in PowerShell Tips [...]
Posted by Gareth on 23.01.10 at 3:35 pm
Great thanks for posting – you just saved me a whole load of pain!!
I owe you a beer!
Cheers
GM
Posted by Andy N on 23.01.10 at 3:35 pm
These functions have proven supremely useful – I’ve automated a tedious firewall object provisioning process based on your work.. Thanks for sharing!
Rather thank dot-sourcing, I’ve made a Module from the file by adding ” Export-ModuleMember -Function * ” and saving the script as a .psm1 file in the system Modules directory.