Author Posts

May 2, 2014 at 12:56 pm

Is there a native way in PowerShell to do IP math? Take for example I want to use the network 10.0.0.0/22 and find all pingable devices on the network. It's a simple example but I should be able to loop through all the address by giving it the CIDR notation or a network with subnet. How should I go about accomplishing this?

May 2, 2014 at 1:24 pm

As luck would have it, this was part of the practice event in the scripting games (given CIDR notation of a subnet, identify computers on the subnet, collect inventory data, etc.) Here's how I accomplished it:

function Get-UInt32FromIPAddress
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [ipaddress]
        $IPAddress
    )

    $bytes = $IPAddress.GetAddressBytes()

    if ([BitConverter]::IsLittleEndian)
    {
        [Array]::Reverse($bytes)
    }

    return [BitConverter]::ToUInt32($bytes, 0)
}

function Get-IPAddressFromUInt32
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [UInt32]
        $UInt32
    )

    $bytes = [BitConverter]::GetBytes($UInt32)
            
    if ([BitConverter]::IsLittleEndian)
    {
        [Array]::Reverse($bytes)
    }

    return New-Object ipaddress(,$bytes)
}

function Get-SubnetAddresses
{
    # Converts an IPv4 subnet address in CIDR notation (ie, 192.168.0.0/24) into a collection of [ipaddress] objects.

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $Subnet
    )

    $ipaddress = $null

    # Validating the string format here instead of in a ValidateScript block allows us to use the
    # $ipaddress and $matches variables without having to perform the parsing twice.

    if ($Subnet -notmatch '^(?
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/(?\d{1,2})$') { throw "Subnet address '$Subnet' does not match the expected CIDR format (example: 192.168.0.0/24)" } if (-not [ipaddress]::TryParse($matches['Address'], [ref]$ipaddress)) { throw "Subnet address '$Subnet' contains an invalid IPv4 address." } $maskDecimal = [int]$matches['Mask'] if ($maskDecimal -gt 30) { throw "Subnet address '$Subnet' contains an invalid subnet mask (must be less than or equal to 30)." } $hostBitCount = 32 - $maskDecimal $netMask = [UInt32]0xFFFFFFFFL -shl $hostBitCount $hostMask = -bnot $netMask $networkAddress = (Get-UInt32FromIPAddress -IPAddress $ipaddress) -band $netMask $broadcastAddress = $networkAddress -bor $hostMask for ($address = $networkAddress + 1; $address -lt $broadcastAddress; $address++) { Get-IPAddressFromUInt32 -UInt32 $address } }

I could then call the Get-SubnetAddresses function and either pipe it to something, or just save the results as an array to be enumerated with a foreach loop.

Edit: Looking over this code again, I should probably note that it requires PowerShell 3.0 or later (for the -shl operator). It can be modified to work with PowerShell 2.0, but the code would be a little bit harder to read. "$netMask = [UInt32]0xFFFFFFFFL -shl $hostBitCount" would become something like "$netMask = ([UInt32]((0xFFFFFFFL * [math]::Pow(2, $hostBitCount)) -band 0xFFFFFFFFL))"

May 5, 2014 at 11:12 pm

Sorry to go off topic a bit, but I like the way that you use

and in your regex pattern to name the keys in the $matches hash table. Is there any documentation on this feature? I've never seen it done before...

May 6, 2014 at 3:09 am

Those are called "named groups". You can find all of the official documentation on .NET regular expressions on MSDN, and http://www.regular-expressions.info/ is also quite a good reference (covering more than just the .NET implementation, plus a lot of good tutorials in general.)