public/Network/Convert-IPCalc.ps1

Function Convert-IPCalc {
    <#
    .SYNOPSIS
        Convert-IPCalc calculates the IP subnet information based upon the entered IP address and subnet.
    .COMPONENT
        Network
    .EXAMPLE
        Convert-IPCalc 10.10.100.5/24
    .EXAMPLE
        Convert-IPCalc -IPAddress 10.100.100.1 -NetMask 255.255.255.0
    .EXAMPLE
        Convert-IPCalc 192.168.0.1/24 -IncludeBinaryOutput
    .EXAMPLE
        Convert-IPCalc 192.168.0.1/24 -IncludeHostList
    .NOTES
        Inspired by Jason Wasser
    #>

    [CmdletBinding()]
    param (
        # Enter the IP address by itself or with CIDR notation.
        [Parameter(Mandatory=$True,Position=1)][string]$IPAddress,
        # Enter the subnet mask information in dotted decimal form.
        [Parameter(Mandatory=$False,Position=2)][string]$Netmask,
        # Include the binary format of the subnet information.
        [switch]$IncludeBinaryOutput,
        # Include List of all hosts.
        [switch]$IncludeHostList

    )
    process {
        # Function to convert IP address string to binary: "1.2.3.4" => "00000001000000100000001100000100"
        function toBinary ($dottedDecimal) {
            $dottedDecimal.split(".") | ForEach-Object { $binary += $([convert]::toString($_,2).padleft(8,"0")) }
            return $binary
        }

        # Function to convert binary IP address to dotted decimal string: "00000001000000100000001100000100" => "1.2.3.4"
        function toDottedDecimal ($binary) {
            do { $dottedDecimal += ".$([string]$([convert]::toInt32($binary.substring($i,8),2)))"; $i+=8 } while ($i -le 24)
            return $dottedDecimal.substring(1)
        }

        # Function to convert CIDR format to binary: 24 => "11111111111111111111111100000000"
        function CidrToBin ($cidr) {
            if($cidr -le 32) {
                [Int[]]$array = (1..32)
                for ($i=0;$i -lt $array.length;$i++) {
                    if($array[$i] -gt $cidr) {$array[$i]="0"} else {$array[$i]="1"}
                }
            }
            return $array -join ""
        }

        # Function to convert network mask to wildcard format: "11111111111111111111111100000000" => 00000000000000000000000011111111
        function NetMasktoWildcard ($wildcard) {
            foreach ($bit in [char[]]$wildcard) {
                if ($bit -eq "1") { $wildcardmask += "0" } 
                elseif ($bit -eq "0") { $wildcardmask += "1" }
            }
            return $wildcardmask
        }


        # Check to see if the IP Address was entered in CIDR format.
        if ($IPAddress -like "*/*") {
            $CIDRIPAddress = $IPAddress
            $IPAddress = $CIDRIPAddress.Split("/")[0]
            $cidr = [convert]::ToInt32($CIDRIPAddress.Split("/")[1])
            if ($cidr -in 1..32 ) {
                $ipBinary = toBinary $IPAddress
                Write-Verbose $ipBinary
                $smBinary = CidrToBin($cidr)
                Write-Verbose $smBinary
                $Netmask = toDottedDecimal($smBinary)
                $wildcardbinary = NetMasktoWildcard ($smBinary)
            } else { throw "Subnet Mask is invalid!" }
        } else { # Address was not entered in CIDR format.
            if (!$Netmask) { throw 'Subnet Mask is missing!' }
            $ipBinary = toBinary $IPAddress
            if ($Netmask -eq "0.0.0.0") { throw "Subnet Mask is invalid!" }
            else {
                $smBinary = toBinary $Netmask
                $wildcardbinary = NetMasktoWildcard ($smBinary)
            }
        }

        $netBits = $smBinary.indexOf("0") # First determine the location of the first zero in the subnet mask in binary (if any)

        # If there is a 0 found then the subnet mask is less than 32 (CIDR).
        if ($netBits -ne -1) {
            $cidr = $netBits
            if (($smBinary.length -ne 32) -or ($smBinary.substring($netBits).contains("1"))) { throw "Subnet Mask is invalid!" } # Validate the subnet mask
            if ($ipBinary.length -ne 32) { throw "IP Address is invalid!" } # Validate the IP address
            #identify subnet boundaries
            $networkIDbinary = $ipBinary.substring(0,$netBits).padright(32,"0")
            $networkID = toDottedDecimal $networkIDbinary
            $firstAddressBinary = $($ipBinary.substring(0,$netBits).padright(31,"0") + "1")
            $firstAddress = toDottedDecimal $firstAddressBinary
            $lastAddressBinary = $($ipBinary.substring(0,$netBits).padright(31,"1") + "0")
            $lastAddress = toDottedDecimal $lastAddressBinary
            $broadCastbinary = $ipBinary.substring(0,$netBits).padright(32,"1")
            $broadCast = toDottedDecimal $broadCastbinary
            $wildcard = toDottedDecimal $wildcardbinary
            $Hostspernet = ([convert]::ToInt32($broadCastbinary,2) - [convert]::ToInt32($networkIDbinary,2)) - 1

        } else { # Subnet mask is 32 (CIDR)
            if($ipBinary.length -ne 32) { throw "IP Address is invalid!" } # Validate the IP address
            #identify subnet boundaries
            $networkID = toDottedDecimal $ipBinary
            $networkIDbinary = $ipBinary
            $firstAddress = toDottedDecimal $ipBinary
            $firstAddressBinary = $ipBinary
            $lastAddress = toDottedDecimal $ipBinary
            $lastAddressBinary = $ipBinary
            $broadCast = toDottedDecimal $ipBinary
            $broadCastbinary = $ipBinary
            $wildcard = toDottedDecimal $wildcardbinary
            $Hostspernet = 1
            $cidr = 32
        }

        # Output custom object with or without binary information.
        $Output = [PSCustomObject]@{
            Address = $IPAddress
            Netmask = $Netmask
            Wildcard = $wildcard
            Network = "$networkID/$cidr"
            Broadcast = $broadCast
            HostMin = $firstAddress
            HostMax = $lastAddress
            'Hosts/Net' = $Hostspernet
        }
        if ($IncludeBinaryOutput) {
            $Output | Add-Member -MemberType NoteProperty -Name 'AddressBinary' -Value $ipBinary
            $Output | Add-Member -MemberType NoteProperty -Name 'NetmaskBinary' -Value $smBinary
            $Output | Add-Member -MemberType NoteProperty -Name 'WildcardBinary' -Value $wildcardbinary
            $Output | Add-Member -MemberType NoteProperty -Name 'NetworkBinary' -Value $networkIDbinary
            $Output | Add-Member -MemberType NoteProperty -Name 'HostMinBinary' -Value $firstAddressBinary
            $Output | Add-Member -MemberType NoteProperty -Name 'HostMaxBinary' -Value $lastAddressBinary
            $Output | Add-Member -MemberType NoteProperty -Name 'BroadcastBinary' -Value $broadCastbinary
        }
        if ($IncludeHostList) {
            $hostList = New-Object System.Collections.Generic.List[System.Object]  # @()
            for ($ip = [Convert]::ToInt64($firstAddressBinary, 2); $ip -le [Convert]::ToInt64($lastAddressBinary, 2); $ip++) {
                $hostList.Add((toDottedDecimal ([Convert]::ToString($ip,2)).padleft(32,"0")))
            }
            $Output | Add-Member -MemberType NoteProperty -Name 'HostList' -Value $hostList
        }
        $Output
    }
}