Test IPv4 subnet membership
Compare addresses by prefix, including default, point-to-point, and host prefixes.
A prefix identifies an address block
A CIDR such as 192.0.2.0/24 has 24 network bits and 8 remaining bits. Its block size is 2^(32 - prefix length). Two IPv4 numbers are in the same block when dividing by that size and rounding down produces the same quotient.
Dot-source AddressMath.ps1 to use the strict converter from the previous lesson. Parse the slash and validate a canonical decimal prefix from 0 to 32. This exercise accepts host bits in the prefix address: 192.0.2.99/24 identifies the same block as 192.0.2.0/24.
[math]::Pow(2, 32 - 24)
Membership is not host assignability
The first and last address are both members of a prefix. Do not subtract network or broadcast addresses from a membership test. A /0 contains every IPv4 address, a /32 contains one, and a /31 contains two. On appropriately configured point-to-point links, both /31 addresses can be used as endpoints.
Use a wide value for the block size: /0 requires 4,294,967,296. A shift on a 32-bit integer by 32 does not provide a reliable /0 shortcut. Avoid enumerating all addresses; a membership decision needs only arithmetic.
The function returns one boolean and throws on malformed addresses or prefixes. It performs no packet transmission or connectivity test.
Go deeper
. '/home/user/AddressMath.ps1'
ConvertTo-IPv4Number '192.0.2.10'
Put it into practice.
Dot-source AddressMath.ps1 and define Test-IPv4Subnet -Address -Cidr. Return a boolean for membership, including the first and last address. Accept prefix lengths 0–32 with no leading zeros, and normalise host bits by comparing blocks. Throw for malformed addresses/CIDRs. Do not enumerate addresses.
See an explained solution
. '/home/user/AddressMath.ps1'
function Test-IPv4Subnet {
param([string]$Address,[string]$Cidr)
$parts = $Cidr.Split('/')
if ($parts.Count -ne 2 -or $parts[1] -cnotmatch '\A(?:0|[1-9][0-9]?)\z' -or [int]$parts[1] -gt 32) { throw 'Invalid IPv4 prefix length.' }
$addressValue = ConvertTo-IPv4Number $Address
$prefixValue = ConvertTo-IPv4Number $parts[0]
[uint64]$blockSize = [math]::Pow(2, 32 - [int]$parts[1])
return [bool]([math]::Floor($addressValue / $blockSize) -eq [math]::Floor($prefixValue / $blockSize))
}
Block arithmetic handles /0, /31, and /32 without special host-count assumptions. The converter rejects malformed address text before a numeric comparison.