Ejemplo n.º 1
0
        /// <summary>
        /// Parses the SACK chunk fields.
        /// </summary>
        /// <param name="buffer">The buffer holding the serialised chunk.</param>
        /// <param name="posn">The position to start parsing at.</param>
        public static SctpSackChunk ParseChunk(byte[] buffer, int posn)
        {
            var    sackChunk = new SctpSackChunk();
            ushort chunkLen  = sackChunk.ParseFirstWord(buffer, posn);

            ushort startPosn = (ushort)(posn + SCTP_CHUNK_HEADER_LENGTH);

            sackChunk.CumulativeTsnAck = NetConvert.ParseUInt32(buffer, startPosn);
            sackChunk.ARwnd            = NetConvert.ParseUInt32(buffer, startPosn + 4);
            ushort numGapAckBlocks  = NetConvert.ParseUInt16(buffer, startPosn + 8);
            ushort numDuplicateTSNs = NetConvert.ParseUInt16(buffer, startPosn + 10);

            int reportPosn = startPosn + FIXED_PARAMETERS_LENGTH;

            for (int i = 0; i < numGapAckBlocks; i++)
            {
                ushort start = NetConvert.ParseUInt16(buffer, reportPosn);
                ushort end   = NetConvert.ParseUInt16(buffer, reportPosn + 2);
                sackChunk.GapAckBlocks.Add(new SctpTsnGapBlock {
                    Start = start, End = end
                });
                reportPosn += GAP_REPORT_LENGTH;
            }

            for (int j = 0; j < numDuplicateTSNs; j++)
            {
                sackChunk.DuplicateTSN.Add(NetConvert.ParseUInt32(buffer, reportPosn));
                reportPosn += DUPLICATE_TSN_LENGTH;
            }

            return(sackChunk);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Parses an SCTP chunk from a buffer.
        /// </summary>
        /// <param name="buffer">The buffer holding the serialised chunk.</param>
        /// <param name="posn">The position to start parsing at.</param>
        /// <returns>An SCTP chunk instance.</returns>
        public static SctpChunk Parse(byte[] buffer, int posn)
        {
            if (buffer.Length < posn + SCTP_CHUNK_HEADER_LENGTH)
            {
                throw new ApplicationException("Buffer did not contain the minimum of bytes for an SCTP chunk.");
            }

            byte chunkType = buffer[posn];

            if (Enum.IsDefined(typeof(SctpChunkType), chunkType))
            {
                switch ((SctpChunkType)chunkType)
                {
                case SctpChunkType.ABORT:
                    return(SctpAbortChunk.ParseChunk(buffer, posn, true));

                case SctpChunkType.DATA:
                    return(SctpDataChunk.ParseChunk(buffer, posn));

                case SctpChunkType.ERROR:
                    return(SctpErrorChunk.ParseChunk(buffer, posn, false));

                case SctpChunkType.SACK:
                    return(SctpSackChunk.ParseChunk(buffer, posn));

                case SctpChunkType.COOKIE_ACK:
                case SctpChunkType.COOKIE_ECHO:
                case SctpChunkType.HEARTBEAT:
                case SctpChunkType.HEARTBEAT_ACK:
                case SctpChunkType.SHUTDOWN_ACK:
                case SctpChunkType.SHUTDOWN_COMPLETE:
                    return(ParseBaseChunk(buffer, posn));

                case SctpChunkType.INIT:
                case SctpChunkType.INIT_ACK:
                    return(SctpInitChunk.ParseChunk(buffer, posn));

                case SctpChunkType.SHUTDOWN:
                    return(SctpShutdownChunk.ParseChunk(buffer, posn));

                default:
                    logger.LogDebug($"TODO: Implement parsing logic for well known chunk type {(SctpChunkType)chunkType}.");
                    return(ParseBaseChunk(buffer, posn));
                }
            }

            // Shouldn't reach this point. The SCTP packet parsing logic checks if the chunk is
            // recognised before attempting to parse it.
            throw new ApplicationException($"SCTP chunk type of {chunkType} was not recognised.");
        }
Ejemplo n.º 3
0
 /// <summary>
 /// Gets a SACK chunk that represents the current state of the receiver.
 /// </summary>
 /// <returns>A SACK chunk that can be sent to the remote peer to update the ACK TSN and
 /// request a retransmit of any missing DATA chunks.</returns>
 public SctpSackChunk GetSackChunk()
 {
     // Can't create a SACK until the initial DATA chunk has been received.
     if (_inOrderReceiveCount > 0)
     {
         SctpSackChunk sack = new SctpSackChunk(_lastInOrderTSN, _receiveWindow);
         sack.GapAckBlocks = GetForwardTSNGaps();
         sack.DuplicateTSN = _duplicateTSN.Keys.ToList();
         return(sack);
     }
     else
     {
         return(null);
     }
 }
Ejemplo n.º 4
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();
            }
        }