Written to complement the VbScript version of this post. This collection of functions handles subnet maths in PowerShell.
The functions described here have been updated since posting this article. The updated functions are available as part of the script Indented.Net.IP. The module may be installed from the PS gallery:
Install-Module Indented.Net.IP
Convert an IP to binary
This function uses System.Convert to change each octet of an IP address to binary form. PadLeft is used to add leading zero’s if required.
filter ConvertTo-BinaryIP {
[CmdletBinding()]
[OutputType([String])]
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[IPAddress]$IPAddress
)
$binaryOctets = $IPAddress.GetAddressBytes() | ForEach-Object {
[Convert]::ToString($_, 2).PadLeft(8, '0')
}
$binaryOctets -join '.'
}
Convert an IP to a 32-bit decimal
Allows conversion of an IP Address to an unsigned 32-bit integer.
filter ConvertTo-DecimalIP {
[CmdletBinding()]
[OutputType([UInt32])]
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[IPAddress]$IPAddress
)
$bytes = $IPAddress.GetAddressBytes()
$decimal = 0;
for ($i = 0; $i -le 3; $i++) {
$decimal += $bytes[$i] * [Math]::Pow(256, 3 - $i)
}
[UInt32]$decimal
}
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.
filter ConvertTo-DottedDecimalIP {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[String]$IPAddress
)
switch -regex ($IPAddress) {
'^([01]{8}.){3}[01]{8}$' {
[Byte[]]$bytes = $IPAddress -split '\.' | ForEach-Object {
[Convert]::ToByte($_, 2)
}
[IPAddress]$bytes
}
'^\d+$' {
$IPAddress = [UInt32]$IPAddress
[Byte[]]$bytes = for ($i = 3; $i -ge 0; $i--) {
$remainder = [UInt32]$IPAddress % [Math]::Pow(256, $i)
[UInt32]$IPAddress - $remainder
$IPAddress = $remainder
}
[IPAddress]$bytes
}
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.
filter ConvertTo-MaskLength {
[CmdletBinding()]
[OutputType([Int32])]
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[Alias("Mask")]
[IPAddress]$SubnetMask
)
$binaryOctets = $SubnetMask.GetAddressBytes() | ForEach-Object {
[Convert]::ToString($_, 2)
}
($binaryOctets -join '').Trim('0').Length
}
Convert a mask length to a subnet mask
To a dotted decimal IP via binary and an unsigned 32-bit integer.
filter ConvertTo-Mask {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[Alias("Length")]
[ValidateRange(0, 32)]
[Int32]$MaskLength
)
$binaryIP = ("1" * $MaskLength).PadRight(32, "0")
$decimalIP = [Convert]::ToUInt32($binaryIP, 2)
[Byte[]]$bytes = for ($i = 3; $i -ge 0; $i--) {
$remainder = $decimalIP % [Math]::Pow(256, $i)
Write-Host ($decimalIP - $remainder)
$decimalIP - $remainder
$decimalIP = $remainder
}
[IPAddress]$bytes
}
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.
filter Get-NetworkAddress {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[IPAddress]$IPAddress,
[Parameter(Mandatory = $true, Position = 1)]
[Alias("Mask")]
[IPAddress]$SubnetMask
)
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> $Value = ConvertTo-DecimalIP 255.255.255.0
PS> $Value
4294967040
PS> $Value.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True UInt32 System.ValueType
PS> $Inverted = -bnot $Value
PS> $Inverted
-4294967041
PS> $Inverted.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int64 System.ValueType
PS> # Convert to Binary (Base 2)
PS> [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.
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. This process is shown in binary below:
Int64 value: 11111111 11111111 11111111 11111111 00000000 00000000 00000000 11111111
UInt32.MaxValue: 00000000 00000000 00000000 00000000 11111111 11111111 11111111 11111111
Result of -band: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 11111111
Finally, the resulting value can be implicitly (or explicitly) converted back to UInt32.
A very long explanation for a very short function.
filter Get-BroadcastAddress {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[IPAddress]$IPAddress,
[Parameter(Mandatory = $true, Position = 1)]
[Alias("Mask")]
[IPAddress]$SubnetMask
)
$decimalIP = ConvertTo-DecimalIP $IPAddress
$invertedMask = -bnot (ConvertTo-DecimalIP $SubnetMask) -band [UInt32]::MaxValue
ConvertTo-DottedDecimalIP ($decimalIP -bor $invertedMask)
}
Get-NetworkSummary
Putting the functions above to work, providing a summary of an IP address range.
function Get-NetworkSummary {
param (
[String]$IPAddress,
[String]$SubnetMask
)
if ($IPAddress.Contains("/")) {
$IPAddress, $SubnetMask = $IPAddress.Split("/")
}
if (-not $SubnetMask.Contains('.')) {
$SubnetMask = ConvertTo-Mask $SubnetMask
}
$DecimalIP = ConvertTo-DecimalIP $IPAddress
$DecimalMask = ConvertTo-DecimalIP $SubnetMask
$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 $SubnetMask
$BinaryIP = ConvertTo-BinaryIP $IPAddress
$Private = $false
switch -regex ($BinaryIP) {
"^1111" { $Class = "E"; $SubnetBitMap = "1111"; break }
"^1110" { $Class = "D"; $SubnetBitMap = "1110"; break }
"^110" {
$Class = "C"
if ($BinaryIP -match "^11000000.10101000") {
$Private = $true
}
break
}
"^10" {
$Class = "B"
if ($BinaryIP -match "^10101100.0001") {
$Private = $true
}
break
}
"^0" {
$Class = "A"
if ($BinaryIP -match "^0000101") {
$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 $SubnetMask
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 {
param (
[String]$IPAddress,
[String]$SubnetMask
)
if ($IPAddress.Contains("/")) {
$IPAddress, $SubnetMask = $IPAddress.Split("/")
}
if (-not $SubnetMask.Contains('.')) {
$SubnetMask = ConvertTo-Mask $SubnetMask
}
$DecimalIP = ConvertTo-DecimalIP $IPAddress
$DecimalMask = ConvertTo-DecimalIP $SubnetMask
$Network = $DecimalIP -band $DecimalMask
$Broadcast = $DecimalIP -bor (-bnot $DecimalMask -band [UInt32]::MaxValue)
for ($i = $Network + 1; $i -lt $Broadcast; $i++) {
ConvertTo-DottedDecimalIP $i
}
}
Examples
Convert functions
PS> ConvertTo-BinaryIP 192.168.1.1
11000000.10101000.00000001.00000001
PS> ConvertTo-DecimalIP 192.168.1.1
3232235777
PS> ConvertTo-DottedDecimalIP 11000000.10101000.00000001.00000001
Address : 16885952
AddressFamily : InterNetwork
ScopeId :
IsIPv6Multicast : False
IsIPv6LinkLocal : False
IsIPv6SiteLocal : False
IsIPv6Teredo : False
IsIPv4MappedToIPv6 : False
IPAddressToString : 192.168.1.1
ConvertTo-DottedDecimalIP 3232235777
Address : 16885952
AddressFamily : InterNetwork
ScopeId :
IsIPv6Multicast : False
IsIPv6LinkLocal : False
IsIPv6SiteLocal : False
IsIPv6Teredo : False
IsIPv4MappedToIPv6 : False
IPAddressToString : 192.168.1.1
PS> ConvertTo-MaskLength 255.255.128.0
17
PS> ConvertTo-Mask 17
Address : 8454143
AddressFamily : InterNetwork
ScopeId :
IsIPv6Multicast : False
IsIPv6LinkLocal : False
IsIPv6SiteLocal : False
IsIPv6Teredo : False
IsIPv4MappedToIPv6 : False
IPAddressToString : 255.255.128.0
Use of Network and Broadcast Address Functions
PS> Get-NetworkAddress 192.168.1.1 255.255.255.0
Address : 108736
AddressFamily : InterNetwork
ScopeId :
IsIPv6Multicast : False
IsIPv6LinkLocal : False
IsIPv6SiteLocal : False
IsIPv6Teredo : False
IsIPv4MappedToIPv6 : False
IPAddressToString : 192.168.1.0
PS> Get-BroadcastAddress 192.168.1.1 255.255.255.0
Address : 4278298816
AddressFamily : InterNetwork
ScopeId :
IsIPv6Multicast : False
IsIPv6LinkLocal : False
IsIPv6SiteLocal : False
IsIPv6Teredo : False
IsIPv4MappedToIPv6 : False
IPAddressToString : 192.168.1.255
Use of Network Info
PS> Get-NetworkSummary 229.168.1.1 255.255.248.0
Network : 229.168.0.0
Broadcast : 229.168.7.255
Range : 229.168.0.1 - 229.168.7.254
Mask : 255.255.248.0
MaskLength : 21
Hosts : 2046
Class : D
IsPrivate : False
PS> Get-NetworkSummary 172.16.1.243 18
Network : 172.16.0.0
Broadcast : 172.16.63.255
Range : 172.16.0.1 - 172.16.63.254
Mask : 255.255.192.0
MaskLength : 18
Hosts : 16382
Class : B
IsPrivate : True
PS> Get-NetworkSummary 10.0.0.3/14
Network : 10.0.0.0
Broadcast : 10.3.255.255
Range : 10.0.0.1 - 10.3.255.254
Mask : 255.252.0.0
MaskLength : 14
Hosts : 262142
Class : A
IsPrivate : True
Use of Network Range
PS> Get-NetworkRange 192.168.1.5 255.255.255.248
PS> Get-NetworkRange 172.18.0.23 30
PS> Get-NetworkRange 172.18.0.23/29