Public/New-BfdControl.ps1

function New-BfdControl {
    <#
    .SYNOPSIS
        Builds a BFD control packet (RFC 5880): 24 bytes plus an optional authentication section. Intervals are in microseconds.
    .DESCRIPTION
        Every header field can be set to an invalid value on purpose (version, length, discriminators, zero DetectMult,
        the Multipoint bit), which makes this the building block for fault injection against a peer.
    .PARAMETER Length
        Overrides the Length field (a lie about the packet size).
    .PARAMETER AuthType
        None, SimplePassword, KeyedMD5, MeticulousKeyedMD5, KeyedSHA1 or MeticulousKeyedSHA1.
    #>

    [CmdletBinding()]
    param(
        [ValidateSet('AdminDown', 'Down', 'Init', 'Up')][string]$State = 'Down',
        [ValidateRange(0, 31)][int]$Diagnostic = 0,
        [ValidateRange(0, 255)][int]$DetectMult = 3,
        [uint32]$MyDiscriminator = 1,
        [uint32]$YourDiscriminator = 0,
        [uint32]$DesiredMinTx = 1000000,
        [uint32]$RequiredMinRx = 1000000,
        [uint32]$RequiredMinEchoRx = 0,
        [switch]$Poll,
        [switch]$Final,
        [switch]$Demand,
        [switch]$ControlPlaneIndependent,
        [switch]$Multipoint,
        [ValidateRange(0, 7)][int]$Version = 1,
        [ValidateRange(0, 255)][int]$Length,
        [ValidateSet('None', 'SimplePassword', 'KeyedMD5', 'MeticulousKeyedMD5', 'KeyedSHA1', 'MeticulousKeyedSHA1')][string]$AuthType = 'None',
        [ValidateRange(0, 255)][int]$AuthKeyId = 0,
        [string]$AuthKey,
        [uint32]$AuthSequence = 0
    )
    $flags = 0
    if ($Poll) { $flags = $flags -bor 0x20 }
    if ($Final) { $flags = $flags -bor 0x10 }
    if ($ControlPlaneIndependent) { $flags = $flags -bor 0x08 }
    if ($Demand) { $flags = $flags -bor 0x02 }
    if ($Multipoint) { $flags = $flags -bor 0x01 }

    $info = $null
    $keyBytes = [byte[]]@()
    $authLen = 0
    if ($AuthType -ne 'None') {
        if (-not $AuthKey) { throw "-AuthKey is required with -AuthType $AuthType." }
        $info = $script:BfdAuthTypes[$AuthType]
        $keyBytes = [Text.Encoding]::UTF8.GetBytes($AuthKey)
        if ($keyBytes.Length -gt $info.MaxKey) { throw "-AuthKey can be at most $($info.MaxKey) bytes for $AuthType." }
        $authLen = if ($AuthType -eq 'SimplePassword') { 3 + $keyBytes.Length } else { $info.Length }
        $flags = $flags -bor 0x04
    }

    $buf = [byte[]]::new(24 + $authLen)
    $buf[0] = [byte](($Version -shl 5) -bor $Diagnostic)
    $buf[1] = [byte](($script:BfdStateCodes[$State] -shl 6) -bor $flags)
    $buf[2] = [byte]$DetectMult
    $buf[3] = if ($PSBoundParameters.ContainsKey('Length')) { [byte]$Length } else { [byte]$buf.Length }
    Set-BfdUInt32 $buf 4 $MyDiscriminator
    Set-BfdUInt32 $buf 8 $YourDiscriminator
    Set-BfdUInt32 $buf 12 $DesiredMinTx
    Set-BfdUInt32 $buf 16 $RequiredMinRx
    Set-BfdUInt32 $buf 20 $RequiredMinEchoRx

    if ($AuthType -eq 'SimplePassword') {
        $buf[24] = 1; $buf[25] = [byte]$authLen; $buf[26] = [byte]$AuthKeyId
        [Array]::Copy($keyBytes, 0, $buf, 27, $keyBytes.Length)
    }
    elseif ($info) {
        $buf[24] = [byte]$info.Code; $buf[25] = [byte]$authLen; $buf[26] = [byte]$AuthKeyId
        Set-BfdUInt32 $buf 28 $AuthSequence
        [Array]::Copy($keyBytes, 0, $buf, 32, $keyBytes.Length)
        $hash = Get-BfdHash -Algorithm $info.Hash -Data $buf
        [Array]::Copy($hash, 0, $buf, 32, $hash.Length)
    }
    , $buf
}