-
-
Save jpbruckler/8ff08a717a4bf6f1f952a5b45489b201 to your computer and use it in GitHub Desktop.
CIDR to IP range conversion using PowerShell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# calculate IP address range from CIDR notation and optionally | |
# provide the full range to iterate over | |
# https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing | |
function cidrToIpRange { | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory)][string]$cidrNotation, | |
[switch]$IncludeRange, # add the Range property | |
[switch]$ExcludeNetworkBroadcast # skip net & broadcast addresses | |
) | |
# -- helpers: IPv4 <-> uint32 (host order) -- | |
function ipToUint32([Net.IPAddress]$ip) { | |
$bytes = $ip.GetAddressBytes() | |
[Array]::Reverse($bytes) # network -> host order | |
[BitConverter]::ToUInt32($bytes, 0) | |
} | |
function uint32ToIp([uint32]$n) { | |
$bytes = [BitConverter]::GetBytes($n) | |
[Array]::Reverse($bytes) # host -> network order | |
[Net.IPAddress]::new($bytes) | |
} | |
$addr, $maskLength = $cidrNotation -split '/', 2 | |
[int]$maskLen = 0 | |
if (-not [int32]::TryParse($maskLength, [ref]$maskLen)) { | |
Write-Warning 'No mask, setting to /32' | |
$maskLen = 32 | |
} | |
if ($maskLen -lt 0 -or $maskLen -gt 32) { | |
throw 'CIDR mask length must be between 0 and 32' | |
} | |
$ipAddr = [Net.IPAddress]::Parse($addr) | |
if ($ipAddr.AddressFamily -ne [Net.Sockets.AddressFamily]::InterNetwork) { | |
throw 'Can only process CIDR for IPv4' | |
} | |
# compute network & broadcast | |
$shift = 32 - $maskLen | |
if ($maskLen -eq 0) { $mask = [uint32]0 } | |
else { | |
$lowMask = ([uint64]1 -shl $shift) - 1 # e.g., shift=8 -> 0xFF | |
$mask = [uint32]::MaxValue -bxor [uint32]$lowMask | |
} | |
$ipNum = ipToUint32 $ipAddr | |
$net = $ipNum -band $mask | |
$bcast = $net -bor ([uint32]::MaxValue -bxor $mask) | |
$start = uint32ToIp $net | |
$end = uint32ToIp $bcast | |
$result = [pscustomobject]@{ | |
Start = $start.ToString() | |
End = $end.ToString() | |
} | |
if ($IncludeRange) { | |
# determine first & last based on whether we exclude network/broadcast | |
$first = if ($ExcludeNetworkBroadcast -and $maskLen -lt 31) { $net + 1 } else { $net } | |
$last = if ($ExcludeNetworkBroadcast -and $maskLen -lt 31) { $bcast - 1 } else { $bcast } | |
if ($last -lt $first) { | |
# happens for /31 with exclude, or /32 generally | |
$result | Add-Member -NotePropertyName Range -NotePropertyValue @() | |
return $result | |
} | |
# size & guardrails | |
$count = [int64]($last - $first + 1) | |
if ($count -gt 2000000) { | |
throw "Refusing to allocate a Range with $count addresses. Stream results instead (see note below)." | |
} | |
# fill pre-sized array (much faster than pipeline) | |
$arr = [Net.IPAddress[]]::new($count) | |
$n = $first | |
for ($i = 0; $i -lt $count; $i++) { | |
$arr[$i] = uint32ToIp([uint32]$n) | |
$n++ | |
} | |
$result | Add-Member -NotePropertyName Range -NotePropertyValue $arr | |
} | |
return $result | |
} | |
# Test | |
$ips = @('1.2.3.4/24', '9.8.7.6') | |
$ips | % { cidrToIpRange $_ -IncludeRange } | sort-object start |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment