Written to complement the VbScript version of this post. This collection of functions handles subnet maths in PowerShell.
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 {
<#
.Synopsis
Converts a Decimal IP address into a binary format.
.Description
ConvertTo-BinaryIP uses System.Convert to switch between decimal and binary format. The output from this function is dotted binary.
.Parameter IPAddress
An IP Address to convert.
#>
[CmdLetBinding()]
Param(
[Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
[Net.IPAddress]$IPAddress
)
Process {
Return [String]::Join('.', $( $IPAddress.GetAddressBytes() |
ForEach-Object { [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 {
<#
.Synopsis
Converts a Decimal IP address into a 32-bit unsigned integer.
.Description
ConvertTo-DecimalIP takes a decimal IP, uses a shift-like operation on each octet and returns a single UInt32 value.
.Parameter IPAddress
An IP Address to convert.
#>
[CmdLetBinding()]
Param(
[Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
[Net.IPAddress]$IPAddress
)
Process {
$i = 3; $DecimalIP = 0;
$IPAddress.GetAddressBytes() | ForEach-Object { $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 set of regular expressions to determine which format is presented.
Function ConvertTo-DottedDecimalIP {
<#
.Synopsis
Returns a dotted decimal IP address from either an unsigned 32-bit integer or a dotted binary string.
.Description
ConvertTo-DottedDecimalIP uses a regular expression match on the input string to convert to an IP address.
.Parameter IPAddress
A string representation of an IP address from either UInt32 or dotted binary.
#>
[CmdLetBinding()]
Param(
[Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
[String]$IPAddress
)
Process {
Switch -RegEx ($IPAddress) {
"([01]{8}\.){3}[01]{8}" {
Return [String]::Join('.', $( $IPAddress.Split('.') | ForEach-Object { [Convert]::ToUInt32($_, 2) } ))
}
"\d" {
$IPAddress = [UInt32]$IPAddress
$DottedIP = $( For ($i = 3; $i -gt -1; $i--) {
$Remainder = $IPAddress % [Math]::Pow(256, $i)
($IPAddress - $Remainder) / [Math]::Pow(256, $i)
$IPAddress = $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 {
<#
.Synopsis
Returns the length of a subnet mask.
.Description
ConvertTo-MaskLength accepts any IPv4 address as input, however the output value
only makes sense when using a subnet mask.
.Parameter SubnetMask
A subnet mask to convert into length
#>
[CmdLetBinding()]
Param(
[Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
[Alias("Mask")]
[Net.IPAddress]$SubnetMask
)
Process {
$Bits = "$( $SubnetMask.GetAddressBytes() | ForEach-Object { [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 {
<#
.Synopsis
Returns a dotted decimal subnet mask from a mask length.
.Description
ConvertTo-Mask returns a subnet mask in dotted decimal format from an integer value ranging
between 0 and 32. ConvertTo-Mask first creates a binary string from the length, converts
that to an unsigned 32-bit integer then calls ConvertTo-DottedDecimalIP to complete the operation.
.Parameter MaskLength
The number of bits which must be masked.
#>
[CmdLetBinding()]
Param(
[Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
[Alias("Length")]
[ValidateRange(0, 32)]
$MaskLength
)
Process {
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 {
<#
.Synopsis
Takes an IP address and subnet mask then calculates the network address for the range.
.Description
Get-NetworkAddress returns the network address for a subnet by performing a bitwise AND
operation against the decimal forms of the IP address and subnet mask. Get-NetworkAddress
expects both the IP address and subnet mask in dotted decimal format.
.Parameter IPAddress
Any IP address within the network range.
.Parameter SubnetMask
The subnet mask for the network.
#>
[CmdLetBinding()]
Param(
[Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
[Net.IPAddress]$IPAddress,
[Parameter(Mandatory = $True, Position = 1)]
[Alias("Mask")]
[Net.IPAddress]$SubnetMask
)
Process {
Return ConvertTo-DottedDecimalIP ((ConvertTo-DecimalIP $IPAddress) -BAnd (ConvertTo-DecimalIP $SubnetMask))
}
}
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 {
<#
.Synopsis
Takes an IP address and subnet mask then calculates the broadcast address for the range.
.Description
Get-BroadcastAddress returns the broadcast address for a subnet by performing a bitwise AND
operation against the decimal forms of the IP address and inverted subnet mask.
Get-BroadcastAddress expects both the IP address and subnet mask in dotted decimal format.
.Parameter IPAddress
Any IP address within the network range.
.Parameter SubnetMask
The subnet mask for the network.
#>
[CmdLetBinding()]
Param(
[Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
[Net.IPAddress]$IPAddress,
[Parameter(Mandatory = $True, Position = 1)]
[Alias("Mask")]
[Net.IPAddress]$SubnetMask
)
Process {
Return ConvertTo-DottedDecimalIP $((ConvertTo-DecimalIP $IPAddress) -BOr `
((-BNot (ConvertTo-DecimalIP $SubnetMask)) -BAnd [UInt32]::MaxValue))
}
}
Get-NetworkSummary
Putting the functions above to work, providing a summary of an IP address range.
Function Get-NetworkSummary ( [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-NetworkSummary 229.168.1.1 255.255.248.0 Get-NetworkSummary 172.16.1.243 18 Get-NetworkSummary 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.
Neil Fairall says:
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.
March 5, 2010, 12:43 pmChris says:
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 :)
March 5, 2010, 12:54 pmEpisode 112 – Scripting Games 2010 Roundup with Ed Wilson and Joel Bennett « PowerScripting Podcast says:
[...] IP subnet math in PowerShell Tips [...]
May 17, 2010, 10:37 amGareth says:
Great thanks for posting – you just saved me a whole load of pain!!
I owe you a beer!
Cheers
GM
May 20, 2010, 2:58 pmAndy N says:
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.
June 29, 2010, 7:02 pmWes says:
I’m noticing a problem when I run the script with Powershell 1 in Windows 2003…
Cannot convert value “4294966784″ to type “System.Int32″. Error: “Value was either too large or too small for
”
+ (ConvertTo-DecimalIP $IP) -BAnd <<<<
this occurs inside the Get-NetworkAddress function. each "ConvertTo-DecimalIP" function within Get-NetworkAddress get called successfully, but it fails right before it gets to the value to send to ConvertTo-DottedDecimalIP.
I believe it has to do with the use of "-BAnd"
September 8, 2010, 10:50 pmChris says:
I can’t make it work as well for PowerShell 1, it needs to support Unsigned Integers and PowerShell 1 does not. That is, it must support System.UInt32, not just System.Int32.
September 9, 2010, 8:18 amScript for DHCP Scope - Admins Goodies says:
[...] Use Powershell. You’ll want to look at the “..” range operator, and have a look at the Net-SubnetMath.ps1 functions by Chris Dent. [...]
August 15, 2011, 4:37 pmIndented! : More IPv4 subnet maths with PowerShell says:
[...] functions for some time now, I thought I’d post the functions that work directly with my original collection. These functions are also included in my NetShell module, I need to post an update to that very [...]
October 13, 2011, 11:55 am