Esempio n. 1
0
        /// <summary>
        /// Updates the sender state for the gap reports received in a SACH chunk from the
        /// remote peer.
        /// </summary>
        /// <param name="sackGapBlocks">The gap reports from the remote peer.</param>
        /// <param name="maxTSNDistance">The maximum distance any valid TSN should be from the current
        /// ACK'ed TSN. If this distance gets exceeded by a gap report then it's likely something has been
        /// miscalculated.</param>
        private void ProcessGapReports(List <SctpTsnGapBlock> sackGapBlocks, uint maxTSNDistance)
        {
            uint lastAckTSN = _cumulativeAckTSN;

            foreach (var gapBlock in sackGapBlocks)
            {
                uint goodTSNStart = _cumulativeAckTSN + gapBlock.Start;

                if (SctpDataReceiver.GetDistance(lastAckTSN, goodTSNStart) > maxTSNDistance)
                {
                    logger.LogWarning($"SCTP SACK gap report had a start TSN of {goodTSNStart} too distant from last good TSN {lastAckTSN}, ignoring rest of SACK.");
                    break;
                }
                else if (!SctpDataReceiver.IsNewer(lastAckTSN, goodTSNStart))
                {
                    logger.LogWarning($"SCTP SACK gap report had a start TSN of {goodTSNStart} behind last good TSN {lastAckTSN}, ignoring rest of SACK.");
                    break;
                }
                else
                {
                    uint missingTSN = lastAckTSN + 1;

                    logger.LogTrace($"SCTP SACK gap report start TSN {goodTSNStart} gap report end TSN {_cumulativeAckTSN + gapBlock.End} " +
                                    $"first missing TSN {missingTSN}.");

                    while (missingTSN != goodTSNStart)
                    {
                        if (!_missingChunks.ContainsKey(missingTSN))
                        {
                            if (!_unconfirmedChunks.ContainsKey(missingTSN))
                            {
                                // What to do? Can't retransmit a chunk that's no longer available.
                                // Hope it's a transient error from a duplicate or out of order SACK.
                                // TODO: Maybe keep count of how many time this occurs and send an ABORT if it
                                // gets to a certain threshold.
                                logger.LogWarning($"SCTP SACK gap report reported missing TSN of {missingTSN} but no matching unconfirmed chunk available.");
                                break;
                            }
                            else
                            {
                                logger.LogTrace($"SCTP SACK gap adding retransmit entry for TSN {missingTSN}.");
                                _missingChunks.TryAdd(missingTSN, 0);
                            }
                        }

                        missingTSN++;
                    }
                }

                lastAckTSN = _cumulativeAckTSN + gapBlock.End;
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Handler for SACK chunks received from the remote peer.
        /// </summary>
        /// <param name="sack">The SACK chunk from the remote peer.</param>
        public void GotSack(SctpSackChunk sack)
        {
            if (sack != null)
            {
                _inRetransmitMode = false;

                unchecked
                {
                    uint maxTSNDistance    = SctpDataReceiver.GetDistance(_cumulativeAckTSN, TSN);
                    bool processGapReports = true;

                    if (_unconfirmedChunks.TryGetValue(sack.CumulativeTsnAck, out var result))
                    {
                        _lastAckedDataChunkSize = result.UserData.Length;
                    }

                    if (!_gotFirstSACK)
                    {
                        if (SctpDataReceiver.GetDistance(_initialTSN, sack.CumulativeTsnAck) < maxTSNDistance &&
                            SctpDataReceiver.IsNewerOrEqual(_initialTSN, sack.CumulativeTsnAck))
                        {
                            logger.LogTrace($"SCTP first SACK remote peer TSN ACK {sack.CumulativeTsnAck} next sender TSN {TSN}, arwnd {sack.ARwnd} (gap reports {sack.GapAckBlocks.Count}).");
                            _gotFirstSACK     = true;
                            _cumulativeAckTSN = _initialTSN;
                            RemoveAckedUnconfirmedChunks(sack.CumulativeTsnAck);
                        }
                    }
                    else
                    {
                        if (_cumulativeAckTSN != sack.CumulativeTsnAck)
                        {
                            if (SctpDataReceiver.GetDistance(_cumulativeAckTSN, sack.CumulativeTsnAck) > maxTSNDistance)
                            {
                                logger.LogWarning($"SCTP SACK TSN from remote peer of {sack.CumulativeTsnAck} was too distant from the expected {_cumulativeAckTSN}, ignoring.");
                                processGapReports = false;
                            }
                            else if (!SctpDataReceiver.IsNewer(_cumulativeAckTSN, sack.CumulativeTsnAck))
                            {
                                logger.LogWarning($"SCTP SACK TSN from remote peer of {sack.CumulativeTsnAck} was behind expected {_cumulativeAckTSN}, ignoring.");
                                processGapReports = false;
                            }
                            else
                            {
                                logger.LogTrace($"SCTP SACK remote peer TSN ACK {sack.CumulativeTsnAck}, next sender TSN {TSN}, arwnd {sack.ARwnd} (gap reports {sack.GapAckBlocks.Count}).");
                                RemoveAckedUnconfirmedChunks(sack.CumulativeTsnAck);
                            }
                        }
                        else
                        {
                            logger.LogTrace($"SCTP SACK remote peer TSN ACK no change {_cumulativeAckTSN}, next sender TSN {TSN}, arwnd {sack.ARwnd} (gap reports {sack.GapAckBlocks.Count}).");
                            RemoveAckedUnconfirmedChunks(sack.CumulativeTsnAck);
                        }
                    }

                    // Check gap reports. Only process them if the cumulative ACK TSN was acceptable.
                    if (processGapReports && sack.GapAckBlocks.Count > 0)
                    {
                        ProcessGapReports(sack.GapAckBlocks, maxTSNDistance);
                    }
                }

                _receiverWindow   = CalculateReceiverWindow(sack.ARwnd);
                _congestionWindow = CalculateCongestionWindow(_lastAckedDataChunkSize);

                // SACK's will normally allow more data to be sent.
                _senderMre.Set();
            }
        }