SimpleIP.psm1

function Convert-IPv4Address
{
    <#
        .SYNOPSIS
            Convert IP address between formats

        .DESCRIPTION
            Convert IP address between formats

        .PARAMETER Ip
            Input IP is either:
            - Quad dot (without mask) eg. "192.168.1.2"
            - Integer (uint32) eg. 3232235778
            - Binary (32 long string) eg. "11000000101010000000000100000010"

        .PARAMETER QuadDot
            Output in quad dot format, eg. "192.168.1.2"
            This is default output

        .PARAMETER Integer
            Output integer (uint32), eg 3232235778

        .PARAMETER Binary
            Output in binary (32 long string), eg. "11000000101010000000000100000010"

        .EXAMPLE
            Convert-IPv4Address -Ip 192.168.1.2 -Integer
            3232235778

        .EXAMPLE
            Convert-IPv4Address -Ip 192.168.1.2 -Binary
            11000000101010000000000100000010

        .EXAMPLE
            Convert-IPv4Address -Ip 11000000101010000000000100000010 -QuadDot
            192.168.1.2

        .EXAMPLE
            3232235778 | Convert-IPv4Address
            192.168.1.2
    #>


    [OutputType([System.String], ParameterSetName = 'QuadDot')]
    [OutputType([System.UInt32], ParameterSetName = 'Integer')]
    [OutputType([System.String], ParameterSetName = 'Binary')]
    [CmdletBinding(DefaultParameterSetName = 'QuadDot')]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)]
        [System.String]
        $Ip,

        [Parameter(ParameterSetName = 'QuadDot')]
        [System.Management.Automation.SwitchParameter]
        $QuadDot,

        [Parameter(Mandatory = $true, ParameterSetName = 'Integer')]
        [System.Management.Automation.SwitchParameter]
        $Integer,

        [Parameter(Mandatory = $true, ParameterSetName = 'Binary')]
        [System.Management.Automation.SwitchParameter]
        $Binary
    )

    begin
    {
        Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)"
        $origErrorActionPreference = $ErrorActionPreference
        $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue')
    }

    process
    {
        Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)"

        try
        {
            # Make sure that we don't continue on error, and that we catches the error
            $ErrorActionPreference = 'Stop'

            [System.UInt32] $i = 0
            if (Test-ValidIPv4 -Ip $Ip)
            {
                $Ip -split '\.' | ForEach-Object -Process {
                    $i = $i * 256 + $_
                }
            }
            elseif ($Ip -match '^[01]{32}$')
            {
                $i = [System.Convert]::ToUInt32($Ip, 2)
            }
            else
            {
                try
                {
                    $i = $Ip
                }
                catch
                {
                    throw "$Ip is not a valid IPv4 address"
                }
            }

            # Return
            if ($Integer)
            {
                $i
            }
            else
            {
                $b = [System.Convert]::ToString(([System.UInt32] $i), 2).PadLeft(32, '0')
                if ($Binary)
                {
                    $b
                }
                else
                {
                    (0..3 | ForEach-Object -Process {[System.Convert]::ToByte($b[(8*$_)..(8*$_+7)] -join '', 2)}) -join '.'
                }
            }
        }
        catch
        {
            # If error was encountered inside this function then stop doing more
            # But still respect the ErrorAction that comes when calling this function
            # And also return the line number where the original error occured
            $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString()
            Write-Verbose -Message "Encountered an error: $msg"
            Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg
        }
        finally
        {
            $ErrorActionPreference = $origErrorActionPreference
        }

        Write-Verbose -Message 'Process end'
    }

    end
    {
        Write-Verbose -Message 'End'
    }
}

function Convert-IPv4Mask
{
    <#
        .SYNOPSIS
            Convert IP subnet mask between formats

        .DESCRIPTION
            Convert IP subnet mask between formats
            If input is in quad dot format ("255.0.0.0"), output defaults to mask length with a leading slash ("/8")
            - else output defaults to quad dot format
            Output can be forced to be in specific format with switches

        .PARAMETER Mask
            Input subnet mask is either:
            - Quad dot eg. "255.255.128.0"
            - Mask length (0-32) eg. "17"
            - Mask length with slash eg. "/17"
            - Integer (uint32) eg. "4294934528"
            - Binary (32 long string) eg. "11111111111111111000000000000000"

        .PARAMETER QuadDot
            Output in quad dot format, eg. "255.255.128.0"

        .PARAMETER Length
            Output is in mask length, eg "17"

        .PARAMETER LengthWithSlash
            Output is in mask length with leading slash, eg "/17"

        .PARAMETER Integer
            Output integer (uint32), eg 4294934528

        .PARAMETER Binary
            Output in binary (32 long string), eg. "11111111111111111000000000000000"

        .EXAMPLE
            Convert-IPv4Mask -Mask 255.255.128.0
            /17

        .EXAMPLE
            Convert-IPv4Mask -Mask /17
            255.255.128.0

        .EXAMPLE
            Convert-IPv4Mask -Mask 17
            255.255.128.0

        .EXAMPLE
            Convert-IPv4Mask -Mask 17 -Binary
            11111111111111111000000000000000
    #>


    [OutputType([System.String], ParameterSetName = 'Default')]
    [OutputType([System.String], ParameterSetName = 'QuadDot')]
    [OutputType([System.Byte]  , ParameterSetName = 'Length')]
    [OutputType([System.String], ParameterSetName = 'LengthWithSlash')]
    [OutputType([System.UInt32], ParameterSetName = 'Integer')]
    [OutputType([System.String], ParameterSetName = 'Binary')]

    [CmdletBinding(DefaultParameterSetName = 'Default')]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)]
        [System.String]
        $Mask,

        [Parameter(Mandatory = $true, ParameterSetName = 'QuadDot')]
        [System.Management.Automation.SwitchParameter]
        $QuadDot,

        [Parameter(Mandatory = $true, ParameterSetName = 'Length')]
        [System.Management.Automation.SwitchParameter]
        $Length,

        [Parameter(Mandatory = $true, ParameterSetName = 'LengthWithSlash')]
        [System.Management.Automation.SwitchParameter]
        $LengthWithSlash,

        [Parameter(Mandatory = $true, ParameterSetName = 'Integer')]
        [System.Management.Automation.SwitchParameter]
        $Integer,

        [Parameter(Mandatory = $true, ParameterSetName = 'Binary')]
        [System.Management.Automation.SwitchParameter]
        $Binary
    )

    begin
    {
        Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)"
        $origErrorActionPreference = $ErrorActionPreference
        $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue')
    }

    process
    {
        Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)"

        try
        {
            # Make sure that we don't continue on error, and that we catches the error
            $ErrorActionPreference = 'Stop'

            $quadDotInput = $false
            [System.UInt32] $i = 0
            if ($Mask -match '^/?([0-9]|[12][0-9]|3[0-2])$')
            {
                $i = [System.Convert]::ToUInt32(('1' * $Matches[1] + '0' * (32 - $Matches[1])), 2)
            }
            else
            {
                try
                {
                    if (-not ($Mask | Convert-IPv4Address | Test-ValidIPv4 -Mask)) {throw}
                }
                catch
                {
                    throw "$Mask is not a valid subnet mask"
                }

                if (Test-ValidIPv4 -Ip $Mask) {$quadDotInput = $true}
                $i = Convert-IPv4Address -Ip $Mask -Integer
            }

            if (($output = $PSCmdlet.ParameterSetName) -eq 'Default')
            {
                $output = if ($quadDotInput) {'LengthWithSlash'} else {'QuadDot'}
            }

            # Return
            switch ($output)
            {
                'QuadDot'         {$i | Convert-IPv4Address}
                'Length'          {[System.Byte] [regex]::Matches(($i | Convert-IPv4Address -Binary), '1').Count}
                'LengthWithSlash' {'/' + [regex]::Matches(($i | Convert-IPv4Address -Binary), '1').Count}
                'Integer'         {$i}
                'Binary'          {$i | Convert-IPv4Address -Binary}
            }
        }
        catch
        {
            # If error was encountered inside this function then stop doing more
            # But still respect the ErrorAction that comes when calling this function
            # And also return the line number where the original error occured
            $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString()
            Write-Verbose -Message "Encountered an error: $msg"
            Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg
        }
        finally
        {
            $ErrorActionPreference = $origErrorActionPreference
        }

        Write-Verbose -Message 'Process end'
    }

    end
    {
        Write-Verbose -Message 'End'
    }
}

function Convert-IPv6Address
{
    <#
        .SYNOPSIS
            Convert IPv6 address between formats

        .DESCRIPTION
            Convert IPv6 address between formats
            Also compress/compact IPv6 address.
            (IPv6 addresses can be hard to compare ("0::1" -eq "::1"),
            but they are run throug this command, they can be compared)
            Output defaults to compacted IPv6 address (eg. "::1")

        .PARAMETER Ip
            Input IP is either
            - Standard IPv6 format with out prefix (eg. "a:b:00c::" or "a:b:00c::0/64")
            - [uint16[]] array with 8 elements

        .PARAMETER Prefix
            If prefix is not set in IP address, it must be set with this parameter

        .PARAMETER Info
            Output object with IP and prefix in different formats

        .EXAMPLE
            Convert-IPv6Address 00ab:00:0:000:00:fff::1
            ab::fff:0:1

        .EXAMPLE
            Convert-IPv6Address -Ip a:b:c::/64 -Info
            IP : a:b:c::/64
            IPCompact : a:b:c::
            IPExpanded : 000a:000b:000c:0000:0000:0000:0000:0000
            IPIntArray : {10, 11, 12, 0...}
            IPHexArray : {a, b, c, 0...}
            IPHexArrayExpanded : {000a, 000b, 000c, 0000...}
            IPBinary : 00000000000010100000000000001011000000000000110000000000000000000000000000000000000000000000000000000000000000000000000000000000
            Cidr : a:b:c::/64
            Prefix : 64
            PrefixIntArray : {65535, 65535, 65535, 65535...}
            PrefixHexArray : {ffff, ffff, ffff, ffff...}
            PrefixHexArrayExpanded : {ffff, ffff, ffff, ffff...}
            PrefixBinary : 11111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000
    #>


    [OutputType([System.String],  ParameterSetName = 'Default')]
    [OutputType([PSCustomObject], ParameterSetName = 'Info')]
    [CmdletBinding(DefaultParameterSetName = 'Default')]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)]
        [System.Object]
        $Ip,

        [Parameter()]
        [Nullable[System.Byte]]
        $Prefix,

        [Parameter(Mandatory = $true, ParameterSetName = 'Info')]
        [System.Management.Automation.SwitchParameter]
        $Info
    )

    begin
    {
        Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)"
        $origErrorActionPreference = $ErrorActionPreference
        $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue')
    }

    process
    {
        Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)"

        try
        {
            # Make sure that we don't continue on error, and that we catches the error
            $ErrorActionPreference = 'Stop'

            if ($Prefix -ne $null -and $Prefix -gt 128) {throw 'Prefix is higher than 128'}

            [System.UInt16[]] $ipIntArray = @()

            if ($Ip -is [System.String])
            {
                if (-not ($Ip -match '^([0-9a-f:]+)(/(([1-9]?[0-9])|(1[01][0-9])|(12[0-8])))?$')) {throw "Error parsing IPv6 address $Ip"}
                $ipOnly = $Matches[1]
                if ($Prefix -eq $null)
                {
                    $Prefix = $Matches[3]
                }
                elseif ($Matches[3] -ne $null -and $Prefix -ne $Matches[3])
                {
                    "Prefix set to /$($Matches[3]) in -Ip but /$Prefix in -Prefix for $Ip. Using /$Prefix" | Write-Warning
                }

                try
                {
                    if (
                        $ipOnly -match '[^0-9a-f:]' -or  # Something else than hex or colon
                        $ipOnly -match '::.+::' -or      # two double colon
                        $ipOnly -match ':::' -or         # tripple colon
                        $ipOnly -match '^:[^:]' -or      # start with single colon
                        $ipOnly -match '[^:]:$'          # end with single colon
                    ) {throw}
                    $ipOnly = $ipOnly -replace '::',':*:'
                    $ipSplit = $ipOnly -split ':'| Where-Object -FilterScript {$_ -ne ''}
                    if ($ipSplit.Count -gt 8) {throw}
                    $ipIntArray = $ipSplit | ForEach-Object -Process {
                        if ($_ -eq '*') { [System.UInt16[]] (@(0) * (9 - $ipSplit.Count)) }
                        else            { [System.Convert]::ToUInt16($_, 16)              }
                    }
                    if ($ipIntArray.Count -ne 8) {throw}
                }
                catch
                {
                    throw "Error parsing IPv6 address $Ip"
                }
            }
            elseif ($Ip -is [array] -and $Ip.Count -eq 8)
            {
                try { $ipIntArray = $Ip } catch { throw 'Input IP is in unknown format' }
            }
            else
            {
                throw 'Input IP is in unknown format'
            }


            $cidr = $prefixIntArray = $prefixHexArray = $prefixHexArrayExpanded = $prefixBinary = $null
            $ipHexArrayExpanded = $ipIntArray | ForEach-Object -Process { '{0:x4}' -f $_ }
            $ipHexArray         = $ipIntArray | ForEach-Object -Process { '{0:x}'  -f $_ }
            $ipExpanded         = $ipHexArrayExpanded -join ':'
            $ipCompact          = $ipHexArray -join ':'
            foreach ($i in (7..0)) { if ($ipCompact -ne ($ipCompact = $ipCompact -replace "(^|:)0(:0){$i}(:|`$)",'::')) {break} }
            $ipReturn           = $ipCompact
            $ipBinary = ($ipIntArray | ForEach-Object -Process {[System.Convert]::ToString(([System.UInt16] $_), 2).PadLeft(16, '0')}) -join ''

            if ($Prefix)
            {
                $ipReturn = $cidr       = '{0}/{1}' -f $ipCompact, $Prefix
                $prefixBinary           = '1' * $Prefix + '0' * (128 - $Prefix)
                $prefixIntArray         = (0..7).ForEach({ [System.Convert]::ToUInt16($prefixBinary.Substring(($_*16), 16), 2) })
                $prefixHexArrayExpanded = $prefixIntArray | ForEach-Object -Process { '{0:x4}' -f $_ }
                $prefixHexArray         = $prefixIntArray | ForEach-Object -Process { '{0:x}'  -f $_ }
            }

            $r = [PSCustomObject] @{
                IP                     = $ipReturn
                IPCompact              = $ipCompact
                IPExpanded             = $ipExpanded
                IPIntArray             = $ipIntArray
                IPHexArray             = $ipHexArray
                IPHexArrayExpanded     = $ipHexArrayExpanded
                IPBinary               = $ipBinary
                Cidr                   = $cidr
                Prefix                 = $Prefix
                PrefixIntArray         = $prefixIntArray
                PrefixHexArray         = $prefixHexArray
                PrefixHexArrayExpanded = $prefixHexArrayExpanded
                PrefixBinary           = $prefixBinary
            }

            # Return
            if ($Info) { $r    }
            else       { $r.IP }
        }
        catch
        {
            # If error was encountered inside this function then stop doing more
            # But still respect the ErrorAction that comes when calling this function
            # And also return the line number where the original error occured
            $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString()
            Write-Verbose -Message "Encountered an error: $msg"
            Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg
        }
        finally
        {
            $ErrorActionPreference = $origErrorActionPreference
        }

        Write-Verbose -Message 'Process end'
    }

    end
    {
        Write-Verbose -Message 'End'
    }
}

function Get-IPv4Address
{
    <#
        .SYNOPSIS
            Get IP subnet, mask, broadcast for an IP address

        .DESCRIPTION
            Get IP subnet, mask, broadcast for an IP address

        .PARAMETER Ip
            Input IP in quad dot format with subnet mask, either:
            - IP + mask in quad dot, eg. "127.0.0.1 255.0.0.0"
            - IP + mask length, eg. "127.0.0.1/8"
            If input is IP without subnet mask (eg. "127.0.0.1") then -Mask parameter must be set

        .PARAMETER Mask
            If input IP is in format without subnet mask, this parameter must be set to either
            - Quad dot format, eg. "255.255.255.0"
            - Mask length (0-32), eg. "24"
            - Mask length with slash, eg. "/24"

        .PARAMETER Subnet
            Return subnet
            If input is "10.11.12.13/24", then "10.11.12.0/24" is returned

        .PARAMETER Broadcast
            Return broadcast
            If input is "10.11.12.13/24", then "10.11.12.255/24" is returned

        .PARAMETER First
            Return first usable IP in subnet
            If input is "10.11.12.13/24", then "10.11.12.1/24" is returned

        .PARAMETER Last
            Return last usable IP in subnet
            If input is "10.11.12.13/24", then "10.11.12.254/24" is returned

        .PARAMETER All
            Return all usable IPs in subnet
            If input is "10.11.12.13/24", then an array with IP addresses from "10.11.12.1/24" to "10.11.12.254/24" is returned

        .PARAMETER Pool
            Treat subnet and broadcast adddresses as usable
            First IP will be same as subnet and last IP will be the same as broadcast

        .PARAMETER WithMaskLength
            Return in "127.0.0.1/8" format
            This is default output

        .PARAMETER WithMask
            Return in "127.0.0.1 255.0.0.0" format

        .PARAMETER IpOnly
            Return in "127.0.0.1" format

        .PARAMETER MaskQuadDotOnly
            Only return subnet mask in "255.0.0.0" format

        .PARAMETER MaskLengthOnly
            Only return subnet mask in "8" format

        .PARAMETER MaskLengthWithSlashOnly
            Only return subnet mask in "/8" format

        .PARAMETER Info
            Return object with different info

        .EXAMPLE
            Get-IPv4Address -Ip 127.0.0.1/8 -Subnet
            127.0.0.0/24

        .EXAMPLE
            Get-IPv4Address -Ip 127.0.0.1/8 -Broadcast -WithMask
            127.255.255.255 255.0.0.0

        .EXAMPLE
            Get-IPv4Address -Ip 10.100.200.201 -Mask /30 -All -WithMask
            10.100.200.201 255.255.255.252
            10.100.200.202 255.255.255.252

        .EXAMPLE
            Get-IPv4Address -Ip 192.168.0.150/255.255.255.128 -Info
            IP : 192.168.0.150
            Subnet : 192.168.0.128
            FirstIP : 192.168.0.129
            LastIP : 192.168.0.254
            Broadcast : 192.168.0.255
            MaskQuadDot : 255.255.255.128
            MaskLength : 25
    #>


    [OutputType([System.String])]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)]
        [ValidateScript({ (Test-ValidIPv4 -Ip $_ -AllowMask) -or $(throw "$_ is not a valid IPv4 address") })]
        [System.String]
        $Ip,

        [Parameter()]
        [ValidateScript({ (Test-ValidIPv4 -Ip $_ -Mask -AllowLength) -or $(throw "$_ is not a valid IPv4 mask") })]
        [System.String]
        $Mask = '',

        [Parameter(Mandatory = $true, ParameterSetName = 'SubnetWithMaskLength')]
        [Parameter(Mandatory = $true, ParameterSetName = 'SubnetWithMask')]
        [Parameter(Mandatory = $true, ParameterSetName = 'SubnetIpOnly')]
        [System.Management.Automation.SwitchParameter]
        $Subnet,

        [Parameter(Mandatory = $true, ParameterSetName = 'BroadcastWithMaskLength')]
        [Parameter(Mandatory = $true, ParameterSetName = 'BroadcastWithMask')]
        [Parameter(Mandatory = $true, ParameterSetName = 'BroadcastIpOnly')]
        [System.Management.Automation.SwitchParameter]
        $Broadcast,

        [Parameter(Mandatory = $true, ParameterSetName = 'FirstWithMaskLength')]
        [Parameter(Mandatory = $true, ParameterSetName = 'FirstWithMask')]
        [Parameter(Mandatory = $true, ParameterSetName = 'FirstIpOnly')]
        [System.Management.Automation.SwitchParameter]
        $First,

        [Parameter(Mandatory = $true, ParameterSetName = 'LastWithMaskLength')]
        [Parameter(Mandatory = $true, ParameterSetName = 'LastWithMask')]
        [Parameter(Mandatory = $true, ParameterSetName = 'LastIpOnly')]
        [System.Management.Automation.SwitchParameter]
        $Last,

        [Parameter(Mandatory = $true, ParameterSetName = 'AllWithMaskLength')]
        [Parameter(Mandatory = $true, ParameterSetName = 'AllWithMask')]
        [Parameter(Mandatory = $true, ParameterSetName = 'AllIpOnly')]
        [System.Management.Automation.SwitchParameter]
        $All,

        [Parameter()]
        [System.Management.Automation.SwitchParameter]
        $Pool,

        [Parameter(ParameterSetName = 'SubnetWithMaskLength')]
        [Parameter(ParameterSetName = 'BroadcastWithMaskLength')]
        [Parameter(ParameterSetName = 'FirstWithMaskLength')]
        [Parameter(ParameterSetName = 'LastWithMaskLength')]
        [Parameter(ParameterSetName = 'AllWithMaskLength')]
        [System.Management.Automation.SwitchParameter]
        $WithMaskLength,

        [Parameter(Mandatory = $true, ParameterSetName = 'SubnetWithMask')]
        [Parameter(Mandatory = $true, ParameterSetName = 'BroadcastWithMask')]
        [Parameter(Mandatory = $true, ParameterSetName = 'FirstWithMask')]
        [Parameter(Mandatory = $true, ParameterSetName = 'LastWithMask')]
        [Parameter(Mandatory = $true, ParameterSetName = 'AllWithMask')]
        [System.Management.Automation.SwitchParameter]
        $WithMask,

        [Parameter(Mandatory = $true, ParameterSetName = 'SubnetIpOnly')]
        [Parameter(Mandatory = $true, ParameterSetName = 'BroadcastIpOnly')]
        [Parameter(Mandatory = $true, ParameterSetName = 'FirstIpOnly')]
        [Parameter(Mandatory = $true, ParameterSetName = 'LastIpOnly')]
        [Parameter(Mandatory = $true, ParameterSetName = 'AllIpOnly')]
        [System.Management.Automation.SwitchParameter]
        $IpOnly,

        [Parameter(Mandatory = $true, ParameterSetName = 'MaskQuadDotOnly')]
        [System.Management.Automation.SwitchParameter]
        $MaskQuadDotOnly,

        [Parameter(Mandatory = $true, ParameterSetName = 'MaskLengthOnly')]
        [System.Management.Automation.SwitchParameter]
        $MaskLengthOnly,

        [Parameter(Mandatory = $true, ParameterSetName = 'MaskLengthWithSlashOnly')]
        [System.Management.Automation.SwitchParameter]
        $MaskLengthWithSlashOnly,

        [Parameter(Mandatory = $true, ParameterSetName = 'Info')]
        [System.Management.Automation.SwitchParameter]
        $Info
    )

    begin
    {
        Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)"
        $origErrorActionPreference = $ErrorActionPreference
        $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue')
    }

    process
    {
        Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)"

        try
        {
            # Make sure that we don't continue on error, and that we catches the error
            $ErrorActionPreference = 'Stop'

            if ($Mask -eq '')
            {
                if (Test-ValidIPv4 -Ip $Ip) {throw "No mask defined for $Ip"}
            }
            else
            {
                $Mask = $Mask | Convert-IPv4Mask -Length
            }

            if (-not (Test-ValidIPv4 -Ip $Ip))
            {
                ($Ip, $m) = $Ip -split '[/ ]'
                $m = $m | Convert-IPv4Mask -Length
                if ($Mask -eq '')
                {
                    $Mask = $m
                }
                elseif ($Mask -ne $m)
                {
                    "Mask set to /$m in -Ip but /$Mask in -Mask for $Ip. Using /$Mask" | Write-Warning
                }
            }

            [System.String] $maskQuadDot  = $Mask | Convert-IPv4Mask -QuadDot
            [System.UInt32] $maskInt      = $Mask | Convert-IPv4Mask -Integer
            [System.UInt32] $ipInt        = $Ip | Convert-IPv4Address -Integer
            [System.UInt32] $subnetInt    = $ipInt -band $maskInt
            [System.UInt32] $broadcastInt = $ipInt -bor (-bnot $maskInt)
            [System.UInt32] $firstInt     = $subnetInt
            [System.UInt32] $lastInt      = $broadcastInt
            if (-not ($Pool -or $Mask -eq 31 -or $Mask -eq 32))
            {
                ++$firstInt
                --$lastInt
            }

            if ($Info)
            {
                [PSCustomObject] @{
                    IP          = $ipInt        | Convert-IPv4Address
                    Subnet      = $subnetInt    | Convert-IPv4Address
                    FirstIP     = $firstInt     | Convert-IPv4Address
                    LastIP      = $lastInt      | Convert-IPv4Address
                    Broadcast   = $broadcastInt | Convert-IPv4Address
                    MaskQuadDot = $maskQuadDot
                    MaskLength  = $Mask
                }
            }
            else
            {
                $createScript =
                    if     ($Subnet)    { {$subnetInt} }
                    elseif ($Broadcast) { {$broadcastInt} }
                    elseif ($First)     { {$firstInt} }
                    elseif ($Last)      { {$lastInt} }
                    elseif ($All)       { {for ([uint32] $i = $firstInt; $i -le $lastInt; $i++) {$i}} }
                    else                { {$ipInt} }

                $outputScript =
                    if     ($WithMask)                { {'{0} {1}' -f $_, $maskQuadDot} }
                    elseif ($IpOnly)                  { {$_} }
                    elseif ($MaskQuadDotOnly)         { {$maskQuadDot} }
                    elseif ($MaskLengthOnly)          { {$Mask} }
                    elseif ($MaskLengthWithSlashOnly) { {'/{0}' -f $Mask} }
                    else                              { {'{0}/{1}' -f $_, $Mask} }

                $createScript.Invoke() | Convert-IPv4Address | ForEach-Object -Process $outputScript
            }
        }
        catch
        {
            # If error was encountered inside this function then stop doing more
            # But still respect the ErrorAction that comes when calling this function
            # And also return the line number where the original error occured
            $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString()
            Write-Verbose -Message "Encountered an error: $msg"
            Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg
        }
        finally
        {
            $ErrorActionPreference = $origErrorActionPreference
        }

        Write-Verbose -Message 'Process end'
    }

    end
    {
        Write-Verbose -Message 'End'
    }
}

function Get-IPv4Mask
{
    <#
        .SYNOPSIS
            Get IP subnet mask for an IP address

        .DESCRIPTION
            Get IP subnet mask for an IP address

        .PARAMETER Ip
            Input IP in quad dot format with subnet mask, either:
            - IP + mask in quad dot, eg. "127.0.0.1 255.0.0.0"
            - IP + mask length, eg. "127.0.0.1/8"

        .PARAMETER QuadDot
            Return subnet mask in "255.0.0.0" format

        .PARAMETER Length
            Return subnet mask in "8" format

        .PARAMETER LengthWithSlash
            Return subnet mask in "/8" format

        .EXAMPLE
            Get-IPv4Mask 9.8.7.6/22 -QuadDot
            255.255.252.0
    #>


    [OutputType([System.String])]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)]
        [ValidateScript({ (Test-ValidIPv4 -Ip $_ -AllowMask) -or $(throw "$_ is not a valid IPv4 address") })]
        [System.String]
        $Ip,

        #[Parameter()]
        #[ValidateScript({ (Test-ValidIPv4 -Ip $_ -Mask -AllowLength) -or $(throw "$_ is not a valid IPv4 mask") })]
        #[System.String]
        #$Mask = '',

        [Parameter(ParameterSetName = 'QuadDot')]
        [System.Management.Automation.SwitchParameter]
        $QuadDot,

        [Parameter(Mandatory = $true, ParameterSetName = 'Length')]
        [System.Management.Automation.SwitchParameter]
        $Length,

        [Parameter(Mandatory = $true, ParameterSetName = 'LengthWithSlash')]
        [System.Management.Automation.SwitchParameter]
        $LengthWithSlash
    )

    process
    {
        Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)"

        $params = @{
            Ip = $Ip
        }
        #if ($Mask -ne '') {$params['Mask'] = $Mask}

        if     ($Length)          {$params['MaskLengthOnly']          = $true}
        elseif ($LengthWithSlash) {$params['MaskLengthWithSlashOnly'] = $true}
        else                      {$params['MaskQuadDotOnly']         = $true}

        Get-IPv4Address @params

        Write-Verbose -Message 'Process end'
    }
}

function Get-IPv4Subnet
{
    <#
        .SYNOPSIS
            Get IP subnet for an IP address

        .DESCRIPTION
            Get IP subnet for an IP address

        .PARAMETER Ip
            Input IP in quad dot format with subnet mask, either:
            - IP + mask in quad dot, eg. "127.0.0.1 255.0.0.0"
            - IP + mask length, eg. "127.0.0.1/8"
            If input is IP without subnet mask (eg. "127.0.0.1") then -Mask parameter must be set

        .PARAMETER Mask
            If input IP is in format without subnet mask, this parameter must be set to either
            - Quad dot format, eg. "255.255.255.0"
            - Mask length (0-32), eg. "24"
            - Mask length with slash, eg. "/24"

        .PARAMETER WithMaskLength
            Return in "127.0.0.0/8" format
            This is default output

        .PARAMETER WithMask
            Return in "127.0.0.0 255.0.0.0" format

        .PARAMETER IpOnly
            Return in "127.0.0.1" format

        .EXAMPLE
            Get-IPv4Subnet 127.0.0.1/8
            127.0.0.0/8

        .EXAMPLE
            Get-IPv4Subnet -Ip 10.20.30.40/28 -WithMask
            10.20.30.32 255.255.255.240
    #>


    [OutputType([System.String])]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)]
        [ValidateScript({ (Test-ValidIPv4 -Ip $_ -AllowMask) -or $(throw "$_ is not a valid IPv4 address") })]
        [System.String]
        $Ip,

        [Parameter()]
        [ValidateScript({ (Test-ValidIPv4 -Ip $_ -Mask -AllowLength) -or $(throw "$_ is not a valid IPv4 mask") })]
        [System.String]
        $Mask = '',

        [Parameter(ParameterSetName = 'SubnetWithMaskLength')]
        [System.Management.Automation.SwitchParameter]
        $WithMaskLength,

        [Parameter(Mandatory = $true, ParameterSetName = 'SubnetWithMask')]
        [System.Management.Automation.SwitchParameter]
        $WithMask,

        [Parameter(Mandatory = $true, ParameterSetName = 'SubnetIpOnly')]
        [System.Management.Automation.SwitchParameter]
        $IpOnly
    )

    process
    {
        Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)"
        Get-IPv4Address -Subnet @PSBoundParameters
        Write-Verbose -Message 'Process end'
    }
}

function Get-IPv6Address
{
    <#
        .SYNOPSIS
            Get subnet, prefix, ... for an IPv6 address

        .DESCRIPTION
            Get subnet, prefix, ... for an IPv6 address

        .PARAMETER Ip
            Input IP is standard IPv6 format with out prefix (eg. "a:b:00c::" or "a:b:00c::0/64")

        .PARAMETER Prefix
            If prefix is not set in IP address, it must be set with this parameter

        .PARAMETER Subnet
            Return subnet
            If input is "7:6:5::77:88/56", then "7:6:5::/56" is returned

        .PARAMETER WithPrefix
            Return in "7:6:5::/64" format
            This is default output
 
        .PARAMETER IpOnly
            Return in "7:6:5::" format

        .PARAMETER Info
            Return object with different info

        .EXAMPLE
            Get-IPv6Address -Ip 7:6:5::77:88/64 -Subnet
            7:6:5::/64

        .EXAMPLE
            Get-IPv6Address -Ip 7:6:5::77:88/64 -Info
            IP : 7:6:5::77:88/64
            Subnet : 7:6:5::/64
            FirstIP4Real : 7:6:5::/64
            FirstIP : 7:6:5::1/64
            LastIP : 7:6:5::ffff:ffff:ffff:fffe/64
            LastIP4Real : 7:6:5::ffff:ffff:ffff:ffff/64
    #>


    [OutputType([System.String])]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)]
        [System.String]
        $Ip,

        [Parameter()]
        [Nullable[System.Byte]]
        $Prefix,

        [Parameter(Mandatory = $true, ParameterSetName = 'SubnetWithPrefix')]
        [Parameter(Mandatory = $true, ParameterSetName = 'SubnetIpOnly')]
        [System.Management.Automation.SwitchParameter]
        $Subnet,

        #[Parameter(Mandatory = $true, ParameterSetName = 'BroadcastWithPrefix')]
        #[Parameter(Mandatory = $true, ParameterSetName = 'BroadcastIpOnly')]
        #[System.Management.Automation.SwitchParameter]
        #$Broadcast,

        #[Parameter(Mandatory = $true, ParameterSetName = 'FirstWithPrefix')]
        #[Parameter(Mandatory = $true, ParameterSetName = 'FirstIpOnly')]
        #[System.Management.Automation.SwitchParameter]
        #$First,

        #[Parameter(Mandatory = $true, ParameterSetName = 'LastWithPrefix')]
        #[Parameter(Mandatory = $true, ParameterSetName = 'LastIpOnly')]
        #[System.Management.Automation.SwitchParameter]
        #$Last,

        #[Parameter(Mandatory = $true, ParameterSetName = 'AllWithPrefix')]
        #[Parameter(Mandatory = $true, ParameterSetName = 'AllIpOnly')]
        #[System.Management.Automation.SwitchParameter]
        #$All,

        #[Parameter()]
        #[System.Management.Automation.SwitchParameter]
        #$Pool,

        [Parameter(ParameterSetName = 'SubnetWithPrefix')]
        #[Parameter(ParameterSetName = 'BroadcastWithPrefix')]
        #[Parameter(ParameterSetName = 'FirstWithPrefix')]
        #[Parameter(ParameterSetName = 'LastWithPrefix')]
        #[Parameter(ParameterSetName = 'AllWithPrefix')]
        [System.Management.Automation.SwitchParameter]
        $WithPrefix,

        [Parameter(Mandatory = $true, ParameterSetName = 'SubnetIpOnly')]
        #[Parameter(Mandatory = $true, ParameterSetName = 'BroadcastIpOnly')]
        #[Parameter(Mandatory = $true, ParameterSetName = 'FirstIpOnly')]
        #[Parameter(Mandatory = $true, ParameterSetName = 'LastIpOnly')]
        #[Parameter(Mandatory = $true, ParameterSetName = 'AllIpOnly')]
        [System.Management.Automation.SwitchParameter]
        $IpOnly,

        #[Parameter(Mandatory = $true, ParameterSetName = 'PrefixOnly')]
        #[System.Management.Automation.SwitchParameter]
        #$PrefixOnly,

        #[Parameter(Mandatory = $true, ParameterSetName = 'PrefixWithSlashOnly')]
        #[System.Management.Automation.SwitchParameter]
        #$PrefixWithSlashOnly,

        [Parameter(Mandatory = $true, ParameterSetName = 'Info')]
        [System.Management.Automation.SwitchParameter]
        $Info
    )

    begin
    {
        Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)"
        $origErrorActionPreference = $ErrorActionPreference
        $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue')
    }

    process
    {
        Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)"

        try
        {
            # Make sure that we don't continue on error, and that we catches the error
            $ErrorActionPreference = 'Stop'

            $prefixParam = if ($Prefix -eq $null) { @{} } else { @{Prefix = $Prefix} }
            $ipInfo = Convert-IPv6Address -Info -Ip $Ip @prefixParam

            if ($ipInfo.Prefix -eq $null) {throw "No prefix defined for $IP"}
            $Prefix = $ipInfo.Prefix

            # Yeah yeah, I know that there's now broadcast IP in IPv6 and that every IP can be used!
            [uint16[]] $ipInt        = $ipInfo.IPIntArray
            [uint16[]] $prefixInt    = $ipInfo.PrefixIntArray

            # Why the f**k does binary operators not work properly with uint16
            # For IPv4 it's just "$ip -band $mask" and "$ip -bor (-bnot $mask)"
            [uint16[]] $subnetInt    = (0..7).ForEach({ [uint16] (([uint32]$ipInt[$_]) -band ([uint32]$prefixInt[$_])) })
            [uint16[]] $broadcastInt = (0..7).ForEach({ [uint16] ((([uint32]$ipInt[$_]) -bor (-bnot ([uint32]$prefixInt[$_]))) -band [uint16]::MaxValue)})
            [uint16[]] $firstInt     = $subnetInt.Clone()
            [uint16[]] $lastInt      = $broadcastInt.Clone()
            if ($Prefix -ne 128)
            {
                ++$firstInt[7]
                --$lastInt[7]
            }

            if ($Info)
            {
                [PSCustomObject] @{
                    IP           = Convert-IPv6Address -Ip $ipInt        -Prefix $Prefix
                    Subnet       = Convert-IPv6Address -Ip $subnetInt    -Prefix $Prefix
                    FirstIP4Real = Convert-IPv6Address -Ip $subnetInt    -Prefix $Prefix
                    FirstIP      = Convert-IPv6Address -Ip $firstInt     -Prefix $Prefix
                    LastIP       = Convert-IPv6Address -Ip $lastInt      -Prefix $Prefix
                    LastIP4Real  = Convert-IPv6Address -Ip $broadcastInt -Prefix $Prefix
                }
            }
            else
            {
                $createScript =
                    if     ($Subnet)    { {,@($subnetInt)} }
                    #elseif ($Broadcast) { {$broadcastInt} }
                    #elseif ($First) { {$firstInt} }
                    #elseif ($Last) { {$lastInt} }
                    #elseif ($All) { {for ([uint32] $i = $firstInt; $i -le $lastInt; $i++) {$i}} }
                    #else { {$ipInt} }
                    else                { throw 'Unknown error' }

                $outputScript =
                    if     ($IpOnly)              { {(Convert-IPv6Address -Ip $_ -Prefix $Prefix -Info).IPCompact} }
                    #elseif ($PrefixOnly) { {} }
                    #elseif ($PrefixWithSlashOnly) { {} }
                    else                          { {Convert-IPv6Address -Ip $_ -Prefix $Prefix} }

                $createScript.Invoke() | ForEach-Object -Process $outputScript
            }

        }
        catch
        {
            # If error was encountered inside this function then stop doing more
            # But still respect the ErrorAction that comes when calling this function
            # And also return the line number where the original error occured
            $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString()
            Write-Verbose -Message "Encountered an error: $msg"
            Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg
        }
        finally
        {
            $ErrorActionPreference = $origErrorActionPreference
        }

        Write-Verbose -Message 'Process end'
    }

    end
    {
        Write-Verbose -Message 'End'
    }
}

function Get-IPv6Subnet
{
    <#
        .SYNOPSIS
            Get IP subnet for an IPv6 address

        .DESCRIPTION
            Get IP subnet for an IPv6 address

        .PARAMETER Ip
            Input IP is standard IPv6 format with out prefix (eg. "a:b:00c::" or "a:b:00c::0/64")

        .PARAMETER Prefix
            If prefix is not set in IP address, it must be set with this parameter

        .PARAMETER WithPrefix
            Return in "7:6:5::/64" format
            This is default output
 
        .PARAMETER IpOnly
            Return in "7:6:5::" format

        .EXAMPLE
            Get-IPv6Subnet -Ip 7:6:5::77:88/64
            7:6:5::/64

        .EXAMPLE
            Get-IPv6Subnet -Ip 7:6:5::77:88/64 -IpOnly
            7:6:5::
    #>


    [OutputType([System.String])]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)]
        [System.String]
        $Ip,

        [Parameter()]
        [Nullable[System.Byte]]
        $Prefix,

        [Parameter(ParameterSetName = 'SubnetWithPrefix')]
        [System.Management.Automation.SwitchParameter]
        $WithPrefix,

        [Parameter(Mandatory = $true, ParameterSetName = 'SubnetIpOnly')]
        [System.Management.Automation.SwitchParameter]
        $IpOnly
    )

    process
    {
        Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)"
        Get-IPv6Address -Subnet @PSBoundParameters
        Write-Verbose -Message 'Process end'
    }
}

function Test-ValidIPv4
{
    <#
        .SYNOPSIS
            Test if a string contains a valid IP address

        .DESCRIPTION
            Test if a string contains a valid IP address
            Uses regex to test
            Returns [bool]

        .PARAMETER Ip
            IP address (or subnet mask) to test is valid or not

        .PARAMETER IpOnly
            Only return True if input is valid IPv4 in quad dot format (without subnet mask)
            Eg. "127.0.0.1"
            This is default

        .PARAMETER Mask
            Only return True if input is valid IPv4 subnet mask in quad dot format
            Eg. "255.255.255.0"

        .PARAMETER AllowLength
            Used along with -Mask
            Only return true if input is subnet mask in:
            - Quad dot format, eg. "255.255.255.0"
            - Mask length (0-32), eg. "24"
            - Mask length with slash, eg. "/24"

        .PARAMETER AllowMask
            Return true if input is either
            - IP in quad dot, eg. "127.0.0.1"
            - IP + mask in quad dot, eg. "127.0.0.1 255.0.0.0"
            - IP + mask length, eg. "127.0.0.1/8"

        .PARAMETER RequireMask
            Return true if input is IP with mask, either
            - IP + mask in quad dot, eg. "127.0.0.1 255.0.0.0"
            - IP + mask length, eg. "127.0.0.1/8"

        .EXAMPLE
            Test-ValidIPv4 -Ip 127.0.0.1
            True

        .EXAMPLE
            Test-ValidIPv4 -Ip 127.0.0.256
            False

        .EXAMPLE
            Test-ValidIPv4 -Ip 127.0.0.1/32
            False

        .EXAMPLE
            Test-ValidIPv4 -Ip 127.0.0.1/32 -AllowMask
            True

        .EXAMPLE
            Test-ValidIPv4 -Ip "127.0.0.1 255.255.255.255" -AllowMask
            True

        .EXAMPLE
            Test-ValidIPv4 -Ip 127.0.0.1 -RequireMask
            False

        .EXAMPLE
            Test-ValidIPv4 -Ip 255.255.0.0 -Mask
            True

        .EXAMPLE
            Test-ValidIPv4 -Ip 255.0.255.0 -Mask
            False

        .EXAMPLE
            Test-ValidIPv4 -Ip 32 -Mask
            False

        .EXAMPLE
            Test-ValidIPv4 -Ip 32 -Mask -AllowLength
            True

        .EXAMPLE
            Test-ValidIPv4 -Ip /32 -Mask -AllowLength
            True
    #>


    [OutputType([System.Boolean])]
    [CmdletBinding(DefaultParameterSetName = 'IpOnly')]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)]
        [System.String]
        $Ip,

        [Parameter(ParameterSetName = 'IpOnly')]
        [System.Management.Automation.SwitchParameter]
        $IpOnly,

        [Parameter(Mandatory = $true, ParameterSetName = 'Mask')]
        [System.Management.Automation.SwitchParameter]
        $Mask,

        [Parameter(ParameterSetName = 'Mask')]
        [System.Management.Automation.SwitchParameter]
        $AllowLength,

        [Parameter(Mandatory = $true, ParameterSetName = 'AllowMask')]
        [System.Management.Automation.SwitchParameter]
        $AllowMask,

        [Parameter(Mandatory = $true, ParameterSetName = 'RequireMask')]
        [System.Management.Automation.SwitchParameter]
        $RequireMask
    )

    begin
    {
        Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)"
        $origErrorActionPreference = $ErrorActionPreference
        $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue')
    }

    process
    {
        Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)"

        try
        {
            # Make sure that we don't continue on error, and that we catches the error
            $ErrorActionPreference = 'Stop'

            $i = '(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
            $s = '(((255\.){3}(255|254|252|248|240|224|192|128|0+))|((255\.){2}(255|254|252|248|240|224|192|128|0+)\.0)|((255\.)(255|254|252|248|240|224|192|128|0+)(\.0+){2})|((255|254|252|248|240|224|192|128|0+)(\.0+){3}))'
            $b = '[0-9]|[12][0-9]|3[0-2]'
            $matchIp            = "^($i)$"
            $matchMask          = "^($s)$"
            $matchMaskAndLength = "^(($s)|(/?($b)))$"
            $matchAll           = "^($i)[/ ](($s)|($b))$"

            (
                ($PSCmdlet.ParameterSetName -eq 'IpOnly'      -and $Ip -match $matchIp                                  ) -or
                ($PSCmdlet.ParameterSetName -eq 'Mask'        -and $Ip -match $matchMask          -and -not $AllowLength) -or
                ($PSCmdlet.ParameterSetName -eq 'Mask'        -and $Ip -match $matchMaskAndLength -and      $AllowLength) -or
                ($PSCmdlet.ParameterSetName -eq 'AllowMask'   -and $Ip -match $matchIp                                  ) -or
                ($PSCmdlet.ParameterSetName -eq 'AllowMask'   -and $Ip -match $matchAll                                 ) -or
                ($PSCmdlet.ParameterSetName -eq 'RequireMask' -and $Ip -match $matchAll                                 )
            )
        }
        catch
        {
            # If error was encountered inside this function then stop doing more
            # But still respect the ErrorAction that comes when calling this function
            # And also return the line number where the original error occured
            $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString()
            Write-Verbose -Message "Encountered an error: $msg"
            Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg
        }
        finally
        {
            $ErrorActionPreference = $origErrorActionPreference
        }

        Write-Verbose -Message 'Process end'
    }

    end
    {
        Write-Verbose -Message 'End'
    }
}

Set-Alias -Name Convert-IP -Value Convert-IPv4Address

Set-Alias -Name Convert-IPAddress -Value Convert-IPv4Address

Set-Alias -Name Convert-IPMask -Value Convert-IPv4Mask

Set-Alias -Name Get-IPAddress -Value Get-IPv4Address

Set-Alias -Name Get-Mask -Value Get-IPv4Mask

Set-Alias -Name Get-Subnet -Value Get-IPv4Subnet

Set-Alias -Name Test-ValidIP -Value Test-ValidIPv4

Export-ModuleMember -Function Get-IPv4Mask
Export-ModuleMember -Function Get-IPv4Subnet
Export-ModuleMember -Function Test-ValidIPv4
Export-ModuleMember -Function Get-IPv4Address
Export-ModuleMember -Function Get-IPv6Address
Export-ModuleMember -Function Convert-IPv6Address
Export-ModuleMember -Function Convert-IPv4Address
Export-ModuleMember -Function Get-IPv6Subnet
Export-ModuleMember -Function Convert-IPv4Mask
Export-ModuleMember -Alias    Convert-IPMask
Export-ModuleMember -Alias    Get-IPAddress
Export-ModuleMember -Alias    Test-ValidIP
Export-ModuleMember -Alias    Get-Mask
Export-ModuleMember -Alias    Convert-IP
Export-ModuleMember -Alias    Convert-IPAddress
Export-ModuleMember -Alias    Get-Subnet