Private/Invoke-BfdSessionLoop.ps1
|
function Invoke-BfdSessionLoop { param($S) $c = $S.Config $rx = $null; $tx = $null $clock = [Diagnostics.Stopwatch]::StartNew() $rand = [Random]::new() try { $bindIp = if ($c.LocalAddress) { [ipaddress]$c.LocalAddress } else { [ipaddress]::Any } $rx = [Net.Sockets.UdpClient]::new([Net.IPEndPoint]::new($bindIp, $c.ListenPort)) for ($i = 0; $i -lt 20 -and -not $tx; $i++) { try { $tx = [Net.Sockets.UdpClient]::new([Net.IPEndPoint]::new($bindIp, $rand.Next(49152, 65535))) } catch { } } if (-not $tx) { throw 'Could not bind a source port in 49152-65535.' } $tx.Client.Ttl = 255 $remote = [Net.IPEndPoint]::new([ipaddress]$c.RemoteAddress, $c.RemotePort) $myDisc = [uint32]$rand.Next(1, [int]::MaxValue) $yourDisc = [uint32]0 $fsm = @{ State = 'Down'; Diag = 0 } $remoteMult = 0; $remoteDesiredTx = 1000000; $remoteRequiredRx = 1000000 $lastRx = 0.0 $replyFinal = $false $nextTx = 0.0 $lastSeq = $null; $txSeq = [uint32]$rand.Next(1, 1000000) $authArgs = @{} $S.AuthFailures = 0 $S.State = 'Down' $S.LocalDiscriminator = $myDisc $setState = { param($new, $newDiag) if ($new -ne $fsm.State) { $S.History.Enqueue(('{0:HH:mm:ss.fff} {1} -> {2}' -f (Get-Date), $fsm.State, $new)) $dummy = $null while ($S.History.Count -gt 100) { $null = $S.History.TryDequeue([ref]$dummy) } } $fsm.State = $new; $fsm.Diag = $newDiag $S.State = $new } while (-not $S.Stop) { $now = $clock.Elapsed.TotalMilliseconds while ($rx.Available -gt 0) { $ep = [Net.IPEndPoint]::new([ipaddress]::Any, 0) $data = $rx.Receive([ref]$ep) if (-not $ep.Address.Equals($remote.Address) -or $data.Length -lt 24) { continue } $isAuth = $c.AuthType -and $c.AuthType -ne 'None' if ($isAuth) { if (-not (Test-BfdAuth -Packet $data -Type $c.AuthType -KeyId $c.AuthKeyId -Key $c.AuthKey)) { $S.AuthFailures++; continue } if ($c.AuthType -eq 'KeyedSHA1') { # sequence must not go backwards by more than 3x the detect multiplier (RFC 5880 6.7.4) $seq = [uint32](Get-NetUInt $data 28 4) if ($null -ne $lastSeq -and $fsm.State -ne 'Down' -and (([uint32]($seq - $lastSeq)) -gt (3 * [Math]::Max(1, $remoteMult)) -and ([uint32]($lastSeq - $seq)) -lt 2147483648)) { $S.AuthFailures++; continue } $lastSeq = $seq } } elseif ($data[1] -band 0x04) { $S.AuthFailures++; continue } $p = ConvertFrom-BfdControl $data if ($p.Version -ne 1 -or $p.DetectMult -eq 0 -or $p.MyDiscriminator -eq 0) { continue } if ($p.YourDiscriminator -ne 0 -and $p.YourDiscriminator -ne $myDisc) { continue } $S.Received++ $lastRx = $now $yourDisc = $p.MyDiscriminator $remoteMult = $p.DetectMult $remoteDesiredTx = $p.DesiredMinTx $remoteRequiredRx = $p.RequiredMinRx $S.RemoteState = $p.State switch ($fsm.State) { 'Down' { if ($p.State -eq 'Down') { & $setState 'Init' 0 } elseif ($p.State -eq 'Init') { & $setState 'Up' 0 } } 'Init' { if ($p.State -in 'Init', 'Up') { & $setState 'Up' 0 } } 'Up' { if ($p.State -in 'Down', 'AdminDown') { & $setState 'Down' 3 } } } if ($p.Poll) { $nextTx = 0; $replyFinal = $true } } if ($fsm.State -in 'Init', 'Up' -and $remoteMult -gt 0) { $detectMs = $remoteMult * [Math]::Max($c.RequiredMinRx, $remoteDesiredTx) / 1000.0 if (($now - $lastRx) -gt $detectMs) { & $setState 'Down' 1; $yourDisc = 0 } } if (-not $S.Suppress -and $now -ge $nextTx) { $advTx = if ($fsm.State -eq 'Up') { $c.DesiredMinTx } else { [Math]::Max($c.DesiredMinTx, 1000000) } $authArgs = if ($c.AuthType -and $c.AuthType -ne 'None') { @{ AuthType = $c.AuthType; AuthKeyId = $c.AuthKeyId; AuthKey = $c.AuthKey; AuthSequence = $txSeq } } else { @{} } $txSeq = [uint32](([uint64]$txSeq + 1) % 4294967296) $pkt = New-BfdControl -State $fsm.State -Diagnostic $fsm.Diag -DetectMult $c.DetectMult -MyDiscriminator $myDisc ` -YourDiscriminator $yourDisc -DesiredMinTx $advTx -RequiredMinRx $c.RequiredMinRx -Final:$replyFinal @authArgs $replyFinal = $false $null = $tx.Send($pkt, $pkt.Length, $remote) $S.Sent++ $intervalUs = if ($fsm.State -eq 'Up') { [Math]::Max($c.DesiredMinTx, $remoteRequiredRx) } else { [Math]::Max($c.DesiredMinTx, 1000000) } $nextTx = $now + ($intervalUs / 1000.0) * (1 - 0.25 * $rand.NextDouble()) } Start-Sleep -Milliseconds 5 } $pkt = New-BfdControl -State AdminDown -Diagnostic 7 -DetectMult $c.DetectMult -MyDiscriminator $myDisc -YourDiscriminator $yourDisc @authArgs $null = $tx.Send($pkt, $pkt.Length, $remote) } catch { $S.LastError = $_.Exception.Message } finally { if ($rx) { $rx.Dispose() } if ($tx) { $tx.Dispose() } $S.State = 'AdminDown' } } |