/* <pre> * 5.2.1. INIT Received in COOKIE-WAIT or COOKIE-ECHOED State (Item B) * * This usually indicates an initialization collision, i.e., each * endpoint is attempting, at about the same time, to establish an * association with the other endpoint. * * Upon receipt of an INIT in the COOKIE-WAIT state, an endpoint MUST * respond with an INIT ACK using the same parameters it sent in its * original INIT chunk (including its Initiate Tag, unchanged). When * responding, the endpoint MUST send the INIT ACK back to the same * address that the original INIT (sent by this endpoint) was sent. * * Upon receipt of an INIT in the COOKIE-ECHOED state, an endpoint MUST * respond with an INIT ACK using the same parameters it sent in its * original INIT chunk (including its Initiate Tag, unchanged), provided * that no NEW address has been added to the forming association. If * the INIT message indicates that a new address has been added to the * association, then the entire INIT MUST be discarded, and NO changes * should be made to the existing association. An ABORT SHOULD be sent * in response that MAY include the error 'Restart of an association * with new addresses'. The error SHOULD list the addresses that were * added to the restarting association. * * When responding in either state (COOKIE-WAIT or COOKIE-ECHOED) with * an INIT ACK, the original parameters are combined with those from the * newly received INIT chunk. The endpoint shall also generate a State * Cookie with the INIT ACK. The endpoint uses the parameters sent in * its INIT to calculate the State Cookie. * * After that, the endpoint MUST NOT change its state, the T1-init timer * shall be left running, and the corresponding TCB MUST NOT be * destroyed. The normal procedures for handling State Cookies when a * TCB exists will resolve the duplicate INITs to a single association. * * For an endpoint that is in the COOKIE-ECHOED state, it MUST populate * its Tie-Tags within both the association TCB and inside the State * Cookie (see Section 5.2.2 for a description of the Tie-Tags). * </pre> */ public virtual Chunk[] inboundInit(InitChunk init) { Chunk[] reply = null; _peerVerTag = init.getInitiateTag(); _winCredit = init.getAdRecWinCredit(); _farTSN = (uint)(init.getInitialTSN() - 1); _maxOutStreams = Math.Min(init.getNumInStreams(), MAXSTREAMS); _maxInStreams = Math.Min(init.getNumOutStreams(), MAXSTREAMS); InitAckChunk iac = new InitAckChunk(); iac.setAdRecWinCredit(MAXBUFF); iac.setNumInStreams(_maxInStreams); iac.setNumOutStreams(_maxOutStreams); iac.setInitialTSN(_nearTSN); iac.setInitiateTag(_myVerTag); CookieHolder cookie = new CookieHolder(); cookie.cookieData = new byte[Association.COOKIESIZE]; cookie.cookieTime = TimeExtension.CurrentTimeMillis(); _random.NextBytes(cookie.cookieData); iac.setCookie(cookie.cookieData); _cookies.Add(cookie); byte[] fse = init.getFarSupportedExtensions(); if (fse != null) { iac.setSupportedExtensions(this.getUnionSupportedExtensions(fse)); } reply = new Chunk[1]; reply[0] = iac; //logger.LogDebug("SCTP received INIT:" + init.ToString()); //logger.LogDebug("Replying with init-ack :" + iac.ToString()); return(reply); }
protected virtual Chunk[] iackDeal(InitAckChunk iack) { iack.getAdRecWinCredit(); iack.getInitialTSN(); iack.getNumInStreams(); iack.getNumOutStreams(); /* * NOTE: TO DO - this is a protocol violation - this should be done with * multiple TCBS and set in cookie echo * NOT HERE */ _peerVerTag = iack.getInitiateTag(); _winCredit = iack.getAdRecWinCredit(); _farTSN = iack.getInitialTSN() - 1; _maxOutStreams = Math.Min(iack.getNumInStreams(), MAXSTREAMS); _maxInStreams = Math.Min(iack.getNumOutStreams(), MAXSTREAMS); iack.getSupportedExtensions(_supportedExtensions); byte[] data = iack.getCookie(); CookieEchoChunk ce = new CookieEchoChunk(); ce.setCookieData(data); Chunk[] reply = new Chunk[1] { ce }; this._state = State.COOKIEECHOED; return(reply); }
/* * If the T1-init timer expires at "A" after the INIT or COOKIE ECHO * chunks are sent, the same INIT or COOKIE ECHO chunk with the same * Initiate Tag (i.e., Tag_A) or State Cookie shall be retransmitted and * the timer restarted. This shall be repeated Max.Init.Retransmits * times before "A" considers "Z" unreachable and reports the failure to * its upper layer (and thus the association enters the CLOSED state). * * When retransmitting the INIT, the endpoint MUST follow the rules * defined in Section 6.3 to determine the proper timer value. */ protected override Chunk[] iackDeal(InitAckChunk iack) { Chunk[] ret = base.iackDeal(iack); _stashCookieEcho = ret; return(ret); }
/** * * @param c - Chunk to be processed * @return valid - false if the remaining chunks of the packet should be * ignored. * @throws IOException * @throws SctpPacketFormatException */ private bool deal(Chunk c, List <Chunk> replies) { ChunkType ty = c.getType(); bool ret = true; State oldState = _state; Chunk[] reply = null; switch (ty) { case ChunkType.INIT: if (acceptableStateForInboundInit()) { InitChunk init = (InitChunk)c; reply = inboundInit(init); } else { // logger.LogDebug("Got an INIT when state was " + _state.ToString() + " - ignoring it for now "); } break; case ChunkType.INITACK: //logger.LogDebug("got initack " + c.ToString()); if (_state == State.COOKIEWAIT) { InitAckChunk iack = (InitAckChunk)c; reply = iackDeal(iack); } else { //logger.LogDebug("Got an INITACK when not waiting for it - ignoring it"); } break; case ChunkType.COOKIE_ECHO: // logger.LogDebug("got cookie echo " + c.ToString()); reply = cookieEchoDeal((CookieEchoChunk)c); if (reply.Length > 0) { ret = !typeof(ErrorChunk).IsAssignableFrom(reply[0].GetType()); // ignore any following data chunk. } break; case ChunkType.COOKIE_ACK: //logger.LogDebug("got cookie ack " + c.ToString()); if (_state == State.COOKIEECHOED) { _state = State.ESTABLISHED; } break; case ChunkType.DATA: //logger.LogDebug("got data " + c.ToString()); reply = dataDeal((DataChunk)c); break; case ChunkType.ABORT: // no reply we should just bail I think. _rcv = null; _transp.Close(); break; case ChunkType.HEARTBEAT: reply = ((HeartBeatChunk)c).mkReply(); break; case ChunkType.SACK: //logger.LogDebug("got tsak for TSN " + ((SackChunk)c).getCumuTSNAck()); reply = sackDeal((SackChunk)c); // fix the outbound list here break; case ChunkType.RE_CONFIG: reply = reconfigState.deal((ReConfigChunk)c); break; case ChunkType.ERROR: logger.LogWarning($"SCTP error chunk received."); foreach (var vparam in c._varList) { if (vparam is KnownError) { var knownErr = vparam as KnownError; logger.LogWarning($"{knownErr.getName()}, {knownErr}"); } } break; } if (reply != null) { foreach (Chunk r in reply) { replies.Add(r); } // theoretically could be multiple DATA in a single packet - // we'd send multiple SACKs in reply - ToDo fix that } if ((_state == State.ESTABLISHED) && (oldState != State.ESTABLISHED)) { if (null != _al) { _al.onAssociated(this); } reconfigState = new ReconfigState(this, _farTSN); } if ((oldState == State.ESTABLISHED) && (_state != State.ESTABLISHED)) { if (null != _al) { _al.onDisAssociated(this); } } return(ret); }
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); }