/// <summary> /// Create a new SCTP association instance where the INIT will be generated /// from this end of the connection. /// </summary> /// <param name="sctpTransport">The transport layer doing the actual sending and receiving of /// packets, e.g. UDP, DTLS, raw sockets etc.</param> /// <param name="destination">Optional. The remote destination end point for this association. /// Some transports, such as DTLS, are already established and do not use this parameter.</param> /// <param name="sctpSourcePort">The source port for the SCTP packet header.</param> /// <param name="sctpDestinationPort">The destination port for the SCTP packet header.</param> /// <param name="defaultMTU">The default Maximum Transmission Unit (MTU) for the underlying /// transport. This determines the maximum size of an SCTP packet that will be used with /// the transport.</param> /// <param name="localTransportPort">Optional. The local transport (e.g. UDP or DTLS) port being /// used for the underlying SCTP transport. This be set on the SCTP association's ID to aid in /// diagnostics.</param> public SctpAssociation( SctpTransport sctpTransport, IPEndPoint destination, ushort sctpSourcePort, ushort sctpDestinationPort, ushort defaultMTU, int localTransportPort, ushort numberOutboundStreams = DEFAULT_NUMBER_OUTBOUND_STREAMS, ushort numberInboundStreams = DEFAULT_NUMBER_INBOUND_STREAMS) { _sctpTransport = sctpTransport; Destination = destination; _sctpSourcePort = sctpSourcePort; _sctpDestinationPort = sctpDestinationPort; _defaultMTU = defaultMTU; _numberOutboundStreams = numberOutboundStreams; _numberInboundStreams = numberInboundStreams; VerificationTag = Crypto.GetRandomUInt(true); ID = $"{sctpSourcePort}:{sctpDestinationPort}:{localTransportPort}"; ARwnd = DEFAULT_ADVERTISED_RECEIVE_WINDOW; _dataReceiver = new SctpDataReceiver(ARwnd, _defaultMTU, 0); _dataSender = new SctpDataSender(ID, this.SendChunk, defaultMTU, Crypto.GetRandomUInt(true), DEFAULT_ADVERTISED_RECEIVE_WINDOW); State = SctpAssociationState.Closed; }
/// <summary> /// Initialises the association state based on the echoed cookie (the cookie that we sent /// to the remote party and was then echoed back to us). An association can only be initialised /// from a cookie prior to it being used and prior to it ever having entered the established state. /// </summary> /// <param name="cookie">The echoed cookie that was returned from the remote party.</param> public void GotCookie(SctpTransportCookie cookie) { // The CookieEchoed state is allowed, even though a cookie should be creating a brand // new association rather than one that has already sent an INIT, in order to deal with // a race condition where both SCTP end points attempt to establish the association at // the same time using the same ports. if (_wasAborted || _wasShutdown) { logger.LogWarning($"SCTP association cannot initialise with a cookie after an abort or shutdown."); } else if (!(State == SctpAssociationState.Closed || State == SctpAssociationState.CookieEchoed)) { throw new ApplicationException($"SCTP association cannot initialise with a cookie in state {State}."); } else { _sctpSourcePort = cookie.SourcePort; _sctpDestinationPort = cookie.DestinationPort; VerificationTag = cookie.Tag; ARwnd = cookie.ARwnd; Destination = !string.IsNullOrEmpty(cookie.RemoteEndPoint) ? IPSocket.Parse(cookie.RemoteEndPoint) : null; if (_dataReceiver == null) { _dataReceiver = new SctpDataReceiver(ARwnd, _defaultMTU, cookie.RemoteTSN); } if (_dataSender == null) { _dataSender = new SctpDataSender(ID, this.SendChunk, _defaultMTU, cookie.TSN, cookie.RemoteARwnd); } InitRemoteProperties(cookie.RemoteTag, cookie.RemoteTSN, cookie.RemoteARwnd); var cookieAckChunk = new SctpChunk(SctpChunkType.COOKIE_ACK); SendChunk(cookieAckChunk); SetState(SctpAssociationState.Established); _dataSender.StartSending(); CancelTimers(); } }