示例#1
0
        /// <summary>
        /// Processes a data frame that is now ready and that is part of an SCTP stream.
        /// Stream frames must be delivered in order.
        /// </summary>
        /// <param name="frame">The data frame that became ready from the latest DATA chunk receive.</param>
        /// <returns>A sorted list of frames for the matching stream ID. Will be empty
        /// if the supplied frame is out of order for its stream.</returns>
        private List <SctpDataFrame> ProcessStreamFrame(SctpDataFrame frame)
        {
            // Relying on unsigned short wrapping.
            unchecked
            {
                // This is a stream chunk. Need to ensure in order delivery.
                var sortedFrames = new List <SctpDataFrame>();

                if (!_streamLatestSeqNums.ContainsKey(frame.StreamID))
                {
                    // First frame for this stream.
                    _streamLatestSeqNums.Add(frame.StreamID, frame.StreamSeqNum);
                    sortedFrames.Add(frame);
                }
                else if ((ushort)(_streamLatestSeqNums[frame.StreamID] + 1) == frame.StreamSeqNum)
                {
                    // Expected seqnum for stream.
                    _streamLatestSeqNums[frame.StreamID] = frame.StreamSeqNum;
                    sortedFrames.Add(frame);

                    // There could also be out of order frames that can now be delivered.
                    if (_streamOutOfOrderFrames.ContainsKey(frame.StreamID) &&
                        _streamOutOfOrderFrames[frame.StreamID].Count > 0)
                    {
                        var outOfOrder = _streamOutOfOrderFrames[frame.StreamID];

                        ushort nextSeqnum = (ushort)(_streamLatestSeqNums[frame.StreamID] + 1);
                        while (outOfOrder.ContainsKey(nextSeqnum) &&
                               outOfOrder.TryGetValue(nextSeqnum, out var nextFrame))
                        {
                            sortedFrames.Add(nextFrame);
                            _streamLatestSeqNums[frame.StreamID] = nextSeqnum;
                            outOfOrder.Remove(nextSeqnum);
                            nextSeqnum++;
                        }
                    }
                }
                else
                {
                    // Stream seqnum is out of order.
                    if (!_streamOutOfOrderFrames.ContainsKey(frame.StreamID))
                    {
                        _streamOutOfOrderFrames[frame.StreamID] = new Dictionary <ushort, SctpDataFrame>();
                    }

                    if (_streamOutOfOrderFrames[frame.StreamID].Count > MAXIMUM_OUTOFORDER_FRAMES)
                    {
                        logger.LogWarning($"SCTP data receiver exceeded the maximum queue size for out of order frames for stream ID {frame.StreamID}.");
                        // TODO https://tools.ietf.org/html/rfc4960#section-6.2 says to drop the chunk with the highest TSN that's ahead of the
                        // ack'ed TSN.
                    }
                    else
                    {
                        _streamOutOfOrderFrames[frame.StreamID].Add(frame.StreamSeqNum, frame);
                    }
                }

                return(sortedFrames);
            }
        }
示例#2
0
        /// <summary>
        /// Extracts a fragmented chunk from the receive dictionary and passes it to the ULP.
        /// </summary>
        /// <param name="fragments">The dictionary containing the chunk fragments.</param>
        /// <param name="beginTSN">The beginning TSN for the fragment.</param>
        /// <param name="endTSN">The end TSN for the fragment.</param>
        private SctpDataFrame GetFragmentedChunk(Dictionary <uint, SctpDataChunk> fragments, uint beginTSN, uint endTSN)
        {
            unchecked
            {
                byte[] full       = new byte[MAX_FRAME_SIZE];
                int    posn       = 0;
                var    beginChunk = fragments[beginTSN];
                var    frame      = new SctpDataFrame(beginChunk.Unordered, beginChunk.StreamID, beginChunk.StreamSeqNum, beginChunk.PPID, full);

                uint afterEndTSN = endTSN + 1;
                uint tsn         = beginTSN;

                while (tsn != afterEndTSN)
                {
                    var fragment = fragments[tsn].UserData;
                    Buffer.BlockCopy(fragment, 0, full, posn, fragment.Length);
                    posn += fragment.Length;
                    fragments.Remove(tsn);
                    tsn++;
                }

                frame.UserData = frame.UserData.Take(posn).ToArray();

                return(frame);
            }
        }
        /// <summary>
        /// Event handler for a DATA chunk being received. The chunk can be either a DCEP message or data channel data
        /// payload.
        /// </summary>
        /// <param name="dataFrame">The received data frame which could represent one or more chunks depending
        /// on fragmentation..</param>
        private void OnDataFrameReceived(SctpDataFrame dataFrame)
        {
            switch (dataFrame)
            {
            case var frame when frame.PPID == (uint)DataChannelPayloadProtocols.WebRTC_DCEP:
                switch (frame.UserData[0])
                {
                case (byte)DataChannelMessageTypes.ACK:
                    OnDataChannelOpened?.Invoke(frame.StreamID);
                    break;

                case (byte)DataChannelMessageTypes.OPEN:
                    var dcepOpen = DataChannelOpenMessage.Parse(frame.UserData, 0);

                    logger.LogDebug($"DCEP OPEN channel type {dcepOpen.ChannelType}, priority {dcepOpen.Priority}, " +
                                    $"reliability {dcepOpen.Reliability}, label {dcepOpen.Label}, protocol {dcepOpen.Protocol}.");

                    DataChannelTypes channelType = DataChannelTypes.DATA_CHANNEL_RELIABLE;
                    if (Enum.IsDefined(typeof(DataChannelTypes), dcepOpen.ChannelType))
                    {
                        channelType = (DataChannelTypes)dcepOpen.ChannelType;
                    }
                    else
                    {
                        logger.LogWarning($"DECP OPEN channel type of {dcepOpen.ChannelType} not recognised, defaulting to {channelType}.");
                    }

                    OnNewDataChannel?.Invoke(
                        frame.StreamID,
                        channelType,
                        dcepOpen.Priority,
                        dcepOpen.Reliability,
                        dcepOpen.Label,
                        dcepOpen.Protocol);

                    break;

                default:
                    logger.LogWarning($"DCEP message type {frame.UserData[0]} not recognised, ignoring.");
                    break;
                }
                break;

            default:
                OnDataChannelData?.Invoke(dataFrame);
                break;
            }
        }
示例#4
0
        /// <summary>
        /// Handler for processing new data chunks.
        /// </summary>
        /// <param name="dataChunk">The newly received data chunk.</param>
        /// <returns>If the received chunk resulted in a full chunk becoming available one
        /// or more new frames will be returned otherwise an empty frame is returned. Multiple
        /// frames may be returned if this chunk is part of a stream and was received out
        /// or order. For unordered chunks the list will always have a single entry.</returns>
        public List <SctpDataFrame> OnDataChunk(SctpDataChunk dataChunk)
        {
            var sortedFrames = new List <SctpDataFrame>();
            var frame        = SctpDataFrame.Empty;

            if (_inOrderReceiveCount == 0 &&
                GetDistance(_initialTSN, dataChunk.TSN) > _windowSize)
            {
                logger.LogWarning($"SCTP data receiver received a data chunk with a {dataChunk.TSN} " +
                                  $"TSN when the initial TSN was {_initialTSN} and a " +
                                  $"window size of {_windowSize}, ignoring.");
            }
            else if (_inOrderReceiveCount > 0 &&
                     GetDistance(_lastInOrderTSN, dataChunk.TSN) > _windowSize)
            {
                logger.LogWarning($"SCTP data receiver received a data chunk with a {dataChunk.TSN} " +
                                  $"TSN when the expected TSN was {_lastInOrderTSN + 1} and a " +
                                  $"window size of {_windowSize}, ignoring.");
            }
            else if (_inOrderReceiveCount > 0 &&
                     !IsNewer(_lastInOrderTSN, dataChunk.TSN))
            {
                logger.LogWarning($"SCTP data receiver received an old data chunk with {dataChunk.TSN} " +
                                  $"TSN when the expected TSN was {_lastInOrderTSN + 1}, ignoring.");
            }
            else if (!_forwardTSN.ContainsKey(dataChunk.TSN))
            {
                logger.LogTrace($"SCTP receiver got data chunk with TSN {dataChunk.TSN}, " +
                                $"last in order TSN {_lastInOrderTSN}, in order receive count {_inOrderReceiveCount}.");

                // Relying on unsigned integer wrapping.
                unchecked
                {
                    if ((_inOrderReceiveCount > 0 && _lastInOrderTSN + 1 == dataChunk.TSN) ||
                        (_inOrderReceiveCount == 0 && dataChunk.TSN == _initialTSN))
                    {
                        _inOrderReceiveCount++;
                        _lastInOrderTSN = dataChunk.TSN;

                        // See if the in order TSN can be bumped using any out of order chunks
                        // already received.
                        if (_inOrderReceiveCount > 0 && _forwardTSN.Count > 0)
                        {
                            while (_forwardTSN.ContainsKey(_lastInOrderTSN + 1))
                            {
                                _lastInOrderTSN++;
                                _inOrderReceiveCount++;
                                _forwardTSN.Remove(_lastInOrderTSN);
                            }
                        }
                    }
                    else
                    {
                        _forwardTSN.Add(dataChunk.TSN, 1);
                    }
                }

                // Now go about processing the data chunk.
                if (dataChunk.Begining && dataChunk.Ending)
                {
                    // Single packet chunk.
                    frame = new SctpDataFrame(
                        dataChunk.Unordered,
                        dataChunk.StreamID,
                        dataChunk.StreamSeqNum,
                        dataChunk.PPID,
                        dataChunk.UserData);
                }
                else
                {
                    // This is a data chunk fragment.
                    _fragmentedChunks.Add(dataChunk.TSN, dataChunk);
                    (var begin, var end) = GetChunkBeginAndEnd(_fragmentedChunks, dataChunk.TSN);

                    if (begin != null && end != null)
                    {
                        frame = GetFragmentedChunk(_fragmentedChunks, begin.Value, end.Value);
                    }
                }
            }
            else
            {
                logger.LogTrace($"SCTP duplicate TSN received for {dataChunk.TSN}.");
                if (!_duplicateTSN.ContainsKey(dataChunk.TSN))
                {
                    _duplicateTSN.Add(dataChunk.TSN, 1);
                }
                else
                {
                    _duplicateTSN[dataChunk.TSN] = _duplicateTSN[dataChunk.TSN] + 1;
                }
            }

            if (!frame.IsEmpty() && !dataChunk.Unordered)
            {
                return(ProcessStreamFrame(frame));
            }
            else
            {
                if (!frame.IsEmpty())
                {
                    sortedFrames.Add(frame);
                }

                return(sortedFrames);
            }
        }