Ejemplo n.º 1
0
        public SCTPMessage(SCTPStream s, SortedArray <DataChunk> chunks)
        {
            _stream = s;
            int tot = 0;

            if ((chunks.First.getFlags() & DataChunk.BEGINFLAG) == 0)
            {
                throw new Exception("[IllegalArgumentException] must start with 'start' chunk");
            }
            if ((chunks.Last.getFlags() & DataChunk.ENDFLAG) == 0)
            {
                throw new Exception("[IllegalArgumentException] must end with 'end' chunk");
            }
            _pPid = chunks.First.getPpid();
            foreach (DataChunk dc in chunks)
            {
                tot += dc.getDataSize();
                if (_pPid != dc.getPpid())
                {
                    // aaagh
                    throw new Exception("[IllegalArgumentException] chunk has wrong ppid" + dc.getPpid() + " vs " + _pPid);
                }
            }
            _data = new byte[tot];
            int offs = 0;

            foreach (DataChunk dc in chunks)
            {
                Array.Copy(dc.getData(), 0, _data, offs, dc.getDataSize());
                offs += dc.getDataSize();
            }
        }
Ejemplo n.º 2
0
 public void closeStream(SCTPStream st)
 {
     Chunk[] cs = new Chunk[1];
     if (canSend())
     {
         //logger.LogDebug("due to reconfig stream " + st);
         cs[0] = reconfigState.makeClose(st);
         this.send(cs);
     }
 }
Ejemplo n.º 3
0
        public SCTPMessage(SCTPStream s, DataChunk singleChunk)
        {
            _stream = s;
            int flags = singleChunk.getFlags();

            if ((flags & DataChunk.SINGLEFLAG) > 0)
            {
                _data = singleChunk.getData();
                _pPid = singleChunk.getPpid();
            }
            else
            {
                throw new Exception("[IllegalArgumentException] must use a 'single' chunk");
            }
        }
Ejemplo n.º 4
0
        /* we can only demand they close their outbound streams */
        /* we can request they start to close inbound (ie ask us to shut our outbound */
        /* DCEP treats streams as bi-directional - so this is somewhat of an inpedance mis-match */
        /* resulting in a temporary 'half closed' state */
        /* mull this over.... */
        public ReConfigChunk makeClose(SCTPStream st)
        {
            ReConfigChunk ret = null;

            logger.LogDebug($"SCTP closing stream {st}");
            st.setClosing(true);
            lock (listOfStreamsToReset)
            {
                listOfStreamsToReset.Enqueue(st);
            }
            if (!timerIsRunning())
            {
                ret = makeSSNResets();
            }
            return(ret);
        }
 public void deliver(SCTPStream s, SortedArray <DataChunk> a, SCTPStreamListener l)
 {
     logger.LogDebug("in deliver() for stream " + s.getLabel() + " with " + a.Count + " chunks. ");
     // strictly this should be looking at flags etc, and bundling the result into a message
     foreach (DataChunk dc in a)
     {
         if (dc.getDCEP() != null)
         {
             logger.LogDebug("in deliver() for a DCEP message " + dc.getDataAsString());
         }
         else
         {
             logger.LogDebug("inbound data chunk is " + dc.ToString());
             l.onMessage(s, dc.getDataAsString());
         }
     }
     a.Clear();
 }
Ejemplo n.º 6
0
        // todo should be in a behave block
        // then we wouldn't be messing with stream seq numbers.

        private Chunk[] dcepDeal(SCTPStream s, DataChunk dc, DataChannelOpen dcep)
        {
            Chunk[] rep = null;
            //logger.LogDebug("dealing with a decp for stream " + dc.getDataAsString());
            if (!dcep.isAck())
            {
                //logger.LogDebug("decp is not an ack... ");

                SCTPStreamBehaviour behave = dcep.mkStreamBehaviour();
                s.setBehave(behave);
                s.setLabel(dcep.getLabel());
                lock (s)
                {
                    int seqIn = s.getNextMessageSeqIn();
                    s.setNextMessageSeqIn(seqIn + 1);
                    int seqOut = s.getNextMessageSeqOut();
                    s.setNextMessageSeqOut(seqOut + 1);
                }

                rep = new Chunk[1];
                DataChunk ack = dc.mkAck(dcep);
                s.outbound(ack);
                ack.setTsn(_nearTSN++);
                // check rollover - will break at maxint.
                rep[0] = ack;
            }
            else
            {
                //logger.LogDebug("got a dcep ack for " + s.getLabel());
                SCTPStreamBehaviour behave = dcep.mkStreamBehaviour();
                s.setBehave(behave);
                lock (s)
                {
                    int seqIn = s.getNextMessageSeqIn();
                    s.setNextMessageSeqIn(seqIn + 1);
                    int seqOut = s.getNextMessageSeqOut();
                    s.setNextMessageSeqOut(seqOut + 1);
                }
            }

            return(rep);
        }
Ejemplo n.º 7
0
        public void deliver(SCTPStream s, SortedArray <DataChunk> stash, SCTPStreamListener l)
        {
            //stash is the list of all DataChunks that have not yet been turned into whole messages
            //we assume that it is sorted by stream sequence number.
            List <DataChunk> delivered = new List <DataChunk>();

            if (stash.Count == 0)
            {
                return; // I'm not fond of these early returns
            }

            long expectedTsn = stash.First.getTsn(); // This ignores gaps - but _hopefully_ messageNo will catch any

            // gaps we care about - ie gaps in the sequence for _this_ stream
            // we can deliver ordered messages on this stream even if earlier messages from other streams are missing
            // - this does assume that the tsn's of a message are contiguous -which is odd.


            foreach (DataChunk dc in stash)
            {
                int messageNo = s.getNextMessageSeqIn();

                int  flags = dc.getFlags() & DataChunk.SINGLEFLAG; // mask to the bits we want
                long tsn   = dc.getTsn();
                bool lookingForOrderedMessages = _ordered;
                if (lookingForOrderedMessages && (tsn != expectedTsn))
                {
                    logger.LogDebug("Hole in chunk sequence  " + tsn + " expected " + expectedTsn);
                    break;
                }

                switch (flags)
                {
                case DataChunk.SINGLEFLAG:
                    if (_ordered && (messageNo != dc.getSSeqNo()))
                    {
                        logger.LogDebug("Hole (single) in message sequence  " + dc.getSSeqNo() + " expected " +
                                        messageNo);
                        break;     // not the message we are looking for...
                    }

                    SCTPMessage single = new SCTPMessage(s, dc);
                    if (single.deliver(l))
                    {
                        delivered.Add(dc);
                        messageNo++;
                        s.setNextMessageSeqIn(messageNo);
                    }

                    break;

                case DataChunk.BEGINFLAG:
                case DataChunk.ENDFLAG:
                case DataChunk.CONTINUEFLAG:
                    if (_ordered && (messageNo != dc.getSSeqNo()))
                    {
                        logger.LogDebug("Hole (single) in message sequence  " + dc.getSSeqNo() + " expected " +
                                        messageNo);
                        break;     // not the message we are looking for...
                    }

                    var orderedMessage = _orderedMessageHandler.GetMessage(dc.getSSeqNo())
                                         .Add(dc, flags);

                    if (orderedMessage.IsReady)
                    {
                        orderedMessage.Finish(() =>
                        {
                            SCTPMessage deliverable = new SCTPMessage(s, orderedMessage.ToArray());
                            if (deliverable.deliver(l))
                            {
                                _orderedMessageHandler.RemoveMessage(orderedMessage);
                                orderedMessage.AddToList(delivered);
                                messageNo++;
                                s.setNextMessageSeqIn(messageNo);
                            }
                        });
                    }

                    break;

                default:
                    throw new Exception("[IllegalStateException] Impossible value in stream logic");
                }

                expectedTsn = tsn + 1;
            }

            stash.RemoveWhere((dc) => { return(delivered.Contains(dc)); });
        }
Ejemplo n.º 8
0
 public Chunk[] respond(SCTPStream a)
 {
     return(null);
 }
Ejemplo n.º 9
0
        /*
         *
         * D) Any time a SACK arrives, the endpoint performs the following:
         *
         * ...
         *
         * ToDo :
         *
         * iii) If the SACK is missing a TSN that was previously acknowledged
         * via a Gap Ack Block (e.g., the data receiver reneged on the
         * data), then consider the corresponding DATA that might be
         * possibly missing: Count one miss indication towards Fast
         * Retransmit as described in Section 7.2.4, and if no
         * retransmit timer is running for the destination address to
         * which the DATA chunk was originally transmitted, then T3-rtx
         * is started for that destination address.
         *
         * iv) If the Cumulative TSN Ack matches or exceeds the Fast
         * Recovery exitpoint (Section 7.2.4), Fast Recovery is exited.
         *
         */

        protected override Chunk[] sackDeal(SackChunk sack)
        {
            Chunk[] ret = { };

            /*
             *           i) If Cumulative TSN Ack is less than the Cumulative TSN Ack
             *           Point, then drop the SACK.  Since Cumulative TSN Ack is
             *           monotonically increasing, a SACK whose Cumulative TSN Ack is
             *           less than the Cumulative TSN Ack Point indicates an out-of-
             *           order SACK.
             */
            if (sack.getCumuTSNAck() >= this._lastCumuTSNAck)
            {
                long ackedTo    = sack.getCumuTSNAck();
                int  totalAcked = 0;
                long now        = TimeExtension.CurrentTimeMillis();
                // interesting SACK
                // process acks
                lock (_inFlight)
                {
                    List <long> removals = new List <long>();
                    foreach (var kvp in _inFlight)
                    {
                        if (kvp.Key <= ackedTo)
                        {
                            removals.Add(kvp.Key);
                        }
                    }

                    foreach (long k in removals)
                    {
                        DataChunk d = _inFlight[k];
                        _inFlight.Remove(k);
                        totalAcked += d.getDataSize();

                        /*
                         *                       todo     IMPLEMENTATION NOTE: RTT measurements should only be made using
                         *                       a chunk with TSN r if no chunk with TSN less than or equal to r
                         *                       is retransmitted since r is first sent.
                         */
                        setRTO(now - d.getSentTime());
                        try
                        {
                            int        sid    = d.getStreamId();
                            SCTPStream stream = getStream(sid);
                            if (stream != null)
                            {
                                stream.delivered(d);
                            }

                            lock (_freeBlocks)
                            {
                                _freeBlocks.Enqueue(d);
                            }
                        }
                        catch (Exception ex)
                        {
                            logger.LogError("eek - can't replace free block on list!?!");
                            logger.LogError(ex.ToString());
                        }
                    }
                }

                /*
                 *               Gap Ack Blocks:
                 *
                 *               These fields contain the Gap Ack Blocks.  They are repeated for
                 *               each Gap Ack Block up to the number of Gap Ack Blocks defined in
                 *               the Number of Gap Ack Blocks field.  All DATA chunks with TSNs
                 *               greater than or equal to (Cumulative TSN Ack + Gap Ack Block
                 *               Start) and less than or equal to (Cumulative TSN Ack + Gap Ack
                 *               Block End) of each Gap Ack Block are assumed to have been received
                 *               correctly.
                 */
                foreach (SackChunk.GapBlock gb in sack.getGaps())
                {
                    long ts = gb.getStart() + ackedTo;
                    long te = gb.getEnd() + ackedTo;
                    lock (_inFlight)
                    {
                        for (long t = ts; t <= te; t++)
                        {
                            //logger.LogDebug("gap block says far end has seen " + t);
                            DataChunk d;
                            if (_inFlight.TryGetValue(t, out d))
                            {
                                d.setGapAck(true);
                                totalAcked += d.getDataSize();
                            }
                            else
                            {
                                logger.LogDebug("Huh? gap for something not inFlight ?!? " + t);
                            }
                        }
                    }
                }

                /*
                 *               ii) Set rwnd equal to the newly received a_rwnd minus the number
                 *               of bytes still outstanding after processing the Cumulative
                 *               TSN Ack and the Gap Ack Blocks.
                 */
                int totalDataInFlight = 0;
                lock (_inFlight)
                {
                    foreach (var kvp in _inFlight)
                    {
                        DataChunk d = kvp.Value;
                        long      k = kvp.Key;
                        if (!d.getGapAck())
                        {
                            totalDataInFlight += d.getDataSize();
                        }
                    }
                }

                _rwnd = sack.getArWin() - totalDataInFlight;
                //logger.LogDebug("Setting rwnd to " + _rwnd);
                bool advanced = (_lastCumuTSNAck < ackedTo);
                adjustCwind(advanced, totalDataInFlight, totalAcked);
                _lastCumuTSNAck = ackedTo;
            }
            else
            {
                logger.LogDebug("Dumping Sack - already seen later sack.");
            }

            return(ret);
        }
Ejemplo n.º 10
0
 public ReConfigChunk addToCloseList(SCTPStream st)
 {
     return(reconfigState.makeClose(st));
 }
Ejemplo n.º 11
0
 public SCTPMessage(string data, SCTPStream s)
 {
     _data   = (data.Length > 0) ? Encoding.UTF8.GetBytes(data) : new byte[1];
     _stream = s;
     _pPid   = (data.Length > 0) ? DataChunk.WEBRTCstring : DataChunk.WEBRTCstringEMPTY;
 }
Ejemplo n.º 12
0
 /**
  * Outbound message - note that we assume no one will mess with data between
  * calls to fill()
  *
  * @param data
  * @param s
  */
 public SCTPMessage(byte[] data, SCTPStream s)
 {
     _data   = (data.Length > 0) ? data : new byte[1];
     _stream = s;
     _pPid   = (data.Length > 0) ? DataChunk.WEBRTCBINARY : DataChunk.WEBRTCBINARYEMPTY;
 }
Ejemplo n.º 13
0
        /*
         * https://tools.ietf.org/html/rfc6525
         */
        public Chunk[] deal(ReConfigChunk rconf)
        {
            Chunk[]       ret   = new Chunk[1];
            ReConfigChunk reply = null;

            //logger.LogDebug("Got a reconfig message to deal with");
            if (haveSeen(rconf))
            {
                // if not - is this a repeat
                reply = getPrevious(rconf); // then send the same reply
            }
            if (reply == null)
            {
                // not a repeat then
                reply = new ReConfigChunk(); // create a new thing
                if (rconf.hasOutgoingReset())
                {
                    OutgoingSSNResetRequestParameter oreset = rconf.getOutgoingReset();
                    int[] streams = oreset.getStreams();
                    if (streams.Length == 0)
                    {
                        streams = assoc.allStreams();
                    }
                    if (timerIsRunning())
                    {
                        markAsAcked(rconf);
                    }
                    // if we are behind, we are supposed to wait until we catch up.
                    if (oreset.getLastAssignedTSN() > assoc.getCumAckPt())
                    {
                        //logger.LogDebug("Last assigned > farTSN " + oreset.getLastAssignedTSN() + " v " + assoc.getCumAckPt());
                        foreach (int s in streams)
                        {
                            SCTPStream defstr = assoc.getStream(s);
                            // AC: All this call did was set an unused local variable. Removed for now.
                            //defstr.setDeferred(true);
                        }
                        ReconfigurationResponseParameter rep = new ReconfigurationResponseParameter();
                        rep.setSeq(oreset.getReqSeqNo());
                        rep.setResult(ReconfigurationResponseParameter.IN_PROGRESS);
                        reply.addParam(rep);
                    }
                    else
                    {
                        // somehow invoke this when TSN catches up ?!?! ToDo
                        //logger.LogDebug("we are up-to-date ");
                        ReconfigurationResponseParameter rep = new ReconfigurationResponseParameter();
                        rep.setSeq(oreset.getReqSeqNo());
                        int result = streams.Length > 0 ? ReconfigurationResponseParameter.SUCCESS_PERFORMED : ReconfigurationResponseParameter.SUCCESS_NOTHING_TO_DO;
                        rep.setResult((uint)result); // assume all good
                        foreach (int s in streams)
                        {
                            SCTPStream cstrm = assoc.delStream(s);
                            if (cstrm == null)
                            {
                                //logger.LogError("Close a non existant stream");
                                rep.setResult(ReconfigurationResponseParameter.ERROR_WRONG_SSN);
                                break;
                                // bidriectional might be a problem here...
                            }
                            else
                            {
                                cstrm.reset();
                            }
                        }
                        reply.addParam(rep);
                    }
                }
                // ponder putting this in a second chunk ?
                if (rconf.hasIncomingReset())
                {
                    IncomingSSNResetRequestParameter ireset = rconf.getIncomingReset();

                    /*The Re-configuration
                     *                  Response Sequence Number of the Outgoing SSN Reset Request
                     *                  Parameter MUST be the Re-configuration Request Sequence Number
                     *                  of the Incoming SSN Reset Request Parameter. */
                    OutgoingSSNResetRequestParameter rep = new OutgoingSSNResetRequestParameter(nextNearNo(), ireset.getReqNo(), assoc.getNearTSN());
                    int[] streams = ireset.getStreams();
                    rep.setStreams(streams);
                    if (streams.Length == 0)
                    {
                        streams = assoc.allStreams();
                    }
                    foreach (int s in streams)
                    {
                        SCTPStream st = assoc.getStream(s);
                        if (st != null)
                        {
                            st.setClosing(true);
                        }
                    }
                    reply.addParam(rep);
                    // set outbound timer running here ???
                    //logger.LogDebug("Ireset " + ireset);
                }
            }
            if (reply.hasParam())
            {
                ret[0] = reply;
                // todo should add sack here
                //logger.LogDebug("about to reply with " + reply.ToString());
            }
            else
            {
                ret = null;
            }
            return(ret);
        }
        public void deliver(SCTPStream s, SortedArray <DataChunk> stash, SCTPStreamListener l)
        {
            //stash is the list of all DataChunks that have not yet been turned into whole messages
            //we assume that it is sorted by stream sequence number.
            List <DataChunk>        delivered = new List <DataChunk>();
            SortedArray <DataChunk> message   = null;

            if (stash.Count == 0)
            {
                return;                              // I'm not fond of these early returns
            }
            long expectedTsn = stash.First.getTsn(); // This ignores gaps - but _hopefully_ messageNo will catch any

            // gaps we care about - ie gaps in the sequence for _this_ stream
            // we can deliver ordered messages on this stream even if earlier messages from other streams are missing
            // - this does assume that the tsn's of a message are contiguous -which is odd.


            foreach (DataChunk dc in stash)
            {
                int messageNo = s.getNextMessageSeqIn();

                int  flags = dc.getFlags() & DataChunk.SINGLEFLAG; // mask to the bits we want
                long tsn   = dc.getTsn();
                bool lookingForOrderedMessages = _ordered || (message != null);
                // which is to say for unordered messages we can tolerate gaps _between_ messages
                // but not within them
                if (lookingForOrderedMessages && (tsn != expectedTsn))
                {
                    logger.LogDebug("Hole in chunk sequence  " + tsn + " expected " + expectedTsn);
                    break;
                }
                switch (flags)
                {
                case DataChunk.SINGLEFLAG:
                    // singles are easy - just dispatch.
                    if (_ordered && (messageNo != dc.getSSeqNo()))
                    {
                        logger.LogDebug("Hole (single) in message sequence  " + dc.getSSeqNo() + " expected " + messageNo);
                        break;     // not the message we are looking for...
                    }
                    SCTPMessage single = new SCTPMessage(s, dc);
                    if (single.deliver(l))
                    {
                        delivered.Add(dc);
                        messageNo++;
                        s.setNextMessageSeqIn(messageNo);
                    }
                    break;

                case DataChunk.BEGINFLAG:
                    if (_ordered && (messageNo != dc.getSSeqNo()))
                    {
                        logger.LogDebug("Hole (begin) in message sequence  " + dc.getSSeqNo() + " expected " + messageNo);
                        break;     // not the message we are looking for...
                    }
                    message = new SortedArray <DataChunk>();
                    message.Add(dc);
                    logger.LogDebug("new message no" + dc.getSSeqNo() + " starts with  " + dc.getTsn());
                    break;

                case 0:     // middle
                    if (message != null)
                    {
                        message.Add(dc);
                        logger.LogDebug("continued message no" + dc.getSSeqNo() + " with  " + dc.getTsn());
                    }
                    else
                    {
                        // perhaps check sno ?
                        logger.LogDebug("Middle with no start" + dc.getSSeqNo() + " tsn " + dc.getTsn());
                    }
                    break;

                case DataChunk.ENDFLAG:
                    if (message != null)
                    {
                        message.Add(dc);
                        logger.LogDebug("finished message no" + dc.getSSeqNo() + " with  " + dc.getTsn());
                        SCTPMessage deliverable = new SCTPMessage(s, message);
                        if (deliverable.deliver(l))
                        {
                            message.AddToList(delivered);
                            messageNo++;
                            s.setNextMessageSeqIn(messageNo);
                        }
                        message = null;
                    }
                    else
                    {
                        logger.LogDebug("End with no start" + dc.getSSeqNo() + " tsn " + dc.getTsn());
                        message = null;
                    }
                    break;

                default:
                    throw new Exception("[IllegalStateException] Impossible value in stream logic");
                }
                expectedTsn = tsn + 1;
            }
            stash.RemoveWhere((dc) => { return(delivered.Contains(dc)); });
        }
Ejemplo n.º 15
0
 public Chunk[] respond(SCTPStream a)
 {
     logger.LogDebug("in respond() for a opened stream " + a.getLabel());
     return(null);
 }