Public/Send-BgpUpdate.ps1

function Send-BgpUpdate {
    <#
    .SYNOPSIS
        Advertises and/or withdraws IPv4 or IPv6 prefixes on an Established peer.
    .DESCRIPTION
        Without -AsPath the local AS is used. Without -NextHop the local socket address is used.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$Name,
        [string[]]$Nlri = @(),
        [string[]]$Withdrawn = @(),
        [ipaddress]$NextHop,
        [uint32[]]$AsPath,
        [ValidateSet('IGP', 'EGP', 'Incomplete')][string]$Origin = 'IGP',
        [uint32]$LocalPref,
        [uint32]$Med,
        [string[]]$Community = @()
    )
    $S = (Get-PowerBgpRegistry).Bgp[$Name]
    if (-not $S) { throw "No such peer '$Name'." }
    if ($S.State -ne 'Established') { throw "Peer '$Name' is $($S.State), not Established." }

    $p = @{} + $PSBoundParameters
    $p.Remove('Name')
    $dummy = $null
    $chunk = 400
    $isV6 = { param($x) "$x" -match ':' }
    $local = [ipaddress]$S.LocalAddress
    $families = @(
        @{ Nlri = @($Nlri | Where-Object { -not (& $isV6 $_) }); Withdrawn = @($Withdrawn | Where-Object { -not (& $isV6 $_) }); V6 = $false; Chunk = 400 }
        @{ Nlri = @($Nlri | Where-Object { & $isV6 $_ }); Withdrawn = @($Withdrawn | Where-Object { & $isV6 $_ }); V6 = $true; Chunk = 150 }
    )
    foreach ($f in $families) {
        $nl = $f.Nlri; $wd = $f.Withdrawn
        if (-not ($nl.Count -or $wd.Count)) { continue }
        $fp = $p.Clone()
        if ($nl.Count) {
            $peerAs = if ($S.PeerOpen) { $S.PeerOpen.Asn } else { $S.Config.RemoteAS }
            $ibgp = $peerAs -and [uint32]$peerAs -eq [uint32]$S.Config.LocalAS
            # iBGP (RFC 4271 5.1.2): no AS prepend, and LOCAL_PREF is carried (default 100)
            if (-not $fp.ContainsKey('AsPath')) { $fp.AsPath = if ($ibgp) { [uint32[]]@() } else { [uint32[]]@($S.Config.LocalAS) } }
            if ($ibgp -and -not $fp.ContainsKey('LocalPref')) { $fp.LocalPref = [uint32]100 }
            if ($fp.ContainsKey('NextHop')) {
                $isV6Hop = $fp.NextHop.AddressFamily -eq 'InterNetworkV6'
                if ($isV6Hop -ne $f.V6) { throw "-NextHop must be an $(if ($f.V6) { 'IPv6' } else { 'IPv4' }) address to advertise $(if ($f.V6) { 'IPv6' } else { 'IPv4' }) prefixes." }
            }
            elseif ($f.V6) {
                if ($local.AddressFamily -ne 'InterNetworkV6' -or $local.IsIPv4MappedToIPv6) { throw 'The session runs over IPv4, so -NextHop (an IPv6 address) is required to advertise IPv6 prefixes.' }
                $fp.NextHop = $local
            }
            else { $fp.NextHop = $local.MapToIPv4() }
        }
        $batches = [Math]::Max(1, [Math]::Max([Math]::Ceiling($nl.Count / $f.Chunk), [Math]::Ceiling($wd.Count / $f.Chunk)))
        for ($i = 0; $i -lt $batches; $i++) {
            $q = $fp.Clone()
            $q.Nlri = @($nl | Select-Object -Skip ($i * $f.Chunk) -First $f.Chunk)
            $q.Withdrawn = @($wd | Select-Object -Skip ($i * $f.Chunk) -First $f.Chunk)
            $S.Outbound.Enqueue([byte[]](New-BgpUpdate @q))
        }
        foreach ($w in $wd) { $null = $S.Advertised.TryRemove($w, [ref]$dummy) }
        foreach ($n in $nl) { $S.Advertised[$n] = [pscustomobject]@{ Peer = $Name; Prefix = $n; NextHop = "$($fp.NextHop)"; AsPath = ($fp.AsPath -join ' '); Params = $fp } }
    }
}