Private/Bytes.ps1

$script:LsaTypeNames = @{ 1 = 'Router'; 2 = 'Network'; 3 = 'SummaryNetwork'; 4 = 'SummaryAsbr'; 5 = 'External' }
$script:LsaTypeNumbers = @{ Router = 1; Network = 2; SummaryNetwork = 3; SummaryAsbr = 4; External = 5 }
$script:PacketTypeNames = @{ 1 = 'Hello'; 2 = 'DatabaseDescription'; 3 = 'LinkStateRequest'; 4 = 'LinkStateUpdate'; 5 = 'LinkStateAck' }
$script:AuthTypeNumbers = @{ None = 0; Simple = 1; Md5 = 2 }
$script:AuthTypeNames = @{ 0 = 'None'; 1 = 'Simple'; 2 = 'Md5' }
$script:NeighborRank = @{ 'Down' = 0; 'Init' = 1; '2-Way' = 2; 'ExStart' = 3; 'Exchange' = 4; 'Loading' = 5; 'Full' = 6 }

$script:OspfMaxAge = 3600
$script:OspfInitialSequence = [long]2147483649
$script:AllSpfRouters = [ipaddress]'224.0.0.5'
$script:AllDRouters = [ipaddress]'224.0.0.6'

function ConvertTo-OspfBytes {
    param(
        [Parameter(Mandatory, Position = 0)][long]$Value,
        [Parameter(Mandatory, Position = 1)][int]$Count
    )
    $b = [byte[]]::new($Count)
    for ($i = $Count - 1; $i -ge 0; $i--) {
        $b[$i] = [byte]($Value -band 0xFF)
        $Value = $Value -shr 8
    }
    , $b
}

function Get-OspfUInt {
    param(
        [Parameter(Mandatory, Position = 0)][byte[]]$Bytes,
        [Parameter(Mandatory, Position = 1)][int]$Offset,
        [Parameter(Mandatory, Position = 2)][int]$Count
    )
    [long]$v = 0
    for ($i = 0; $i -lt $Count; $i++) {
        $v = ($v -shl 8) -bor [long]$Bytes[$Offset + $i]
    }
    $v
}

function Get-OspfIp {
    param([byte[]]$Bytes, [int]$Offset)
    [ipaddress]::new([byte[]]$Bytes[$Offset..($Offset + 3)])
}

function ConvertTo-OspfLong {
    param([Parameter(Mandatory, Position = 0)][ipaddress]$Address)
    Get-OspfUInt -Bytes $Address.GetAddressBytes() -Offset 0 -Count 4
}

function ConvertFrom-OspfLong {
    param([Parameter(Mandatory, Position = 0)][long]$Value)
    [ipaddress]::new([byte[]](ConvertTo-OspfBytes -Value $Value -Count 4))
}

function Join-OspfBytes {
    param([Parameter(ValueFromRemainingArguments)]$Parts)
    $l = [System.Collections.Generic.List[byte]]::new()
    foreach ($p in $Parts) {
        if ($null -ne $p) { $l.AddRange([byte[]]$p) }
    }
    , $l.ToArray()
}

function ConvertTo-OspfByteList {
    param($InputObject)
    $list = [System.Collections.Generic.List[byte[]]]::new()
    if ($null -eq $InputObject) { }
    elseif ($InputObject -is [byte[]]) { $list.Add($InputObject) }
    else { foreach ($i in $InputObject) { $list.Add([byte[]]$i) } }
    , $list.ToArray()
}

function ConvertFrom-OspfPrefix {
    param([Parameter(Mandatory, Position = 0)][string]$Prefix)
    $parts = $Prefix.Split('/')
    if ($parts.Count -ne 2) { throw "Invalid prefix '$Prefix' (expected a.b.c.d/len)" }
    $len = [int]$parts[1]
    if ($len -lt 0 -or $len -gt 32) { throw "Invalid prefix length in '$Prefix'" }
    $maskLong = ([long]4294967295 -shl (32 - $len)) -band [long]4294967295
    $addr = ConvertTo-OspfLong ([ipaddress]$parts[0])
    [pscustomobject]@{
        Network      = ConvertFrom-OspfLong ($addr -band $maskLong)
        PrefixLength = $len
        Mask         = ConvertFrom-OspfLong $maskLong
        NetworkLong  = $addr -band $maskLong
        MaskLong     = $maskLong
    }
}

function Get-InternetChecksum {
    param([byte[]]$Data)
    [long]$sum = 0
    for ($i = 0; $i + 1 -lt $Data.Length; $i += 2) {
        $sum += (([long]$Data[$i] -shl 8) + [long]$Data[$i + 1])
    }
    if ($Data.Length % 2 -eq 1) { $sum += ([long]$Data[$Data.Length - 1] -shl 8) }
    while (($sum -shr 16) -ne 0) { $sum = ($sum -band 0xFFFF) + ($sum -shr 16) }
    [int]((-bnot $sum) -band 0xFFFF)
}

function Set-OspfLsaChecksum {
    # ISO 8473 Fletcher checksum (RFC 905 Annex B), computed in place; the age field is excluded.
    param([byte[]]$Lsa)
    $Lsa[16] = 0
    $Lsa[17] = 0
    $len = $Lsa.Length - 2
    $c0 = 0
    $c1 = 0
    for ($i = 2; $i -lt $Lsa.Length; $i++) {
        $c0 = ($c0 + $Lsa[$i]) % 255
        $c1 = ($c1 + $c0) % 255
    }
    $x = (($len - 15) * $c0 - $c1) % 255
    if ($x -le 0) { $x += 255 }
    $y = 510 - $c0 - $x
    if ($y -gt 255) { $y -= 255 }
    $Lsa[16] = [byte]$x
    $Lsa[17] = [byte]$y
}

function Test-OspfLsaChecksum {
    param([byte[]]$Lsa)
    if ($Lsa.Length -lt 20) { return $false }
    $c0 = 0
    $c1 = 0
    for ($i = 2; $i -lt $Lsa.Length; $i++) {
        $c0 = ($c0 + $Lsa[$i]) % 255
        $c1 = ($c1 + $c0) % 255
    }
    ($c0 -eq 0 -and $c1 -eq 0)
}