Private/Receive.ps1
|
function Invoke-OspfReceive { param( $Session, [byte[]]$Data, [ipaddress]$Source, [switch]$HasIpHeader, [datetime]$Now = [datetime]::UtcNow ) if ($Session.Status -ne 'Running') { return } $srcLong = ConvertTo-OspfLong $Source if ($srcLong -eq $Session.LocalAddressLong) { return } $authKey = if ($Session.AuthType -ne 'None') { $Session.AuthKey } else { '' } $msg = ConvertFrom-OspfMessage -Message $Data -HasIpHeader:$HasIpHeader -AuthKey $authKey if (-not $msg.Valid) { Add-OspfEvent -Session $Session -Direction 'Received' -Type 'Invalid' -Message "Dropped from ${Source}: $($msg.Error)" -Bytes $msg.Bytes return } $drop = { param($why) Add-OspfEvent -Session $Session -Direction 'Received' -Type $msg.TypeName -Message "Dropped from ${Source}: $why" -Neighbor $msg.RouterId.ToString() -Bytes $msg.Bytes } if ($msg.Version -ne 2) { & $drop "version $($msg.Version)"; return } if ($msg.RouterId.Equals($Session.RouterId)) { return } if (-not $msg.AreaId.Equals($Session.AreaId)) { & $drop "area mismatch ($($msg.AreaId))"; return } if ($msg.AuthType -ne $Session.AuthType) { & $drop "auth type mismatch ($($msg.AuthType))"; return } if ($msg.AuthType -eq 'Md5') { if (-not $msg.AuthValid) { & $drop 'MD5 digest invalid'; return } } else { if (-not $msg.ChecksumValid) { & $drop 'bad checksum'; return } if ($msg.AuthType -eq 'Simple' -and -not $msg.AuthValid) { & $drop 'simple password mismatch'; return } } $key = $msg.RouterId.ToString() $n = $Session.Neighbors[$key] if ($msg.AuthType -eq 'Md5' -and $n) { if ($msg.CryptoSequence -lt $n.LastCrypto) { & $drop 'MD5 sequence replay'; return } $n.LastCrypto = $msg.CryptoSequence } Add-OspfEvent -Session $Session -Direction 'Received' -Type $msg.TypeName -Message "from $Source" -Neighbor $key -Bytes $msg.Bytes if ($msg.Type -eq 1) { Receive-OspfHello $Session $msg $srcLong $Now } elseif ($null -eq $n) { & $drop 'unknown neighbor' } else { $n.Deadline = $Now.AddSeconds($Session.DeadInterval) switch ($msg.Type) { 2 { Receive-OspfDbd $Session $n $msg $Now } 3 { Receive-OspfLsr $Session $n $msg $Now } 4 { Receive-OspfLsu $Session $n $msg $Now } 5 { Receive-OspfLsAck $Session $n $msg } } } if ($Session.LsaDirty) { Update-OspfOwnLsas -Session $Session -Now $Now } } function Receive-OspfHello { param($Session, $Msg, [long]$SrcLong, [datetime]$Now) $why = $null if ($Session.NetworkType -eq 'Broadcast' -and -not $Msg.NetworkMask.Equals($Session.Mask)) { $why = "network mask mismatch ($($Msg.NetworkMask))" } elseif ($Msg.HelloInterval -ne $Session.HelloInterval) { $why = "hello interval mismatch ($($Msg.HelloInterval))" } elseif ($Msg.DeadInterval -ne $Session.DeadInterval) { $why = "dead interval mismatch ($($Msg.DeadInterval))" } elseif (($Msg.Options -band 2) -ne ($Session.Options -band 2)) { $why = "E-bit mismatch (options $($Msg.Options))" } if ($why) { Add-OspfEvent -Session $Session -Direction 'Info' -Type 'HelloMismatch' -Neighbor $Msg.RouterId.ToString() -Message "Hello rejected: $why" return } $key = $Msg.RouterId.ToString() if ($Session.HoldDown[$key] -and $Now -lt $Session.HoldDown[$key]) { return } $n = $Session.Neighbors[$key] if (-not $n) { $n = New-OspfNeighbor (ConvertTo-OspfLong $Msg.RouterId) $SrcLong $Now $Session.Neighbors[$key] = $n } $oldPrio = $n.Priority; $oldDr = $n.Dr; $oldBdr = $n.Bdr $n.Address = $SrcLong $n.Priority = $Msg.Priority $n.Dr = ConvertTo-OspfLong $Msg.DesignatedRouter $n.Bdr = ConvertTo-OspfLong $Msg.BackupDesignatedRouter $n.Options = $Msg.Options $n.Deadline = $Now.AddSeconds($Session.DeadInterval) if ($n.State -eq 'Down') { Set-OspfNeighborState $Session $n 'Init' 'HelloReceived' $Now } $seen = $false foreach ($x in $Msg.Neighbors) { if ($x.Equals($Session.RouterId)) { $seen = $true } } $elect = $false if ($seen) { if ($n.State -eq 'Init') { Set-OspfNeighborState $Session $n '2-Way' '2-WayReceived' $Now; $elect = $true } } elseif ($script:NeighborRank[$n.State] -ge 2) { Set-OspfNeighborState $Session $n 'Init' '1-WayReceived' $Now $elect = $true } if ($oldPrio -ne $n.Priority -or $oldDr -ne $n.Dr -or $oldBdr -ne $n.Bdr) { $elect = $true } $declared = ($n.Dr -eq $n.Address -and $n.Bdr -eq 0) -or $n.Bdr -eq $n.Address if ($Session.InterfaceState -eq 'Waiting') { $elect = $declared } if ($elect) { Invoke-OspfElection $Session $Now } else { Update-OspfAdjacencies $Session $Now } } function Receive-OspfDbd { param($Session, $N, $Msg, [datetime]$Now) if ($script:NeighborRank[$N.State] -lt $script:NeighborRank['ExStart']) { Add-OspfEvent -Session $Session -Direction 'Info' -Type 'Dbd' -Neighbor $N.RouterId.ToString() -Message "DBD ignored in state $($N.State)" return } if ($Msg.InterfaceMtu -gt $Session.InterfaceMtu) { Add-OspfEvent -Session $Session -Direction 'Info' -Type 'MtuMismatch' -Neighbor $N.RouterId.ToString() -Message "DBD rejected: neighbor MTU $($Msg.InterfaceMtu) > interface MTU $($Session.InterfaceMtu)" return } $recv = @{ Flags = $Msg.Flags; Options = $Msg.Options; Sequence = $Msg.Sequence } $same = $N.LastRecv -and $N.LastRecv.Flags -eq $recv.Flags -and $N.LastRecv.Options -eq $recv.Options -and $N.LastRecv.Sequence -eq $recv.Sequence if ($N.State -eq 'ExStart') { if ($Msg.Init -and $Msg.More -and $Msg.Master -and $Msg.LsaHeaders.Count -eq 0 -and $N.RouterIdLong -gt $Session.RouterIdLong) { $N.IsMaster = $false $N.DdSeq = $Msg.Sequence $N.Options = $Msg.Options $N.LastRecv = $recv Set-OspfNeighborState $Session $N 'Exchange' 'NegotiationDone (slave)' $Now Send-OspfDbdChunk $Session $N $Now if (-not $N.LastDbdMore -and -not $Msg.More) { Complete-OspfExchange $Session $N $Now } return } elseif (-not $Msg.Init -and -not $Msg.Master -and $Msg.Sequence -eq $N.DdSeq -and $N.RouterIdLong -lt $Session.RouterIdLong) { $N.IsMaster = $true $N.Options = $Msg.Options Set-OspfNeighborState $Session $N 'Exchange' 'NegotiationDone (master)' $Now $same = $false } else { return } } elseif ($same) { if (-not $N.IsMaster -and $N.LastDbdBody) { Send-OspfDbdPacket $Session $N $N.LastDbdBody $Now 'retransmit' } return } elseif ($N.State -ne 'Exchange') { Restart-OspfAdjacency $Session $N 'SeqNumberMismatch (DBD in Loading/Full)' $Now return } if ($same) { if (-not $N.IsMaster -and $N.LastDbdBody) { Send-OspfDbdPacket $Session $N $N.LastDbdBody $Now 'retransmit' } return } $expectedSeq = if ($N.IsMaster) { $N.DdSeq } else { $N.DdSeq + 1 } if ($Msg.Master -eq $N.IsMaster -or $Msg.Init -or $Msg.Options -ne $N.Options -or $Msg.Sequence -ne $expectedSeq) { Restart-OspfAdjacency $Session $N 'SeqNumberMismatch' $Now return } $N.LastRecv = $recv foreach ($h in $Msg.LsaHeaders) { if ($h.Type -lt 1 -or $h.Type -gt 5) { Restart-OspfAdjacency $Session $N "bad LSA type $($h.Type) in DBD" $Now; return } $e = $Session.Lsdb[$h.Key] $needed = $true if ($e) { $needed = (Compare-OspfLsaInstance $h (Get-OspfEntryHeader $e $Now)) -gt 0 } if ($needed) { $N.RequestList[$h.Key] = $h } } if ($N.IsMaster) { $sentMore = $N.LastDbdMore $N.DdSeq++ if (-not $sentMore -and -not $Msg.More) { Complete-OspfExchange $Session $N $Now } else { Send-OspfDbdChunk $Session $N $Now } } else { $N.DdSeq = $Msg.Sequence Send-OspfDbdChunk $Session $N $Now if (-not $N.LastDbdMore -and -not $Msg.More) { Complete-OspfExchange $Session $N $Now } } } function Receive-OspfLsr { param($Session, $N, $Msg, [datetime]$Now) if ($script:NeighborRank[$N.State] -lt $script:NeighborRank['Exchange']) { return } $lsas = [System.Collections.Generic.List[byte[]]]::new() foreach ($r in $Msg.Requests) { $k = "$($r.Type)|$($r.LinkStateId)|$($r.AdvertisingRouter)" $e = $Session.Lsdb[$k] if (-not $e) { Restart-OspfAdjacency $Session $N "BadLSReq for $k" $Now; return } $lsas.Add([byte[]](Get-OspfEntryBytes $e $Now 1)) } if ($lsas.Count -gt 0) { Send-OspfLsuTo $Session $N $lsas.ToArray() } } function Receive-OspfLsu { param($Session, $N, $Msg, [datetime]$Now) if ($script:NeighborRank[$N.State] -lt $script:NeighborRank['Exchange']) { return } $acks = [System.Collections.Generic.List[byte[]]]::new() $flood = [System.Collections.Generic.List[string]]::new() foreach ($l in $Msg.Lsas) { if (-not $l.ChecksumValid) { Add-OspfEvent -Session $Session -Direction 'Info' -Type 'Lsa' -Neighbor $N.RouterId.ToString() -Message "LSA $($l.Key) dropped: bad checksum" continue } if ($l.Type -lt 1 -or $l.Type -gt 5) { continue } $e = $Session.Lsdb[$l.Key] $cmp = if ($e) { Compare-OspfLsaInstance $l (Get-OspfEntryHeader $e $Now) } else { 1 } $hdr = [byte[]]$l.Bytes[0..19] if ($cmp -gt 0) { if ($l.AdvertisingRouter.Equals($Session.RouterId)) { if ($Session.Originated.ContainsKey($l.Key)) { Add-OspfEvent -Session $Session -Direction 'Info' -Type 'Lsa' -Message "Received newer copy of own LSA $($l.Key); advancing sequence" $Session.SeqFloor[$l.Key] = $l.Sequence $Session.LsaDirty = $true $Session.ForceOriginate = $true } else { Add-OspfEvent -Session $Session -Direction 'Info' -Type 'Lsa' -Message "Received stale own LSA $($l.Key); flushing" $null = Install-OspfLsa $Session $l.Bytes $Now Stop-OspfLsaInstance $Session $l.Key $Now } $acks.Add($hdr) continue } $others = @($Session.Neighbors.Values | Where-Object { $_.State -in 'Exchange', 'Loading' }) if ($l.Age -ge $script:OspfMaxAge -and -not $e -and $others.Count -eq 0) { $acks.Add($hdr); continue } $null = Install-OspfLsa $Session $l.Bytes $Now $acks.Add($hdr) $flood.Add($l.Key) $N.Retrans.Remove($l.Key) if ($N.RequestList.ContainsKey($l.Key)) { $N.RequestList.Remove($l.Key) } } elseif ($cmp -eq 0) { if ($N.Retrans.ContainsKey($l.Key)) { $N.Retrans.Remove($l.Key) } else { $acks.Add($hdr) } if ($N.RequestList.ContainsKey($l.Key)) { $N.RequestList.Remove($l.Key) } } else { if ($N.RequestList.ContainsKey($l.Key)) { continue } Send-OspfLsuTo $Session $N @(, [byte[]](Get-OspfEntryBytes $e $Now 1)) } } if ($acks.Count -gt 0) { Send-OspfAck $Session $N $acks.ToArray() } if ($flood.Count -gt 0) { Send-OspfFlood $Session $flood.ToArray() $Now $N } if ($N.State -eq 'Loading') { if ($N.RequestList.Count -eq 0) { Set-OspfNeighborState $Session $N 'Full' 'LoadingDone' $Now } else { Send-OspfLsrTo $Session $N $Now } } } function Receive-OspfLsAck { param($Session, $N, $Msg) foreach ($h in $Msg.LsaHeaders) { $r = $N.Retrans[$h.Key] if ($r -and $r.Sequence -eq $h.Sequence) { $N.Retrans.Remove($h.Key) } } } function Stop-OspfLsaInstance { # Premature aging: flood a MaxAge copy of an installed LSA. param($Session, [string]$Key, [datetime]$Now) $e = $Session.Lsdb[$Key] if (-not $e) { return } $e.Age0 = $script:OspfMaxAge $e.InstallTime = $Now $e.Originated = $false $Session.Originated.Remove($Key) Send-OspfFlood $Session @($Key) $Now } function Invoke-OspfTick { param($Session, [datetime]$Now = [datetime]::UtcNow) if ($Session.Status -ne 'Running') { return } if ($Session.Bfd -and $Session.Bfd.State -eq 'Down' -and $Session.Bfd.WasUp) { foreach ($n in @($Session.Neighbors.Values)) { if ($n.Address -eq (ConvertTo-OspfLong $Session.Bfd.Peer) -and $n.State -ne 'Down') { Add-OspfEvent -Session $Session -Direction 'Info' -Type 'Bfd' -Neighbor $n.RouterId.ToString() -Message 'BFD session Down: killing neighbor' Remove-OspfNeighbor $Session $n 'BFD Down' $Now } } $Session.Bfd.WasUp = $false } if ($Session.InterfaceState -eq 'Waiting' -and $Now -ge $Session.WaitDeadline) { Add-OspfEvent -Session $Session -Direction 'Info' -Type 'WaitTimer' -Message 'Wait timer expired' Invoke-OspfElection $Session $Now } foreach ($n in @($Session.Neighbors.Values)) { if ($Now -ge $n.Deadline) { Remove-OspfNeighbor $Session $n 'InactivityTimer' $Now } } if ($Now -ge $Session.NextHello) { if (-not $Session.SuppressHellos) { Send-OspfHello $Session } $Session.NextHello = $Now.AddSeconds($Session.HelloInterval) } $rx = $Session.RxmtInterval foreach ($n in @($Session.Neighbors.Values)) { if ($n.State -eq 'ExStart' -and ($Now - $n.LastDbdTime).TotalSeconds -ge $rx) { Send-OspfInitialDbd $Session $n $Now } elseif ($n.State -eq 'Exchange' -and $n.IsMaster -and $n.LastDbdBody -and ($Now - $n.LastDbdTime).TotalSeconds -ge $rx) { Send-OspfDbdPacket $Session $n $n.LastDbdBody $Now 'retransmit' } elseif ($n.State -eq 'Loading' -and ($Now - $n.LastLsrTime).TotalSeconds -ge $rx) { Send-OspfLsrTo $Session $n $Now } $due = @($n.Retrans.Keys | Where-Object { ($Now - $n.Retrans[$_].Time).TotalSeconds -ge $rx }) if ($due.Count -gt 0) { $lsas = [System.Collections.Generic.List[byte[]]]::new() foreach ($k in $due) { $e = $Session.Lsdb[$k] if ($e) { $n.Retrans[$k].Time = $Now; $lsas.Add([byte[]](Get-OspfEntryBytes $e $Now 1)) } else { $n.Retrans.Remove($k) } } if ($lsas.Count -gt 0) { Send-OspfLsuTo $Session $n $lsas.ToArray() } } } if (($Now - $Session.LastRefresh).TotalSeconds -ge 1800) { $Session.ForceOriginate = $true; $Session.LsaDirty = $true } foreach ($k in @($Session.Lsdb.Keys)) { $e = $Session.Lsdb[$k] if ((Get-OspfLsaAge $e $Now) -ge $script:OspfMaxAge -and ($Now - $e.InstallTime).TotalSeconds -ge 10) { $held = @($Session.Neighbors.Values | Where-Object { $_.Retrans.ContainsKey($k) }) if ($held.Count -eq 0) { $Session.Lsdb.Remove($k) } } } if ($Session.LsaDirty) { Update-OspfOwnLsas -Session $Session -Now $Now } } function Remove-OspfNeighbor { param($Session, $Neighbor, [string]$Reason, [datetime]$Now) $wasRank = $script:NeighborRank[$Neighbor.State] Set-OspfNeighborState $Session $Neighbor 'Down' $Reason $Now $Session.Neighbors.Remove($Neighbor.RouterId.ToString()) if ($Session.NetworkType -eq 'Broadcast' -and $wasRank -ge 2) { if ($Neighbor.Address -eq $Session.Dr) { $Session.Dr = [long]0 } if ($Neighbor.Address -eq $Session.Bdr) { $Session.Bdr = [long]0 } Invoke-OspfElection $Session $Now } $Session.LsaDirty = $true } |