1

I'm trying to convert given IPv6 ranges supplied with CIDR notation into a range (first_address-last_address) in Powershell but am coming a bit unstuck.

A sample of the source data:

2a01:111:f406:c00::/64
2a01:111:f400:7c00::/54
2a01:111:f403::/48

Sample of required Output (doesn't need to be exact, i can mess with the format:

2a01:111:f406:c00::-2a01:111:f406:c00:ffff:ffff:ffff:ffff
2a01:111:f400:7c00::-2a01:111:f400:7fff:ffff:ffff:ffff:ffff
2a01:111:f403:0:0:0:0:0::-2a01:111:f403:ffff:ffff:ffff:ffff:ffff

I have googled extensively but not found anything that has pointed me in the right direction for powershell - I've found a php and js example but did't translate well.

Any pointers?

Thanks!

  • I'd recommend starting with "normalizing" the format of the CIDR given such that you have a fixed format - 'unsuppress' leading zeros, and fill out the zeros that the :: substitute for. (example: `2a01:111:f406:c00::/64` gets converted to `2a01:0111:f406:0c00:0000:0000:0000:0000/64`) Once you've got that, it should be relatively easy to work out how to achieve your purpose. – Jeff Zeitlin Feb 08 '17 at 16:23
  • Additional hint: the `-split` and `-f` operators might come in handy. – Jeff Zeitlin Feb 08 '17 at 16:31
  • @JeffZeitlin Thanks, that is a feasible solution but I am concerned about accuracy for any edge cases (there are 100+ in the list) – Stuart Grierson Feb 08 '17 at 19:48
  • If you "normalize" the addresses as suggested, there should not be any 'edge cases'. I believe that in IPv6 addresses, you can only have one instance of ::, and it's not difficult to convert the other segments to universally 4 hexdigits. – Jeff Zeitlin Feb 08 '17 at 19:53
  • Fair enough, it is likely my lack of knowledge around ipv6 addressing, time for a little more research. – Stuart Grierson Feb 08 '17 at 20:04
  • @JeffZeitlin I'm sure you had something way more graceful in mind, but with a bit of butchery and persistence and thanks to the pointers, this does the job. (can't post it here, didn't realise) – Stuart Grierson Feb 09 '17 at 15:59

3 Answers3

4

AFAIK, there's nothing in the BCL that parse IPv6 network segments - I would use the IPNetwork library (works with both IPv4 and 6):

Add-Type -Path C:\path\to\System.Net.IPNetwork.dll

# Parse ipv6/cidr string
$Network = [System.Net.IPNetwork]::Parse('2a01:111:f400:7c00::/54')

# Format string with first and last usable address
$Range = '{0}-{1}' -f $Network.FirstUsable,$Network.LastUsable
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Thanks, that does do what I am looking for, ideally I'd like not to have use an external library for portability reasons but it works pretty smoothly. – Stuart Grierson Feb 08 '17 at 19:49
  • A note for anyone else, I couldn't load the dll until I followed this: http://stackoverflow.com/questions/2094694/how-can-i-run-powershell-with-the-net-4-runtime – Stuart Grierson Feb 08 '17 at 19:57
  • Use `Unblock-File` to remove the zone ads from files you've downloaded from the Internet – Mathias R. Jessen Feb 08 '17 at 20:06
0

I ended up with this, where $ipv6cidr is an array of ipv6 addresses in CIDR format...

#convert IPv6 CIDR to IPv6 range
$ipv6range=@()
foreach($ip in $ipv6cidr){
$binip=@()
$netbits=$ip.Split("/")[1]
$ip = $ip.Split("/")[0]
$ip=$ip -replace '::',''
$ip=$ip.Split(':')
foreach($p in $ip){
$p='0x'+$p
$bin=[Convert]::ToString($p, 2).PadLeft(16,'0')
$binip+=$bin
}
$binip=$binip -join ''
If($binip.Length -lt $netbitslength)
{
$difference=$netbitslength-$binip.Length
$missing= "1" * $difference
[string]$missing=$missing
$missing=$missing.Padleft(16,'0')
$binip=$binip+$missing
$binip=$binip -replace ',',''
$binstart=$binip.PadRight(128,'0')
$binend=$binip.PadRight(128,'1')
}
elseIf($binip.Length -gt $netbitslength){
$binstart=$binip.substring(0,$netbits).padright(128,'0')
$binend= $binip.substring(0,$netbits).padright(128,'1')
}
$startbin=@()
While ($binstart)
{ 
$x,$binstart= ([char[]]$binstart).where({$_},'Split',16)
$startbin+=$x -join ''
}
$starts=@()
foreach($start in $startbin){
$start=[Convert]::ToInt32("$start",2)
$starts+='{0:X4}' -f $start
}
$finalstartip=$starts -join ':'
$endbin=@()
While ($binend)
{ 
$x,$binend= ([char[]]$binend).where({$_},'Split',16)
$endbin+=$x -join ''
}
$ends=@()
foreach($end in $endbin){
$end=[Convert]::ToInt32("$end",2)
$ends+='{0:X4}' -f $end
}
$finalendip=$ends -join ':'
$ipv6range+=$finalstartip+'-'+$finalendip
}
0
#I had Troubles so I fixed some things:
$AllAddresses = '::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'
$ipv6cidr = $_
if ($ipv6cidr -match "[0-9a-f:]+[:]" -and $_ -ne $AllAddresses) {
    $EndBinaryArray = $StartBinaryArray = $null
    $NetBits = $($ipv6cidr.Split("/").Replace('::', ''))[1]

    #Convert To Binary
    $BinaryEquivalent = $(($ipv6cidr.Split("/").Replace('::', ''))[0].Split(':').ForEach(
            {
                $Decimal = '0x' + $_
                [Convert]::ToString($([Uint32]($Decimal)), 2).PadLeft(16, '0')
            }
        ) 
    ) 

    $BitLength = $BinaryEquivalent.length * 16
    $HostId = $BinaryEquivalent -join ""
    #Adjust for NetMask
    if ($Netbits -lt $BitLength) {
        $Difference = $BitLength - $NetBits
        $HostnetworkId = $HostId -Replace ".{$Difference}$"
    }
    if ($Netbits -gt $BitLength) {
        $Difference = $Netbits - $BitLength
        $HostnetworkId = [String]::Format("$HostId{0}", $("0" * $Difference)) 
    }
    if ($Netbits -eq $BitLength) {
        $HostnetworkId = $HostId
    }
    $BinaryStart = $HostnetworkId.PadRight(128, '0')
    $BinaryEnd = $HostnetworkId.PadRight(128, '1')

    #Convert Back to Decimal then to Hex
    While ($BinaryStart) {
        $Bytes, $BinaryStart = ([char[]]$BinaryStart).where( { $_ }, 'Split', 16)
        [Array]$StartBinaryArray += $Bytes -join ''
    }
    $HexStartArray = ($StartBinaryArray.ForEach( { '{0:X4}' -f $([Convert]::ToInt32("$_", 2)) })) -join ":"

    While ($BinaryEnd) {
        $Bytes, $BinaryEnd = ([char[]]$BinaryEnd).where( { $_ }, 'Split', 16)
        [Array]$EndBinaryArray += $Bytes -join ''
    }
    $HexEndArray = ($EndBinaryArray.ForEach( { '{0:X4}' -f $([Convert]::ToInt32("$_", 2)) })) -join ":"

    "[{0}] Start: {1} End: {2}" -f $ipv6cidr, $HexStartArray, $HexEndArray
}
if ($ipv6cidr -eq $AllAddresses) {
    "[{0}] Start: {1} End: {2}" -f $ipv6cidr, '000:000:000:0000:0000:0000:0000', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'
}