IPv4 Addresses and CIDR Fun

Classless Inter-Domain Routing is the current way to define a subnet.  An example entry would be “192.168.1.0/24”.  What this means is that the first 24 bits of the 32 bit IPv4 address are considered ‘off the subnet.’  The last octet is considered part of the subnet.  I.e. the computer will not route traffic through the default gateway if the destination address is on the same subnet.  In this example, all machines on 192.168.1.xyz c an talk to one another.

/24 is simple.  That’s just the last octet.  What if people want to sub divide it further?  193.168.1.128.0/25 and 192.168.1.128/25 further divide that last octect into two separate subnets.

Also, what is considered on the subnet, IPv4 address wise?  Assuming you’ve calculated the highest IP address in the range defined by the CIDR record, it’s relatively simple, at least for people.  Look at each octet, side by side.  Make sure the IPv4 address in question is at least the same, if not greater than, the start of the CIDR range.  Then compare it to the upper bound.

For computers, that could be a lot more work:

- Split the IPv4 address into the four octets.

- Compare first octet, compare second only, etc.

- Two comparisons each: the octet being compared must be greater or equal to the first entry in the range, and less than the upper entry in the range.

 <#
.synopsis
Convert an IPv4 address to a UInt64.

.description
Once an IPv4 Address is converted to a UInt64, an IP range can be added to it to determine the upper address of the range.

.parameter IpV4Address
Ipv$ address to convert to UIint 64.

.example
(Convert-IpV4AddressToUInt64 '10.8.152.0') + (Get-IpV4AddressRangeFromCidrPrefix 24) | Convert-UInt64ToIpV4Address
#>
function Convert-IpV4AddressToUInt64
{
    param (
        [parameter(mandatory=$true,valuefrompipeline=$true,helpmessage='Example: 10.8.152.0')]
        [ipAddress]$IpV4Address 
    );
    
    $bits = '';
    $IpV4Address.ToString() -split '\.' |
    % { $bits += (("0" * 8) + [convert]::ToString($_,2)) -replace '.*(.{8})$', "`$1"; }

    [convert]::ToUInt64($bits, 2)
} #>


<#
.synopsis
Convert a UInt64 back to an IPv4 address.

.description
Reverses Convert-IpV4AddresstoUInt64, after an IP range has been added.

.parameter UInt64
UInt64 to convert to IPv4 address.

.example
(Convert-IpV4AddressToUInt64 '10.8.152.0') + (Get-IpV4AddressRangeFromCidrPrefix 24) | Convert-UInt64ToIpV4Address
#>
function Convert-UInt64ToIpV4Address
{
    param (
        [parameter(mandatory=$true,valuefrompipeline=$true,helpmessage='Example: 168335360')]
        [uint64]$UInt64
    )

    if (((("0" * 31) + [convert]::ToString($UInt64, 2)) -replace '.*(.{32})$', "`$1") -match ("(.{8})" * 4))
    {
        $ipV4Addr = '';
        1 .. 4 | % { $ipV4Addr += [string]([convert]::ToInt16($matches[$_], 2)) + '.'; }
        $ipV4Addr -replace '\.$';
    }
} #>


<#
.synopsis
For given CIDR prefix, return number of IP addresses in that range.

.description
For given CIDR prefix n (such as 24), number of addresses in the range is:

(2 ^ (32 - n)) - 1

.parameter CidrPrefix
CIDR prefix. The 'n' in the notation "192.168.1.0/24".

.example
(Convert-IpV4AddressToUInt64 '10.8.152.0') + (Get-IpV4AddressRangeFromCidrPrefix 24) | Convert-UInt64ToIpV4Address
#>
function Get-IpV4AddressRangeFromCidrPrefix
{
    param (
        [validateRange(0,32)]
        [parameter(mandatory=$true,valuefrompipeline=$true,helpmessage='Example: 10.8.152.0')]
        [int]$CidrPrefix
    );

    [math]::Pow(2, (32 -$CidrPrefix)) - 1;
} #>