private Chunk[] dataDeal(DataChunk dc) { List <Chunk> rep = new List <Chunk>(); List <uint> duplicates = new List <uint>(); uint tsn = dc.getTsn(); if (tsn > _farTSN) { // put it in the pen. //logger.LogDebug("TSN:::" + tsn); DataChunk dup; if (_holdingPen.TryGetValue(tsn, out dup)) { duplicates.Add(tsn); } else { _holdingPen.Add(tsn, dc); } // now see if we can deliver anything new to the streams bool gap = false; for (uint t = _farTSN + 1; !gap; t++) { if (_holdingPen.TryGetValue(t, out dc)) { _holdingPen.Remove(t); ingest(dc, rep); } else { //logger.LogDebug("gap in inbound tsns at " + t); gap = true; } } } else { // probably wrong now.. logger.LogWarning("Already seen . " + tsn + " expecting " + (_farTSN)); duplicates.Add(tsn); } List <uint> l = new List <uint>(); l.AddRange(_holdingPen.Keys); l.Sort(); SackChunk sack = mkSack(l, duplicates); rep.Add(sack); return(rep.ToArray()); }
private SackChunk mkSack(List <uint> pen, List <uint> dups) { SackChunk ret = new SackChunk(); ret.setCumuTSNAck(_farTSN); int stashcap = calcStashCap(); ret.setArWin((uint)(MAXBUFF - stashcap)); ret.setGaps(pen); ret.setDuplicates(dups); //logger.LogDebug("made SACK " + ret.ToString()); return(ret); }
/* * * 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); }
abstract protected Chunk[] sackDeal(SackChunk sackChunk);
public static Chunk mkChunk(ByteBuffer pkt) { Chunk ret = null; if (pkt.remaining() >= 4) { ChunkType type = (ChunkType)pkt.GetByte(); byte flags = pkt.GetByte(); int length = pkt.GetUShort(); switch (type) { case ChunkType.DATA: ret = new DataChunk(flags, length, pkt); break; case ChunkType.INIT: ret = new InitChunk(type, flags, length, pkt); break; case ChunkType.SACK: ret = new SackChunk(type, flags, length, pkt); break; case ChunkType.INITACK: ret = new InitAckChunk(type, flags, length, pkt); break; case ChunkType.COOKIE_ECHO: ret = new CookieEchoChunk(type, flags, length, pkt); break; case ChunkType.COOKIE_ACK: ret = new CookieAckChunk(type, flags, length, pkt); break; case ChunkType.ABORT: ret = new AbortChunk(type, flags, length, pkt); break; case ChunkType.HEARTBEAT: ret = new HeartBeatChunk(type, flags, length, pkt); break; case ChunkType.RE_CONFIG: ret = new ReConfigChunk(type, flags, length, pkt); break; case ChunkType.ERROR: ret = new ErrorChunk(type, flags, length, pkt); break; default: logger.LogWarning($"SCTP unknown chunk type received {type}."); ret = new FailChunk(type, flags, length, pkt); break; } if (ret != null) { if (pkt.hasRemaining()) { int mod = ret.getLength() % 4; if (mod != 0) { for (int pad = mod; pad < 4; pad++) { pkt.GetByte(); } } } } } return(ret); }