diff --git a/worker/fuzzer/src/RTC/SCTP/association/FuzzerStateCookie.cpp b/worker/fuzzer/src/RTC/SCTP/association/FuzzerStateCookie.cpp index 881dc4ec45..f751c54e1d 100644 --- a/worker/fuzzer/src/RTC/SCTP/association/FuzzerStateCookie.cpp +++ b/worker/fuzzer/src/RTC/SCTP/association/FuzzerStateCookie.cpp @@ -22,8 +22,11 @@ void FuzzerRtcSctpStateCookie::Fuzz(const uint8_t* data, size_t len) if (len < RTC::SCTP::StateCookie::StateCookieLength + 5) { - Utils::Byte::Set4Bytes(clonedData, 0, RTC::SCTP::StateCookie::MagicValue1); - Utils::Byte::Set2Bytes(clonedData, 34, RTC::SCTP::StateCookie::MagicValue2); + Utils::Byte::Set4Bytes(clonedData, 0, RTC::SCTP::StateCookie::Magic1); + Utils::Byte::Set2Bytes( + clonedData, + RTC::SCTP::StateCookie::NegotiatedCapabilitiesOffset, + RTC::SCTP::StateCookie::Magic2); } } @@ -36,21 +39,21 @@ void FuzzerRtcSctpStateCookie::Fuzz(const uint8_t* data, size_t len) return; } - stateCookie->GetMyVerificationTag(); - stateCookie->GetPeerVerificationTag(); - stateCookie->GetMyInitialTsn(); - stateCookie->GetPeerInitialTsn(); - stateCookie->GetMyAdvertisedReceiverWindowCredit(); + stateCookie->GetLocalVerificationTag(); + stateCookie->GetRemoteVerificationTag(); + stateCookie->GetLocalInitialTsn(); + stateCookie->GetRemoteInitialTsn(); + stateCookie->GetRemoteAdvertisedReceiverWindowCredit(); stateCookie->GetTieTag(); stateCookie->GetNegotiatedCapabilities(); stateCookie->Serialize(StateCookieSerializeBuffer, len); - stateCookie->GetMyVerificationTag(); - stateCookie->GetPeerVerificationTag(); - stateCookie->GetMyInitialTsn(); - stateCookie->GetPeerInitialTsn(); - stateCookie->GetMyAdvertisedReceiverWindowCredit(); + stateCookie->GetLocalVerificationTag(); + stateCookie->GetRemoteVerificationTag(); + stateCookie->GetLocalInitialTsn(); + stateCookie->GetRemoteInitialTsn(); + stateCookie->GetRemoteAdvertisedReceiverWindowCredit(); stateCookie->GetTieTag(); stateCookie->GetNegotiatedCapabilities(); @@ -58,11 +61,11 @@ void FuzzerRtcSctpStateCookie::Fuzz(const uint8_t* data, size_t len) delete stateCookie; - clonedStateCookie->GetMyVerificationTag(); - clonedStateCookie->GetPeerVerificationTag(); - clonedStateCookie->GetMyInitialTsn(); - clonedStateCookie->GetPeerInitialTsn(); - clonedStateCookie->GetMyAdvertisedReceiverWindowCredit(); + clonedStateCookie->GetLocalVerificationTag(); + clonedStateCookie->GetRemoteVerificationTag(); + clonedStateCookie->GetLocalInitialTsn(); + clonedStateCookie->GetRemoteInitialTsn(); + clonedStateCookie->GetRemoteAdvertisedReceiverWindowCredit(); clonedStateCookie->GetTieTag(); clonedStateCookie->GetNegotiatedCapabilities(); diff --git a/worker/fuzzer/src/RTC/SCTP/packet/FuzzerPacket.cpp b/worker/fuzzer/src/RTC/SCTP/packet/FuzzerPacket.cpp index e9c1436419..ab2cdce599 100644 --- a/worker/fuzzer/src/RTC/SCTP/packet/FuzzerPacket.cpp +++ b/worker/fuzzer/src/RTC/SCTP/packet/FuzzerPacket.cpp @@ -36,7 +36,7 @@ void FuzzerRtcSctpPacket::Fuzz(const uint8_t* data, size_t len) packet->GetChecksum(); packet->ValidateCRC32cChecksum(); packet->SetChecksum(999999); - packet->SetCRC32cChecksum(); + packet->WriteCRC32cChecksum(); packet->ValidateCRC32cChecksum(); packet->HasChunks(); packet->GetChunksCount(); @@ -58,7 +58,7 @@ void FuzzerRtcSctpPacket::Fuzz(const uint8_t* data, size_t len) clonedPacket->GetChecksum(); clonedPacket->ValidateCRC32cChecksum(); clonedPacket->SetChecksum(999999); - clonedPacket->SetCRC32cChecksum(); + clonedPacket->WriteCRC32cChecksum(); clonedPacket->ValidateCRC32cChecksum(); clonedPacket->HasChunks(); clonedPacket->GetChunksCount(); diff --git a/worker/include/RTC/SCTP/TODO_SCTP.md b/worker/include/RTC/SCTP/TODO_SCTP.md index 4b4c621d22..d233e83be9 100644 --- a/worker/include/RTC/SCTP/TODO_SCTP.md +++ b/worker/include/RTC/SCTP/TODO_SCTP.md @@ -12,9 +12,3 @@ Here some notes about our future SCTP implementation. However, in step 4 `WebRtcTransport::OnDtlsTransportApplicationDataReceived()` should instead call `RTC::SCTP::Packet::parse()` and `Transport::ReceiveSctpPacket()` with a `SCTP::Packet` instance instead than `data` and `len`. In fact it should be named `Transport::ReceiveSctpPacket()` instead of the current `Transport::ReceiveSctpData()`. Same in `PipeTransport` and `PlainTransport`. - -## TODO - -- Try to remove all `friend class`. - -- In `HeartbeatChunk` the `HeartbeatInfoParameter` should be mandatory when parsing. Or should we add some `Validate()` method? diff --git a/worker/include/RTC/SCTP/association/NegotiatedCapabilities.hpp b/worker/include/RTC/SCTP/association/NegotiatedCapabilities.hpp index 233ced90e8..7236b87864 100644 --- a/worker/include/RTC/SCTP/association/NegotiatedCapabilities.hpp +++ b/worker/include/RTC/SCTP/association/NegotiatedCapabilities.hpp @@ -2,6 +2,10 @@ #define MS_RTC_SCTP_NEGOTIATED_CAPABILITIES_HPP #include "common.hpp" +#include "RTC/SCTP/association/SocketOptions.hpp" +#include "RTC/SCTP/packet/chunks/InitAckChunk.hpp" +#include "RTC/SCTP/packet/chunks/InitChunk.hpp" +#include // std::variant, std::visit() namespace RTC { @@ -14,6 +18,19 @@ namespace RTC */ struct NegotiatedCapabilities { + using InitOrInitAckChunkVariant = std::variant; + + /** + * Create a NegotiatedCapabilities struct. Intended to be used during + * the SCTP association handshake flow. + * + * @remarks + * Given `remoteChunk` must be an INIT or an INIT_ACK Chunk. Otherwise + * it will fail in compilation time. + */ + static NegotiatedCapabilities Factory( + SocketOptions socketOptions, InitOrInitAckChunkVariant remoteChunk); + /** * Negotiated maximum number of outbound streams (OS). */ diff --git a/worker/include/RTC/SCTP/association/Socket.hpp b/worker/include/RTC/SCTP/association/Socket.hpp index 2bfc5cff3a..676987db6a 100644 --- a/worker/include/RTC/SCTP/association/Socket.hpp +++ b/worker/include/RTC/SCTP/association/Socket.hpp @@ -2,9 +2,25 @@ #define MS_RTC_SCTP_SOCKET_HPP #include "common.hpp" -#include "RTC/Consts.hpp" +#include "RTC/SCTP/association/NegotiatedCapabilities.hpp" +#include "RTC/SCTP/association/SocketMetrics.hpp" +#include "RTC/SCTP/association/SocketOptions.hpp" #include "RTC/SCTP/association/TransmissionControlBlock.hpp" -#include "RTC/SCTP/packet/parameters/ZeroChecksumAcceptableParameter.hpp" +#include "RTC/SCTP/packet/Chunk.hpp" +#include "RTC/SCTP/packet/Packet.hpp" +#include "RTC/SCTP/packet/chunks/AbortAssociationChunk.hpp" +#include "RTC/SCTP/packet/chunks/DataChunk.hpp" +#include "RTC/SCTP/packet/chunks/HeartbeatRequestChunk.hpp" +#include "RTC/SCTP/packet/chunks/InitAckChunk.hpp" +#include "RTC/SCTP/packet/chunks/InitChunk.hpp" +#include "RTC/SCTP/packet/chunks/OperationErrorChunk.hpp" +#include "RTC/SCTP/packet/chunks/SackChunk.hpp" +#include "RTC/SCTP/packet/chunks/ShutdownAckChunk.hpp" +#include "RTC/SCTP/packet/chunks/ShutdownCompleteChunk.hpp" +#include "RTC/SCTP/packet/chunks/UnknownChunk.hpp" +#include "handles/BackoffTimerHandle.hpp" +#include +#include namespace RTC { @@ -16,82 +32,168 @@ namespace RTC * * It manages all Packet and Chunk dispatching and the connection flow. */ - class Socket + class Socket : public BackoffTimerHandle::Listener { public: - struct SocketOptions + class Listener { - /** - * Signaled local port. - */ - uint16_t localPort{ 0 }; - /** - * Signaled destination port. - */ - uint16_t destinationPort{ 0 }; - /** - * Announced maximum number of outbound streams (OS). - * NOTE: We use maximum value by default. - */ - uint16_t maxOutboundStreams{ 65535 }; - /** - * Announced maximum number of inbound streams (MIS). - * NOTE: We use maximum value by default. - */ - uint16_t maxInboundStreams{ 65535 }; - /** - * Maximum received window buffer size. It must be larger than the - * largest sized message we want to be able to receive. - * - * @remarks - * Default value copied from dcSCTP library. - */ - uint32_t myAdvertisedReceiverWindowCredit{ 5 * 1024 * 1024 }; - /** - * Use Partial Reliability Extension. - * @see RFC 3758. - */ - bool partialReliability{ false }; - /** - * Use Stream Schedulers and User Message Interleaving (I-DATA Chunks). - * @see RFC 8260. - */ - bool messageInterleaving{ false }; - /** - * Alternate error detection method for Zero Checksum. - * - * @remarks - * This feature is only enabled if both peers signal their wish to use - * the same (non-zero) Zero Checksum Alternate Error Detection Method. - * - * @see RFC 9653. - */ - ZeroChecksumAcceptableParameter::AlternateErrorDetectionMethod zeroCheksumAlternateErrorDetectionMethod{ - ZeroChecksumAcceptableParameter::AlternateErrorDetectionMethod::NONE - }; - /** - * Maximum size of a SCTP packet. It doesn't include any overhead of - * DTLS, TURN, UDP or IP headers. - */ - size_t mtu{ RTC::Consts::MaxSafeMtuSizeForSctp }; + public: + virtual ~Listener() = default; + + public: + virtual void OnSocketSendSctpPacket(const Socket* socket, Packet* packet) const = 0; + }; + + public: + /** + * SCTP association state. + */ + enum class State + { + CLOSED, + COOKIE_WAIT, + // NOTE: TCB is valid in these states: + COOKIE_ECHOED, + ESTABLISHED, + SHUTDOWN_PENDING, + SHUTDOWN_SENT, + SHUTDOWN_RECEIVED, + SHUTDOWN_ACK_SENT, }; + /** + * Struct holding local verification tag and initial TSN between having + * sent the INIT Chunk until the connection is established (there is no + * TCB in between). + * + * @remarks + * This is how dcSCTP does, despite RFC 9260 states that the TCB should + * also be created when an INIT Chunk is sent. + */ + struct PreTransmissionControlBlock + { + uint32_t localVerificationTag{ 0 }; + uint32_t localInitialTsn{ 0 }; + }; + + public: + static constexpr std::string_view State2String(State state); + public: - explicit Socket(SocketOptions options); + explicit Socket(SocketOptions options, Listener* listener); ~Socket(); void Dump(int indentation = 0) const; + /** + * Initiate the SCTP association with the remote peer. It sends an INIT + * Chunk. + * + * @remarks + * The Socket must be in Closed state. + */ + void Associate(); + + /** + * Receive a Packet received from the peer. + */ + void ReceivePacket(const Packet* receivedPacket); + private: + void SetState(State state, const std::string& reason); + + void AddCapabilitiesParametersToInitOrInitAckChunk(Chunk* chunk) const; + + void CreateTransmissionControlBlock( + uint32_t localVerificationTag, + uint32_t remoteVerificationTag, + uint32_t localInitialTsn, + uint32_t remoteInitialTsn, + uint32_t localAdvertisedReceiverWindowCredit, + uint64_t tieTag, + const NegotiatedCapabilities& negotiatedCapabilities); + + Packet* CreatePacket() const; + + Packet* CreatePacketWithVerificationTag(uint32_t verificationTag) const; + + /** + * Notify the parent about a Packet to be sent to the peer. + * + * This method also writes the Packet checksum field depending on the value + * of `writeChecksum`. If it's explicitly set then it's honored. Otherwise + * the checksum field is written based on whether Zero Checksum has been + * negotiated or not. + * + * @remarks + * This method does not delete the given `packet`. The caller must do it + * after invoking this method. + */ + void SendPacket(Packet* packet, std::optional writeChecksum = std::nullopt); + void SendInitChunk(); + void SendShutdownAckChunk(); + + bool ValidateReceivedPacket(const Packet* receivedPacket); + + bool ProcessReceivedChunk(const Packet* receivedPacket, const Chunk* receivedChunk); + + void ProcessReceivedDataChunk(const Packet* receivedPacket, const DataChunk* receivedDataChunk); + + void ProcessReceivedInitChunk(const Packet* receivedPacket, const InitChunk* receivedInitChunk); + + void ProcessReceivedInitAckChunk( + const Packet* receivedPacket, const InitAckChunk* receivedInitAckChunk); + + void ProcessReceivedSackChunk(const Packet* receivedPacket, const SackChunk* receivedSackChunk); + + void ProcessReceivedHeartbeatRequestChunk( + const Packet* receivedPacket, const HeartbeatRequestChunk* receivedHeartbeatRequestChunk); + + bool ProcessReceivedUnknownChunk( + const Packet* receivedPacket, const UnknownChunk* receivedUnknownChunk); + + void OnT1InitTimer(uint64_t& baseTimeout, bool& stop); + + void OnT1CookieTimer(uint64_t& baseTimeout, bool& stop); + + void OnT2ShutdownTimer(uint64_t& baseTimeout, bool& stop); + + template + void AssertState(States... expectedStates) const; + + template + void AssertNotState(States... unexpectedStates) const; + + void AssertHasTcb() const; + + /* Pure virtual methods inherited from BackoffTimerHandle::Listener. */ + public: + void OnTimer(BackoffTimerHandle* backoffTimer, uint64_t& baseTimeout, bool& stop) override; + private: // Socket options given in th econstructor. - Socket::SocketOptions options; + const SocketOptions options; + // Listener. + const Listener* listener{ nullptr }; + // SCTP association state. + State state{ State::CLOSED }; + // Metrics. + SocketMetrics metrics{}; + // To keep settings between sending of INIT Chunk and establishment of + // the connection. + PreTransmissionControlBlock preTcb; // Once the SCTP association is established a Transmission Control Block - // is created (and also when we are the initiator of the association). + // is created. std::unique_ptr tcb; + // T1-init timer. + const std::unique_ptr t1InitTimer; + // T1-cookie timer. + const std::unique_ptr t1CookieTimer; + // T2-shutdown timer. + const std::unique_ptr t2ShutdownTimer; }; } // namespace SCTP } // namespace RTC diff --git a/worker/include/RTC/SCTP/association/SocketMetrics.hpp b/worker/include/RTC/SCTP/association/SocketMetrics.hpp new file mode 100644 index 0000000000..cf7ef5a80c --- /dev/null +++ b/worker/include/RTC/SCTP/association/SocketMetrics.hpp @@ -0,0 +1,77 @@ +#ifndef MS_RTC_SCTP_SOCKET_METRICS_HPP +#define MS_RTC_SCTP_SOCKET_METRICS_HPP + +#include "common.hpp" +#include "RTC/SCTP/association/StateCookie.hpp" + +namespace RTC +{ + namespace SCTP + { + /** + * SCTP Socket metrics. + */ + struct SocketMetrics + { + /** + * Number of SCTP Packets sent. + */ + uint64_t txPacketsCount{ 0 }; + /** + * Number of messages requested to be sent. + */ + uint64_t txMessagesCount{ 0 }; + /** + * Number of Packets retransmitted. Since SCTP Packets can contain both + * retransmitted DATA or I-DATA Chunks and Chunks that are transmitted for + * the first time, this represents an upper bound as it's incremented + * every time a Packet contains a retransmitted DATA or I-DATA chunk. + */ + uint64_t rtxPacketsCount{ 0 }; + + // TODO: More. + + /** + * Number of SCTP Packets received. + */ + uint64_t rxPacketsCount{ 0 }; + /** + * Number of messages received. + */ + uint64_t rxMessagesCount{ 0 }; + + // TODO: More. + + /** + * SCTP implementation of the peer. Only detected when the peer sends an + * INIT_ACK Chunk to us with a State Cookie. + */ + StateCookie::SctpImplementation peerImplementation{ StateCookie::SctpImplementation::UNKNOWN }; + + // TODO: More. + + /** + * Whether Stream Schedulers and User Message Interleaving (I-DATA Chunks) + * have been negotiated. + * + * @see RFC 8260. + */ + bool messageInterleaving{ false }; + /** + * Whether Alternate Error Detection Method for Zero Checksum has been + * negotiated. + * + * @remarks + * This feature is only enabled if both peers signal their wish to use + * the same (non-zero) Zero Checksum Alternate Error Detection Method. + * + * @see RFC 9653. + */ + bool zeroChecksum{ false }; + + void Dump(int indentation = 0) const; + }; + } // namespace SCTP +} // namespace RTC + +#endif diff --git a/worker/include/RTC/SCTP/association/SocketOptions.hpp b/worker/include/RTC/SCTP/association/SocketOptions.hpp new file mode 100644 index 0000000000..77a167f1a3 --- /dev/null +++ b/worker/include/RTC/SCTP/association/SocketOptions.hpp @@ -0,0 +1,103 @@ +#ifndef MS_RTC_SCTP_SOCKET_OPTIONS_HPP +#define MS_RTC_SCTP_SOCKET_OPTIONS_HPP + +#include "common.hpp" +#include "RTC/Consts.hpp" +#include "RTC/SCTP/packet/parameters/ZeroChecksumAcceptableParameter.hpp" + +namespace RTC +{ + namespace SCTP + { + /** + * Options given to Socket constructor. + */ + struct SocketOptions + { + /** + * Signaled source port. + */ + uint16_t sourcePort{ 0 }; + /** + * Signaled destination port. + */ + uint16_t destinationPort{ 0 }; + /** + * Announced maximum number of outbound streams (OS). + * NOTE: We use maximum value by default. + */ + uint16_t maxOutboundStreams{ 65535 }; + /** + * Announced maximum number of inbound streams (MIS). + * NOTE: We use maximum value by default. + */ + uint16_t maxInboundStreams{ 65535 }; + /** + * Maximum received window buffer size. It must be larger than the + * largest sized message we want to be able to receive. + * + * @remarks + * Default value copied from dcSCTP library. + */ + uint32_t localAdvertisedReceiverWindowCredit{ 5 * 1024 * 1024 }; + /** + * Use Partial Reliability Extension. + * @see RFC 3758. + */ + bool partialReliability{ false }; + /** + * Use Stream Schedulers and User Message Interleaving (I-DATA Chunks). + * + * @see RFC 8260. + */ + bool messageInterleaving{ false }; + /** + * Alternate Error Detection Method for Zero Checksum. + * + * @remarks + * This feature is only enabled if both peers signal their wish to use + * the same (non-zero) Zero Checksum Alternate Error Detection Method. + * + * @see RFC 9653. + */ + ZeroChecksumAcceptableParameter::AlternateErrorDetectionMethod zeroChecksumAlternateErrorDetectionMethod{ + ZeroChecksumAcceptableParameter::AlternateErrorDetectionMethod::NONE + }; + /** + * Maximum size of a SCTP Packet. It doesn't include any overhead of + * DTLS, TURN, UDP or IP headers. + */ + size_t mtu{ RTC::Consts::MaxSafeMtuSizeForSctp }; + /** + * T1-init timeout (ms). + */ + uint64_t t1InitTimeout{ 1000 }; + /** + * T1-cookie timeout (ms). + */ + uint64_t t1CookieTimeout{ 1000 }; + /** + * T2-shutdown timeout (ms). + */ + uint64_t t2ShutdownTimeout{ 1000 }; + /** + * Maximum duration of the backoff timeout. If no value is given, no + * limit is set. + */ + std::optional timerMaxBackoffTimeout{ std::nullopt }; + /** + * Max.Init.Retransmits. Set to std::nullopt for no limit. + * + * @see https://datatracker.ietf.org/doc/html/rfc9260#section-16 + */ + std::optional maxInitRetransmits = 8; + /** + * Maximum data retransmit attempts (for DATA, I_DATA and other Chunks). + * Set to std::nullopt for no limit. + */ + std::optional maxRetransmits = 8; + }; + } // namespace SCTP +} // namespace RTC + +#endif diff --git a/worker/include/RTC/SCTP/association/StateCookie.hpp b/worker/include/RTC/SCTP/association/StateCookie.hpp index a1103cb9ad..84ffcfc25b 100644 --- a/worker/include/RTC/SCTP/association/StateCookie.hpp +++ b/worker/include/RTC/SCTP/association/StateCookie.hpp @@ -5,6 +5,7 @@ #include "Utils.hpp" #include "RTC/SCTP/association/NegotiatedCapabilities.hpp" #include "RTC/Serializable.hpp" +#include namespace RTC { @@ -20,17 +21,18 @@ namespace RTC * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Magic Value 1 | + * | Magic 1 | + * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | My Verification Tag | + * | Local Verification Tag | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Peer Verification Tag | + * | Remote Verification Tag | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | My Initial TSN | + * | Local Initial TSN | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Peer Initial TSN | + * | Remote Initial TSN | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | My Advertised Receiver Window Credit (a_rwnd) | + * | Remote Advertised Receiver Window Credit (a_rwnd) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Tie-Tag | * | | @@ -45,7 +47,7 @@ namespace RTC * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | (Reserved) | |D|C|B|A| Magic Value 2 | + * | (Reserved) | |D|C|B|A| Magic 2 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Max Outbound Streams | Max Inbound Streams | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -59,38 +61,17 @@ namespace RTC class StateCookie : public Serializable { public: - static constexpr size_t StateCookieLength{ 40 }; - // Magic value we prefix the State Cookie with. - static constexpr uint32_t MagicValue1{ 0xF109ABE4 }; - // Magic value used within the Negotiated Capabilities block. - static constexpr uint16_t MagicValue2{ 0xAD81 }; - - public: - /** - * Parse a StateCookie. - * - * @remarks - * `bufferLength` must be the exact length of the State Cookie. - */ - static StateCookie* Parse(const uint8_t* buffer, size_t bufferLength); - /** - * Create a StateCookie. - * - * @remarks - * `bufferLength` could be greater than the real length of the State - * Cookie. + * SCTP implementation determined by first 8 bytes of the State Cookie + * sent by the remote peer. */ - static StateCookie* Factory( - uint8_t* buffer, - size_t bufferLength, - uint32_t myVerificationTag, - uint32_t peerVerificationTag, - uint32_t myInitialTsn, - uint32_t peerInitialTsn, - uint32_t myAdvertisedReceiverWindowCredit, - uint64_t tieTag, - const NegotiatedCapabilities& negotiatedCapabilities); + enum class SctpImplementation + { + UNKNOWN, + MEDIASOUP, + DCSCTP, + USRSCTP, + }; private: struct NegotiatedCapabilitiesField @@ -109,11 +90,79 @@ namespace RTC uint8_t bitB : 1; uint8_t bitA : 1; #endif - uint16_t magicValue2; + uint16_t magic2; uint16_t maxOutboundStreams; uint16_t maxInboundStreams; }; + public: + // Fixed total length of our generated State Cookies. + static constexpr size_t StateCookieLength{ 44 }; + // Offset in the State Cookie where the Negotiated Capabilitied are + // located. + static constexpr size_t NegotiatedCapabilitiesOffset{ 36 }; + // Magic value we prefix the State Cookie with. Note that it is + // "msworker" in ASCII bytes. + static constexpr uint64_t Magic1{ 0x6D73776F726B6572 }; + static constexpr size_t Magic1Length{ 8 }; + // Magic value used within the Negotiated Capabilities block. + static constexpr uint16_t Magic2{ 0xAD81 }; + + public: + /** + * Whether the given buffer is a StateCookie generated by mediasoup. + */ + static bool IsMediasoupStateCookie(const uint8_t* buffer, size_t bufferLength); + + /** + * Parse a StateCookie supposely generated by mediasoup. + * + * @remarks + * `bufferLength` must be the exact length of the State Cookie. + */ + static StateCookie* Parse(const uint8_t* buffer, size_t bufferLength); + + /** + * Create a StateCookie. + * + * @remarks + * `bufferLength` could be greater than the real length of the State + * Cookie. + */ + static StateCookie* Factory( + uint8_t* buffer, + size_t bufferLength, + uint32_t localVerificationTag, + uint32_t remoteVerificationTag, + uint32_t localInitialTsn, + uint32_t remoteInitialTsn, + uint32_t remoteAdvertisedReceiverWindowCredit, + uint64_t tieTag, + const NegotiatedCapabilities& negotiatedCapabilities); + + /** + * Serialize a StateCookie (based on given arguments) in the given buffer. + */ + static void Write( + uint8_t* buffer, + size_t bufferLength, + uint32_t localVerificationTag, + uint32_t remoteVerificationTag, + uint32_t localInitialTsn, + uint32_t remoteInitialTsn, + uint32_t remoteAdvertisedReceiverWindowCredit, + uint64_t tieTag, + const NegotiatedCapabilities& negotiatedCapabilities); + + /** + * Determine the SCTP implementation of the generator of State Cookie + * given in the buffer. + */ + static SctpImplementation DetermineSctpImplementation(const uint8_t* buffer, size_t bufferLength); + + static const std::string_view SctpImplementation2String( + StateCookie::SctpImplementation sctpImplementation); + public: StateCookie(uint8_t* buffer, size_t bufferLength); @@ -128,9 +177,9 @@ namespace RTC * Chunk. Packets sent by the remote peer must include this value in * their Verification Tag field. */ - uint32_t GetMyVerificationTag() const + uint32_t GetLocalVerificationTag() const { - return Utils::Byte::Get4Bytes(GetBuffer(), 4); + return Utils::Byte::Get4Bytes(GetBuffer(), 8); } /** @@ -138,36 +187,36 @@ namespace RTC * INIT_ACK Chunk. Packets sent by us to the peer must include this value * in their Verification Tag field. */ - uint32_t GetPeerVerificationTag() const + uint32_t GetRemoteVerificationTag() const { - return Utils::Byte::Get4Bytes(GetBuffer(), 8); + return Utils::Byte::Get4Bytes(GetBuffer(), 12); } /** * The value of the Initial TSN field we put in our INIT or INIT_ACK * Chunk. */ - uint32_t GetMyInitialTsn() const + uint32_t GetLocalInitialTsn() const { - return Utils::Byte::Get4Bytes(GetBuffer(), 12); + return Utils::Byte::Get4Bytes(GetBuffer(), 16); } /** * The value of the Initial TSN field the peer put in its INIT or * INIT_ACK Chunk. */ - uint32_t GetPeerInitialTsn() const + uint32_t GetRemoteInitialTsn() const { - return Utils::Byte::Get4Bytes(GetBuffer(), 16); + return Utils::Byte::Get4Bytes(GetBuffer(), 20); } /** * The value of the Advertised Receiver Window Credit field we put in our * INIT or INIT_ACK Chunk. */ - uint32_t GetMyAdvertisedReceiverWindowCredit() const + uint32_t GetRemoteAdvertisedReceiverWindowCredit() const { - return Utils::Byte::Get4Bytes(GetBuffer(), 20); + return Utils::Byte::Get4Bytes(GetBuffer(), 24); } /** @@ -175,7 +224,7 @@ namespace RTC */ uint64_t GetTieTag() const { - return Utils::Byte::Get8Bytes(GetBuffer(), 24); + return Utils::Byte::Get8Bytes(GetBuffer(), 28); } /** @@ -186,7 +235,8 @@ namespace RTC private: NegotiatedCapabilitiesField* GetNegotiatedCapabilitiesField() const { - return reinterpret_cast(const_cast(GetBuffer()) + 32); + return reinterpret_cast( + const_cast(GetBuffer()) + StateCookie::NegotiatedCapabilitiesOffset); } }; } // namespace SCTP diff --git a/worker/include/RTC/SCTP/association/TransmissionControlBlock.hpp b/worker/include/RTC/SCTP/association/TransmissionControlBlock.hpp index f74865ee67..4051e87409 100644 --- a/worker/include/RTC/SCTP/association/TransmissionControlBlock.hpp +++ b/worker/include/RTC/SCTP/association/TransmissionControlBlock.hpp @@ -18,11 +18,11 @@ namespace RTC { public: TransmissionControlBlock( - uint32_t myVerificationTag, - uint32_t peerVerificationTag, - uint32_t myInitialTsn, - uint32_t peerInitialTsn, - uint32_t myAdvertisedReceiverWindowCredit, + uint32_t localVerificationTag, + uint32_t remoteVerificationTag, + uint32_t localInitialTsn, + uint32_t remoteInitialTsn, + uint32_t remoteAdvertisedReceiverWindowCredit, uint64_t tieTag, const NegotiatedCapabilities& negotiatedCapabilities); @@ -35,9 +35,9 @@ namespace RTC * Chunk. Packets sent by the remote peer must include this value in * their Verification Tag field. */ - uint32_t GetMyVerificationTag() const + uint32_t GetLocalVerificationTag() const { - return this->myVerificationTag; + return this->localVerificationTag; } /** @@ -45,36 +45,36 @@ namespace RTC * INIT_ACK Chunk. Packets sent by us to the peer must include this value * in their Verification Tag field. */ - uint32_t GetPeerVerificationTag() const + uint32_t GetRemoteVerificationTag() const { - return this->peerVerificationTag; + return this->remoteVerificationTag; } /** * The value of the Initial TSN field we put in our INIT or INIT_ACK * Chunk. */ - uint32_t GetMyInitialTsn() const + uint32_t GetLocalInitialTsn() const { - return this->myInitialTsn; + return this->localInitialTsn; } /** * The value of the Initial TSN field the peer put in its INIT or * INIT_ACK Chunk. */ - uint32_t GetPeerInitialTsn() const + uint32_t GetRemoteInitialTsn() const { - return this->peerInitialTsn; + return this->remoteInitialTsn; } /** * The value of the Advertised Receiver Window Credit field we put in our * INIT or INIT_ACK Chunk. */ - uint32_t GetMyAdvertisedReceiverWindowCredit() const + uint32_t GetRemoteAdvertisedReceiverWindowCredit() const { - return this->myAdvertisedReceiverWindowCredit; + return this->remoteAdvertisedReceiverWindowCredit; } /** @@ -94,11 +94,11 @@ namespace RTC } private: - uint32_t myVerificationTag{ 0 }; - uint32_t peerVerificationTag{ 0 }; - uint32_t myInitialTsn{ 0 }; - uint32_t peerInitialTsn{ 0 }; - uint32_t myAdvertisedReceiverWindowCredit{ 0 }; + uint32_t localVerificationTag{ 0 }; + uint32_t remoteVerificationTag{ 0 }; + uint32_t localInitialTsn{ 0 }; + uint32_t remoteInitialTsn{ 0 }; + uint32_t remoteAdvertisedReceiverWindowCredit{ 0 }; uint64_t tieTag{ 0 }; NegotiatedCapabilities negotiatedCapabilities; }; diff --git a/worker/include/RTC/SCTP/packet/ErrorCause.hpp b/worker/include/RTC/SCTP/packet/ErrorCause.hpp index 4e2fc47f6a..f144c32568 100644 --- a/worker/include/RTC/SCTP/packet/ErrorCause.hpp +++ b/worker/include/RTC/SCTP/packet/ErrorCause.hpp @@ -138,6 +138,23 @@ namespace RTC return false; } + virtual const std::string ToString() const final + { + // Get the custom content from the subclass. + const auto contentToString = ContentToString(); + + if (contentToString.size() > 0) + { + return ErrorCause::ErrorCauseCodeToString(GetCode()) + " (" + + std::to_string(static_cast(GetCode())) + ") " + contentToString; + } + else + { + return ErrorCause::ErrorCauseCodeToString(GetCode()) + " (" + + std::to_string(static_cast(GetCode())) + ")"; + } + } + protected: /** * Subclasses must invoke this method within their Dump() method. @@ -177,6 +194,14 @@ namespace RTC GetHeaderPointer()->code = static_cast(htons(static_cast(causeCode))); } + + /** + * Subclasses can override this method. + */ + virtual const std::string ContentToString() const + { + return ""; + } }; } // namespace SCTP } // namespace RTC diff --git a/worker/include/RTC/SCTP/packet/Packet.hpp b/worker/include/RTC/SCTP/packet/Packet.hpp index d34bc7f65c..909d58cf74 100644 --- a/worker/include/RTC/SCTP/packet/Packet.hpp +++ b/worker/include/RTC/SCTP/packet/Packet.hpp @@ -223,7 +223,7 @@ namespace RTC * Calculate CRC32C value of the whole Packet and insert it into the * Checksum field. */ - void SetCRC32cChecksum(); + void WriteCRC32cChecksum(); /** * Validate CRC32C value in the Checksum field. diff --git a/worker/include/RTC/SCTP/packet/errorCauses/InvalidStreamIdentifierErrorCause.hpp b/worker/include/RTC/SCTP/packet/errorCauses/InvalidStreamIdentifierErrorCause.hpp index b271b3c069..8400c4cf89 100644 --- a/worker/include/RTC/SCTP/packet/errorCauses/InvalidStreamIdentifierErrorCause.hpp +++ b/worker/include/RTC/SCTP/packet/errorCauses/InvalidStreamIdentifierErrorCause.hpp @@ -95,6 +95,8 @@ namespace RTC return InvalidStreamIdentifierErrorCause::InvalidStreamIdentifierErrorCauseHeaderLength; } + virtual const std::string ContentToString() const override final; + private: void SetReserved(); }; diff --git a/worker/include/RTC/SCTP/packet/errorCauses/MissingMandatoryParameterErrorCause.hpp b/worker/include/RTC/SCTP/packet/errorCauses/MissingMandatoryParameterErrorCause.hpp index 8677235dbe..08ee872635 100644 --- a/worker/include/RTC/SCTP/packet/errorCauses/MissingMandatoryParameterErrorCause.hpp +++ b/worker/include/RTC/SCTP/packet/errorCauses/MissingMandatoryParameterErrorCause.hpp @@ -106,6 +106,8 @@ namespace RTC return MissingMandatoryParameterErrorCause::MissingMandatoryParameterErrorCauseHeaderLength; } + virtual const std::string ContentToString() const override final; + private: void SetNumberOfMissingParameters(uint32_t value); }; diff --git a/worker/include/RTC/SCTP/packet/errorCauses/NoUserDataErrorCause.hpp b/worker/include/RTC/SCTP/packet/errorCauses/NoUserDataErrorCause.hpp index 209abb4767..3c497e2b9d 100644 --- a/worker/include/RTC/SCTP/packet/errorCauses/NoUserDataErrorCause.hpp +++ b/worker/include/RTC/SCTP/packet/errorCauses/NoUserDataErrorCause.hpp @@ -93,6 +93,8 @@ namespace RTC { return NoUserDataErrorCause::NoUserDataErrorCauseHeaderLength; } + + virtual const std::string ContentToString() const override final; }; } // namespace SCTP } // namespace RTC diff --git a/worker/include/RTC/SCTP/packet/errorCauses/ProtocolViolationErrorCause.hpp b/worker/include/RTC/SCTP/packet/errorCauses/ProtocolViolationErrorCause.hpp index 67fd397866..51fa691341 100644 --- a/worker/include/RTC/SCTP/packet/errorCauses/ProtocolViolationErrorCause.hpp +++ b/worker/include/RTC/SCTP/packet/errorCauses/ProtocolViolationErrorCause.hpp @@ -3,6 +3,7 @@ #include "common.hpp" #include "RTC/SCTP/packet/ErrorCause.hpp" +#include namespace RTC { @@ -88,8 +89,12 @@ namespace RTC void SetAdditionalInformation(const uint8_t* info, uint16_t infoLength); + void SetAdditionalInformation(const std::string& info); + protected: ProtocolViolationErrorCause* SoftClone(const uint8_t* buffer) const final; + + virtual const std::string ContentToString() const override final; }; } // namespace SCTP } // namespace RTC diff --git a/worker/include/RTC/SCTP/packet/errorCauses/StaleCookieErrorCause.hpp b/worker/include/RTC/SCTP/packet/errorCauses/StaleCookieErrorCause.hpp index 6b1bad9a99..88fbca98cd 100644 --- a/worker/include/RTC/SCTP/packet/errorCauses/StaleCookieErrorCause.hpp +++ b/worker/include/RTC/SCTP/packet/errorCauses/StaleCookieErrorCause.hpp @@ -93,6 +93,8 @@ namespace RTC { return StaleCookieErrorCause::StaleCookieErrorCauseHeaderLength; } + + virtual const std::string ContentToString() const override final; }; } // namespace SCTP } // namespace RTC diff --git a/worker/include/RTC/SCTP/packet/errorCauses/UserInitiatedAbortErrorCause.hpp b/worker/include/RTC/SCTP/packet/errorCauses/UserInitiatedAbortErrorCause.hpp index f0863b4eaa..c0e404a4f0 100644 --- a/worker/include/RTC/SCTP/packet/errorCauses/UserInitiatedAbortErrorCause.hpp +++ b/worker/include/RTC/SCTP/packet/errorCauses/UserInitiatedAbortErrorCause.hpp @@ -90,6 +90,8 @@ namespace RTC protected: UserInitiatedAbortErrorCause* SoftClone(const uint8_t* buffer) const final; + + virtual const std::string ContentToString() const override final; }; } // namespace SCTP } // namespace RTC diff --git a/worker/include/RTC/SCTP/packet/parameters/StateCookieParameter.hpp b/worker/include/RTC/SCTP/packet/parameters/StateCookieParameter.hpp index 5b95538605..63c90a77e3 100644 --- a/worker/include/RTC/SCTP/packet/parameters/StateCookieParameter.hpp +++ b/worker/include/RTC/SCTP/packet/parameters/StateCookieParameter.hpp @@ -2,6 +2,7 @@ #define MS_RTC_SCTP_STATE_COOKIE_PARAMETER_HPP #include "common.hpp" +#include "RTC/SCTP/association/NegotiatedCapabilities.hpp" #include "RTC/SCTP/packet/Parameter.hpp" namespace RTC @@ -88,6 +89,23 @@ namespace RTC void SetCookie(const uint8_t* cookie, uint16_t cookieLength); + /** + * Write a locally generated StateCookie in place within the Cookie + * field. + * + * This method is more performant than SetCookie() since it doesn't + * require neither the allocation of a StateCookie class instance nor a + * copy of its buffer to the StateCookieParameter. + */ + void WriteStateCookieInPlace( + uint32_t localVerificationTag, + uint32_t remoteVerificationTag, + uint32_t localInitialTsn, + uint32_t remoteInitialTsn, + uint32_t remoteAdvertisedReceiverWindowCredit, + uint64_t tieTag, + const NegotiatedCapabilities& negotiatedCapabilities); + protected: StateCookieParameter* SoftClone(const uint8_t* buffer) const final; }; diff --git a/worker/include/RTC/SCTP/packet/parameters/SupportedExtensionsParameter.hpp b/worker/include/RTC/SCTP/packet/parameters/SupportedExtensionsParameter.hpp index 56e08efa79..c1c930eed9 100644 --- a/worker/include/RTC/SCTP/packet/parameters/SupportedExtensionsParameter.hpp +++ b/worker/include/RTC/SCTP/packet/parameters/SupportedExtensionsParameter.hpp @@ -87,6 +87,19 @@ namespace RTC Utils::Byte::Get1Byte(GetVariableLengthValuePointer(), idx)); } + bool IncludesChunkType(Chunk::ChunkType chunkType) const + { + for (size_t idx{ 0 }; idx < GetNumberOfChunkTypes(); ++idx) + { + if (chunkType == GetChunkTypeAt(idx)) + { + return true; + } + } + + return false; + } + void AddChunkType(Chunk::ChunkType chunkType); protected: diff --git a/worker/include/Utils.hpp b/worker/include/Utils.hpp index 98989628ab..26f0407e51 100644 --- a/worker/include/Utils.hpp +++ b/worker/include/Utils.hpp @@ -232,6 +232,7 @@ namespace Utils { public: static void ClassInit(); + static void ClassDestroy(); template diff --git a/worker/include/handles/BackoffTimerHandle.hpp b/worker/include/handles/BackoffTimerHandle.hpp new file mode 100644 index 0000000000..9419a5b19b --- /dev/null +++ b/worker/include/handles/BackoffTimerHandle.hpp @@ -0,0 +1,143 @@ +#ifndef MS_BACKOFF_TIMER_HANDLE_HPP +#define MS_BACKOFF_TIMER_HANDLE_HPP + +#include "common.hpp" +#include "handles/TimerHandle.hpp" +#include // std::numeric_limits() + +class BackoffTimerHandle : public TimerHandle::Listener +{ +public: + class Listener + { + public: + virtual ~Listener() = default; + + public: + /** + * Invoked on timeout expiration. The parent can modify the base + * timeout given as reference and affect the next timeout duration. + * + * @remarks + * If the caller deletes this instance of SmartTimer within the callback + * it must signal it be setting `stop` to true. + */ + virtual void OnTimer(BackoffTimerHandle* backoffTimer, uint64_t& baseTimeout, bool& stop) = 0; + }; + +public: + enum class BackoffAlgorithm + { + // The base duration will be used for any restart. + FIXED, + // An exponential backoff is used for restarts, with a 2x multiplier, + // meaning that every restart will use a duration that is twice as long as + // the previous. + EXPONENTIAL, + }; + +public: + static constexpr uint64_t MaxTimeout{ std::numeric_limits::max() / 2 }; + +public: + explicit BackoffTimerHandle( + /** + * Listener on which OnTimer() callback will be invoked. + */ + Listener* listener, + /** + * Base timeout duration. + */ + uint64_t baseTimeout, + /** + * Backoff algorithm. + */ + BackoffAlgorithm backoffAlgorithm, + /** + * Maximum duration of the backoff timeout. If no value is given, no + * limit is set. + */ + std::optional maxBackoffTimeout, + /** + * Maximum number of restarts. If no value is given, it will restart + * forever until stopped. + */ + std::optional maxRestarts); + + BackoffTimerHandle& operator=(const BackoffTimerHandle&) = delete; + + BackoffTimerHandle(const BackoffTimerHandle&) = delete; + + ~BackoffTimerHandle(); + +public: + /** + * Start the smart timer (if it's stopped) or restart it (if already + * running). It will reset the timeout count. + */ + void Start(); + + /** + * Stop the smart timer. It will reset the timeout count. + */ + void Stop(); + + /** + * Restart the smart timer (if it's active) or start it. It will reset the + * timeout count. + */ + void Restart(); + + /** + * Get the base timeout duration. + */ + uint64_t GetBaseTimeout() const + { + return this->baseTimeout; + } + + /** + * Set the base timeout duration. It will be applied after the next timeout + * and effective duration can be larger if backoff algorithm is exponential. + */ + void SetBaseTimeout(uint64_t baseTimeout); + + /** + * Whether the smart timer is running. Useful to check if this smart timer + * will timeout again within the OnTimer() callback. + */ + bool IsActive() const + { + return this->active; + } + + /** + * Number of times the timer has expired. + */ + size_t GetTimeoutCount() const + { + return this->timeoutCount; + } + +private: + uint64_t ComputeNextTimeout() const; + + /* Pure virtual methods inherited from TimerHandle::Listener. */ +public: + void OnTimer(TimerHandle* timer) override; + +private: + // Passed by argument. + Listener* listener{ nullptr }; + uint64_t baseTimeout{ 0 }; + BackoffAlgorithm backoffAlgorithm; + std::optional maxBackoffTimeout; + std::optional maxRestarts; + // Allocated by this. + TimerHandle* timer{ nullptr }; + // Others. + bool active{ false }; + size_t timeoutCount{ 0 }; +}; + +#endif diff --git a/worker/include/handles/TimerHandle.hpp b/worker/include/handles/TimerHandle.hpp index 7948ec608f..109eebb263 100644 --- a/worker/include/handles/TimerHandle.hpp +++ b/worker/include/handles/TimerHandle.hpp @@ -25,7 +25,6 @@ class TimerHandle public: void Start(uint64_t timeout, uint64_t repeat = 0); void Stop(); - void Reset(); void Restart(); uint64_t GetTimeout() const { diff --git a/worker/meson.build b/worker/meson.build index cb48c93bf3..8928ea1073 100644 --- a/worker/meson.build +++ b/worker/meson.build @@ -91,6 +91,7 @@ common_sources = [ 'src/Utils/File.cpp', 'src/Utils/IP.cpp', 'src/Utils/String.cpp', + 'src/handles/BackoffTimerHandle.cpp', 'src/handles/SignalHandle.cpp', 'src/handles/TcpConnectionHandle.cpp', 'src/handles/TcpServerHandle.cpp', @@ -197,6 +198,7 @@ common_sources = [ 'src/RTC/RTCP/XrReceiverReferenceTime.cpp', 'src/RTC/SCTP/association/NegotiatedCapabilities.cpp', 'src/RTC/SCTP/association/Socket.cpp', + 'src/RTC/SCTP/association/SocketMetrics.cpp', 'src/RTC/SCTP/association/StateCookie.cpp', 'src/RTC/SCTP/association/TransmissionControlBlock.cpp', 'src/RTC/SCTP/packet/Packet.cpp', @@ -449,6 +451,7 @@ test_sources = [ 'test/src/RTC/RTCP/TestPacket.cpp', 'test/src/RTC/RTCP/TestXr.cpp', 'test/src/RTC/SCTP/sctpCommon.cpp', + 'test/src/RTC/SCTP/association/TestNegotiatedCapabilities.cpp', 'test/src/RTC/SCTP/association/TestStateCookie.cpp', 'test/src/RTC/SCTP/packet/TestPacket.cpp', 'test/src/RTC/SCTP/packet/chunks/TestDataChunk.cpp', diff --git a/worker/src/RTC/SCTP/association/NegotiatedCapabilities.cpp b/worker/src/RTC/SCTP/association/NegotiatedCapabilities.cpp index e6a1ac2b28..e90b520336 100644 --- a/worker/src/RTC/SCTP/association/NegotiatedCapabilities.cpp +++ b/worker/src/RTC/SCTP/association/NegotiatedCapabilities.cpp @@ -3,11 +3,79 @@ #include "RTC/SCTP/association/NegotiatedCapabilities.hpp" #include "Logger.hpp" +#include "RTC/SCTP/packet/parameters/ForwardTsnSupportedParameter.hpp" +#include "RTC/SCTP/packet/parameters/SupportedExtensionsParameter.hpp" +#include "RTC/SCTP/packet/parameters/ZeroChecksumAcceptableParameter.hpp" namespace RTC { namespace SCTP { + /* Class methods. */ + + NegotiatedCapabilities NegotiatedCapabilities::Factory( + SocketOptions socketOptions, InitOrInitAckChunkVariant remoteChunk) + { + MS_TRACE(); + + // Here it's guaranteed that `remoteChunk` it's a InitChunk or + // InitAckChunk, and both classes provide same methods (the ones used in + // the lambda). + return std::visit( + [&](const auto* remoteChunk) + { + NegotiatedCapabilities negotiatedCapabilities{}; + + auto* remoteSupportedExtensionsParameter = + remoteChunk->template GetFirstParameterOfType(); + auto* remoteForwardTsnSupportedParameter = + remoteChunk->template GetFirstParameterOfType(); + auto* remoteZeroChecksumAcceptableParameter = + remoteChunk->template GetFirstParameterOfType(); + + negotiatedCapabilities.maxOutboundStreams = + std::min(socketOptions.maxOutboundStreams, remoteChunk->GetNumberOfInboundStreams()); + + negotiatedCapabilities.maxInboundStreams = + std::min(socketOptions.maxInboundStreams, remoteChunk->GetNumberOfOutboundStreams()); + + // Partial Reliability Extension is negotiated if we desire it and + // peer announces support via Forward-TSN-Supported Parameter or via + // Supported Extensions Parameter. + negotiatedCapabilities.partialReliability = + socketOptions.partialReliability && + (remoteForwardTsnSupportedParameter || + (remoteSupportedExtensionsParameter && + remoteSupportedExtensionsParameter->IncludesChunkType(Chunk::ChunkType::FORWARD_TSN))); + + // Message Interleaving is negotiated if we desire it and peer + // announces support via Supported Extensions Parameter. + negotiatedCapabilities.messageInterleaving = + socketOptions.messageInterleaving && remoteSupportedExtensionsParameter && + remoteSupportedExtensionsParameter->IncludesChunkType(Chunk::ChunkType::I_DATA) && + remoteSupportedExtensionsParameter->IncludesChunkType(Chunk::ChunkType::I_FORWARD_TSN); + + // Stream Reconfiguration is negotiated if peer announces support via + // Supported Extensions Parameter. + negotiatedCapabilities.reconfig = + remoteSupportedExtensionsParameter && + remoteSupportedExtensionsParameter->IncludesChunkType(Chunk::ChunkType::RE_CONFIG); + + // Alternate Error Detection Method for Zero Checksum is negotiated + // if we desire it and peer announces the same non-none alternate + // error detection method. + negotiatedCapabilities.zeroChecksum = + socketOptions.zeroChecksumAlternateErrorDetectionMethod != + ZeroChecksumAcceptableParameter::AlternateErrorDetectionMethod::NONE && + remoteZeroChecksumAcceptableParameter && + remoteZeroChecksumAcceptableParameter->GetAlternateErrorDetectionMethod() == + socketOptions.zeroChecksumAlternateErrorDetectionMethod; + + return negotiatedCapabilities; + }, + remoteChunk); + } + /* Instance methods. */ void NegotiatedCapabilities::Dump(int indentation) const diff --git a/worker/src/RTC/SCTP/association/Socket.cpp b/worker/src/RTC/SCTP/association/Socket.cpp index 42760b0320..d74054087d 100644 --- a/worker/src/RTC/SCTP/association/Socket.cpp +++ b/worker/src/RTC/SCTP/association/Socket.cpp @@ -3,14 +3,81 @@ #include "RTC/SCTP/association/Socket.hpp" #include "Logger.hpp" +#include "Utils.hpp" +#include "RTC/Consts.hpp" +#include "RTC/SCTP/association/StateCookie.hpp" +#include "RTC/SCTP/packet/errorCauses/ProtocolViolationErrorCause.hpp" +#include "RTC/SCTP/packet/errorCauses/UnrecognizedChunkTypeErrorCause.hpp" +#include "RTC/SCTP/packet/parameters/ForwardTsnSupportedParameter.hpp" +#include "RTC/SCTP/packet/parameters/StateCookieParameter.hpp" +#include "RTC/SCTP/packet/parameters/SupportedExtensionsParameter.hpp" +#include "RTC/SCTP/packet/parameters/ZeroChecksumAcceptableParameter.hpp" +#include // std::numeric_limits() +#include // std::ostringstream +#include // std::is_same_v namespace RTC { namespace SCTP { + /* Static. */ + + thread_local static uint8_t FactoryBuffer[RTC::Consts::MaxSafeMtuSizeForSctp]; + + /* Class methods. */ + + constexpr std::string_view Socket::State2String(Socket::State state) + { + // NOTE: We cannot use MS_TRACE() here because clang in Linux will + // comlain about "read of non-constexpr variable 'configuration' is not + // allowed in a constant expression". + + switch (state) + { + case Socket::State::CLOSED: + return "CLOSED"; + case Socket::State::COOKIE_WAIT: + return "COOKIE_WAIT"; + case Socket::State::COOKIE_ECHOED: + return "COOKIE_ECHOED"; + case Socket::State::ESTABLISHED: + return "ESTABLISHED"; + case Socket::State::SHUTDOWN_PENDING: + return "SHUTDOWN_PENDING"; + case Socket::State::SHUTDOWN_SENT: + return "SHUTDOWN_SENT"; + case Socket::State::SHUTDOWN_RECEIVED: + return "SHUTDOWN_RECEIVED"; + case Socket::State::SHUTDOWN_ACK_SENT: + return "SHUTDOWN_ACK_SENT"; + } + } + /* Instance methods. */ - Socket::Socket(Socket::SocketOptions options) : options(options) + Socket::Socket(SocketOptions options, Listener* listener) + : options(options), listener(listener), + t1InitTimer( + std::make_unique( + /*listener*/ this, + /*baseTimeout*/ options.t1InitTimeout, + /*backoffAlgorithm*/ BackoffTimerHandle::BackoffAlgorithm::EXPONENTIAL, + /*maxBackoffTimeout*/ options.timerMaxBackoffTimeout, + /*maxRestarts*/ options.maxInitRetransmits)), + t1CookieTimer( + std::make_unique( + /*listener*/ this, + /*baseTimeout*/ options.t1CookieTimeout, + /*backoffAlgorithm*/ BackoffTimerHandle::BackoffAlgorithm::EXPONENTIAL, + /*maxBackoffTimeout*/ options.timerMaxBackoffTimeout, + /*maxRestarts*/ options.maxInitRetransmits)), + t2ShutdownTimer( + std::make_unique( + /*listener*/ this, + /*baseTimeout*/ options.t2ShutdownTimeout, + /*backoffAlgorithm*/ BackoffTimerHandle::BackoffAlgorithm::EXPONENTIAL, + /*maxBackoffTimeout*/ options.timerMaxBackoffTimeout, + /*maxRestarts*/ options.maxRetransmits)) { MS_TRACE(); } @@ -20,18 +87,972 @@ namespace RTC MS_TRACE(); } - void Socket::Dump(int /*indentation*/) const + void Socket::Dump(int indentation) const + { + MS_TRACE(); + + auto stateStringView = Socket::State2String(this->state); + + MS_DUMP_CLEAN(indentation, ""); + MS_DUMP_CLEAN( + indentation, " state: %.*s", static_cast(stateStringView.size()), stateStringView.data()); + this->metrics.Dump(indentation); + MS_DUMP_CLEAN(indentation, ""); + } + + void Socket::Associate() + { + MS_TRACE(); + + if (this->state != State::CLOSED) + { + auto stateStringView = Socket::State2String(this->state); + + MS_DEBUG_TAG( + sctp, + "cannot initiate the association since state is not CLOSED but %.*s", + static_cast(stateStringView.size()), + stateStringView.data()); + + return; + } + + this->preTcb.localVerificationTag = + Utils::Crypto::GetRandomUInt(1, std::numeric_limits::max()); + this->preTcb.localInitialTsn = + Utils::Crypto::GetRandomUInt(0, std::numeric_limits::max()); + + SendInitChunk(); + + this->t1InitTimer->Start(); + + SetState(State::COOKIE_WAIT, "Associate() called"); + } + + // TODO: Should the caller call free packet after calling this method? or us? + void Socket::ReceivePacket(const Packet* receivedPacket) { MS_TRACE(); + this->metrics.rxPacketsCount++; + + /* Verify Packet. */ + + if (!ValidateReceivedPacket(receivedPacket)) + { + MS_WARN_TAG(sctp, "Packet verification failed, discarded"); + + return; + } + + // TODO + // MaybeSendShutdownOnPacketReceived(receivedPacket); + + for (auto it = receivedPacket->ChunksBegin(); it != receivedPacket->ChunksEnd(); ++it) + { + const auto* receivedChunk = *it; + + if (!ProcessReceivedChunk(receivedPacket, receivedChunk)) + { + break; + } + } + // TODO + // if (tcb_ != nullptr) { + // tcb_->data_tracker().ObservePacketEnd(); + // tcb_->MaybeSendSack(); + // } + } + + void Socket::SetState(State state, const std::string& reason) + { + MS_TRACE(); + + auto stateStringView = Socket::State2String(state); + + if (state == this->state) + { + MS_WARN_TAG( + sctp, + "Socket state is already %.*s (reason:'%s')", + static_cast(stateStringView.size()), + stateStringView.data(), + reason.c_str()); + + return; + } + + auto previousStateStringView = Socket::State2String(this->state); + + MS_WARN_TAG( + sctp, + "Socket state changed from %.*s to %.*s (reason:'%s')", + static_cast(previousStateStringView.size()), + previousStateStringView.data(), + static_cast(stateStringView.size()), + stateStringView.data(), + reason.c_str()); + + this->state = state; + } + + void Socket::AddCapabilitiesParametersToInitOrInitAckChunk(Chunk* chunk) const + { + MS_TRACE(); + + MS_ASSERT( + chunk->GetType() == Chunk::ChunkType::INIT || chunk->GetType() == Chunk::ChunkType::INIT_ACK, + "chunk must be an INIT or INIT_ACK"); + + auto* supportedExtensionsParameter = + chunk->BuildParameterInPlace(); + + supportedExtensionsParameter->AddChunkType(Chunk::ChunkType::RE_CONFIG); + + if (this->options.partialReliability) + { + supportedExtensionsParameter->AddChunkType(Chunk::ChunkType::FORWARD_TSN); + + auto* forwardTsnSupportedParameter = + chunk->BuildParameterInPlace(); + + forwardTsnSupportedParameter->Consolidate(); + } + + if (this->options.messageInterleaving) + { + supportedExtensionsParameter->AddChunkType(Chunk::ChunkType::I_DATA); + supportedExtensionsParameter->AddChunkType(Chunk::ChunkType::I_FORWARD_TSN); + } + + if ( + this->options.zeroChecksumAlternateErrorDetectionMethod != + ZeroChecksumAcceptableParameter::AlternateErrorDetectionMethod::NONE) + { + auto* zeroChecksumAcceptableParameter = + chunk->BuildParameterInPlace(); + + zeroChecksumAcceptableParameter->SetAlternateErrorDetectionMethod( + this->options.zeroChecksumAlternateErrorDetectionMethod); + zeroChecksumAcceptableParameter->Consolidate(); + } + + supportedExtensionsParameter->Consolidate(); + } + + void Socket::CreateTransmissionControlBlock( + uint32_t localVerificationTag, + uint32_t remoteVerificationTag, + uint32_t localInitialTsn, + uint32_t remoteInitialTsn, + uint32_t remoteAdvertisedReceiverWindowCredit, + uint64_t tieTag, + const NegotiatedCapabilities& negotiatedCapabilities) + { + MS_TRACE(); + + this->tcb = std::make_unique( + localVerificationTag, + remoteVerificationTag, + localInitialTsn, + remoteInitialTsn, + remoteAdvertisedReceiverWindowCredit, + tieTag, + negotiatedCapabilities); + + this->metrics.messageInterleaving = negotiatedCapabilities.messageInterleaving; + this->metrics.zeroChecksum = negotiatedCapabilities.zeroChecksum; + } + + Packet* Socket::CreatePacket() const + { + MS_TRACE(); + + uint32_t verificationTag = this->tcb ? this->tcb->GetRemoteVerificationTag() : 0; + + return CreatePacketWithVerificationTag(verificationTag); + } + + Packet* Socket::CreatePacketWithVerificationTag(uint32_t verificationTag) const + { + MS_TRACE(); + + auto* packet = Packet::Factory(FactoryBuffer, sizeof(FactoryBuffer)); + + packet->SetSourcePort(this->options.sourcePort); + packet->SetDestinationPort(this->options.destinationPort); + packet->SetVerificationTag(verificationTag); + + return packet; + } + + void Socket::SendPacket(Packet* packet, std::optional writeChecksum) + { + MS_TRACE(); + + // Decide whether to write the CRC32c Checksum field in the Packet or + // not. Note that in same special cases the decision is made by the + // caller of SendPacket() which explicitly sets the value of the + // `writeChecksum` argument. + + // If `writeChecksum` is explicitly set to true then write the checksum. + if (writeChecksum.has_value() && *writeChecksum) + { + packet->WriteCRC32cChecksum(); + } + // If `writeChecksum` is explicitly set to false then do not write the + // checksum. + else if (writeChecksum.has_value() && !*writeChecksum) + { + // Nothing to do. + } + // If `writeChecksum` is not set, decide based on TCB. + else if (!this->tcb || !this->tcb->GetNegotiatedCapabilities().zeroChecksum) + { + packet->WriteCRC32cChecksum(); + } + + // Send the Packet. + this->listener->OnSocketSendSctpPacket(this, packet); } void Socket::SendInitChunk() { MS_TRACE(); + auto* packet = CreatePacket(); + + // Insert an INIT Chunk in the Packet. + auto* initChunk = packet->BuildChunkInPlace(); + + initChunk->SetInitiateTag(this->preTcb.localVerificationTag); + initChunk->SetAdvertisedReceiverWindowCredit(this->options.localAdvertisedReceiverWindowCredit); + initChunk->SetNumberOfOutboundStreams(this->options.maxOutboundStreams); + initChunk->SetNumberOfInboundStreams(this->options.maxInboundStreams); + initChunk->SetInitialTsn(this->preTcb.localInitialTsn); + + // Insert capabilities related Parameters in the INIT Chunk. + AddCapabilitiesParametersToInitOrInitAckChunk(initChunk); + + initChunk->Consolidate(); + + // https://datatracker.ietf.org/doc/html/rfc9653#section-5.2 + // When a sender sends a packet containing an INIT chunk, it MUST include + // a correct CRC32c checksum in the packet containing the INIT chunk. + SendPacket(packet, /*writeChecksum*/ true); + + delete packet; + } + + void Socket::SendShutdownAckChunk() + { + MS_TRACE(); + + auto* packet = CreatePacket(); + auto* shutdownAckChunk = packet->BuildChunkInPlace(); + + shutdownAckChunk->Consolidate(); + + SendPacket(packet); + + delete packet; + + // TODO + // this->t2ShutdownTimer->SetBaseTimeout(this->tcb->GetCurrentRto()); + this->t2ShutdownTimer->Restart(); + } + + bool Socket::ValidateReceivedPacket(const Packet* receivedPacket) + { + MS_TRACE(); + + uint32_t localVerificationTag = this->tcb ? this->tcb->GetLocalVerificationTag() : 0; + + // "When an endpoint receives an SCTP packet with the Verification Tag + // set to 0, it SHOULD verify that the packet contains only an INIT + // chunk. Otherwise, the receiver MUST silently discard the packet." + // + // @see https://datatracker.ietf.org/doc/html/rfc9260#name-exceptions-in-verification- + if (receivedPacket->GetVerificationTag() == 0) + { + if (receivedPacket->GetChunksCount() == 1 && receivedPacket->GetChunkAt(0)->GetType() == Chunk::ChunkType::INIT) + { + return true; + } + else + { + MS_WARN_TAG( + sctp, + "Packet with Verification Tag 0 must have a single Chunk and it must be an INIT Chunk, packet discarded"); + + // TODO: Emit error? + return false; + } + } + + if (receivedPacket->GetChunksCount() >= 1 && receivedPacket->GetChunkAt(0)->GetType() == Chunk::ChunkType::INIT_ACK) + { + if (receivedPacket->GetVerificationTag() == this->preTcb.localVerificationTag) + { + return true; + } + else + { + MS_WARN_TAG( + sctp, + "invalid Verification Tag %" PRIu32 " (should be %" PRIu32 ")", + receivedPacket->GetVerificationTag(), + this->preTcb.localVerificationTag); + + // TODO: Emit error? + return false; + } + } + + // "The receiver of an ABORT chunk MUST accept the packet if the + // Verification Tag field of the packet matches its own tag and the T bit + // is not set OR if it is set to its Peer's Tag and the T bit is set in + // the Chunk Flags. Otherwise, the receiver MUST silently discard the + // packet and take no further action." + // + // @see https://datatracker.ietf.org/doc/html/rfc9260#section-8.5.1 + if (receivedPacket->GetChunksCount() == 1 && receivedPacket->GetChunkAt(0)->GetType() == Chunk::ChunkType::ABORT) + { + auto* abortChunk = static_cast(receivedPacket->GetChunkAt(0)); + + // We cannot verify the Verification Tag so assume it's okey. + if (abortChunk->GetT() && !this->tcb) + { + return true; + } + else if ( + (!abortChunk->GetT() && receivedPacket->GetVerificationTag() == localVerificationTag) || + (abortChunk->GetT() && + receivedPacket->GetVerificationTag() == this->tcb->GetRemoteVerificationTag())) + { + return true; + } + else + { + MS_WARN_TAG( + sctp, + "ABORT Chunk Verification Tag %" PRIu32 " is wrong, packet discarded", + receivedPacket->GetVerificationTag()); + + // TODO: Emit error? + return false; + } + } + + // This is handled in ProcessCookieEchoChunk(). + // + // @see https://datatracker.ietf.org/doc/html/rfc9260#name-handle-a-cookie-echo-chunk- + if (receivedPacket->GetChunksCount() >= 1 && receivedPacket->GetChunkAt(0)->GetType() == Chunk::ChunkType::COOKIE_ECHO) + { + return true; + } + + // "The receiver of a SHUTDOWN COMPLETE shall accept the packet if the + // Verification Tag field of the packet matches its own tag and the T bit is + // not set OR if it is set to its peer's tag and the T bit is set in the + // Chunk Flags. Otherwise, the receiver MUST silently discard the packet + // and take no further action." + // + // @see https://datatracker.ietf.org/doc/html/rfc9260#section-8.5.1 + if (receivedPacket->GetChunksCount() == 1 && receivedPacket->GetChunkAt(0)->GetType() == Chunk::ChunkType::SHUTDOWN_COMPLETE) + { + auto* shutdownCompleteChunk = + static_cast(receivedPacket->GetChunkAt(0)); + + // We cannot verify the Verification Tag so assume it's okey. + if (shutdownCompleteChunk->GetT() && !this->tcb) + { + return true; + } + else if ( + (!shutdownCompleteChunk->GetT() && + receivedPacket->GetVerificationTag() == localVerificationTag) || + (shutdownCompleteChunk->GetT() && + receivedPacket->GetVerificationTag() == this->tcb->GetRemoteVerificationTag())) + { + return true; + } + else + { + MS_WARN_TAG( + sctp, + "SHUTDOWN_COMPLETE Chunk Verification Tag %" PRIu32 " is wrong, packet discarded", + receivedPacket->GetVerificationTag()); + + // TODO: Emit error? + return false; + } + } + + // "When receiving an SCTP packet, the endpoint MUST ensure that the + // value in the Verification Tag field of the received SCTP packet + // matches its own tag. If the received Verification Tag value does not + // match the receiver's own tag value, the receiver MUST silently discard + // the packet and MUST NOT process it any further, except for those cases + // listed in Section 8.5.1 below." + // + // @see https://datatracker.ietf.org/doc/html/rfc9260#section-8.5 + if (receivedPacket->GetVerificationTag() == localVerificationTag) + { + return true; + } + else + { + MS_WARN_TAG( + sctp, + "invalid Verification Tag %" PRIu32 " (should be %" PRIu32 ")", + receivedPacket->GetVerificationTag(), + localVerificationTag); + + // TODO: Emit error? + return false; + } + } + + bool Socket::ProcessReceivedChunk(const Packet* receivedPacket, const Chunk* receivedChunk) + { + MS_TRACE(); + + switch (receivedChunk->GetType()) + { + case Chunk::ChunkType::DATA: + { + ProcessReceivedDataChunk(receivedPacket, static_cast(receivedChunk)); + + break; + } + + case Chunk::ChunkType::INIT: + { + ProcessReceivedInitChunk(receivedPacket, static_cast(receivedChunk)); + + break; + } + + case Chunk::ChunkType::INIT_ACK: + { + ProcessReceivedInitAckChunk(receivedPacket, static_cast(receivedChunk)); + + break; + } + + case Chunk::ChunkType::SACK: + { + ProcessReceivedSackChunk(receivedPacket, static_cast(receivedChunk)); + + break; + } + + case Chunk::ChunkType::HEARTBEAT_REQUEST: + { + ProcessReceivedHeartbeatRequestChunk( + receivedPacket, static_cast(receivedChunk)); + + break; + } + + default: + { + return ProcessReceivedUnknownChunk( + receivedPacket, static_cast(receivedChunk)); + } + } + + return true; + } + + void Socket::ProcessReceivedDataChunk(const Packet* receivedPacket, const DataChunk* receivedDataChunk) + { + MS_TRACE(); + + // TODO + } + + void Socket::ProcessReceivedInitChunk( + const Packet* /*receivedPacket*/, const InitChunk* receivedInitChunk) + { + MS_TRACE(); + + // Verify some fields that cannot be 0. + if ( + receivedInitChunk->GetInitiateTag() == 0 || + receivedInitChunk->GetNumberOfOutboundStreams() == 0 or + receivedInitChunk->GetNumberOfInboundStreams() == 0) + { + MS_WARN_TAG( + sctp, + "invalid value 0 in Initiate Tag or Number of Outbound Streams or Number of Inbound Streams in received INIT Chunk, aborting association"); + + auto* packet = CreatePacketWithVerificationTag(0); + auto* abortChunk = packet->BuildChunkInPlace(); + + // NOTE: We are not setting the Verification Tag expected by the peer + // so must set be T to 1. + abortChunk->SetT(true); + + auto* protocolViolationErrorCause = + abortChunk->BuildErrorCauseInPlace(); + + protocolViolationErrorCause->SetAdditionalInformation( + "invalid value 0 in Initiate Tag or Number of Outbound Streams or Number of Inbound Streams in received INIT chunk"); + + protocolViolationErrorCause->Consolidate(); + abortChunk->Consolidate(); + + SendPacket(packet); + + delete packet; + + // TODO + // InternalClose(ErrorKind::kProtocolViolation, "Received invalid INIT Chunk"); + + return; + } + + // "If an endpoint is in the SHUTDOWN-ACK-SENT state and receives an INIT + // chunk (e.g., if the SHUTDOWN COMPLETE chunk was lost) with source and + // destination transport addresses (either in the IP addresses or in the + // INIT chunk) that belong to this association, it SHOULD discard the + // INIT chunk and retransmit the SHUTDOWN ACK chunk." + // + // @see https://datatracker.ietf.org/doc/html/rfc9260#section-9.2 + if (this->state == State::SHUTDOWN_ACK_SENT) + { + MS_DEBUG_TAG( + sctp, "INIT Chunk received in SHUTDOWN_ACK_SENT state, retransmitting SHUTDOWN_ACK Chunk"); + + SendShutdownAckChunk(); + + return; + } + + uint64_t tieTag{ 0 }; + uint32_t localVerificationTag; + uint32_t localInitialTsn; + + switch (this->state) + { + case State::CLOSED: + { + MS_DEBUG_TAG(sctp, "INIT Chunk received in CLOSED state (normal scenario)"); + + localVerificationTag = + Utils::Crypto::GetRandomUInt(1, std::numeric_limits::max()); + localInitialTsn = + Utils::Crypto::GetRandomUInt(0, std::numeric_limits::max()); + + break; + } + + /** + * "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 chunk + * in the COOKIE-WAIT state, an endpoint MUST respond with an INIT ACK + * chunk using the same parameters it sent in its original INIT chunk + * (including its Initiate Tag, unchanged)." + * + * @see https://datatracker.ietf.org/doc/html/rfc9260#section-5.2.1 + */ + case State::COOKIE_WAIT: + case State::COOKIE_ECHOED: + { + MS_DEBUG_TAG(sctp, "INIT Chunk received after sending INIT Chunk (collision, no problem)"); + + localVerificationTag = this->preTcb.localVerificationTag; + localInitialTsn = this->preTcb.localInitialTsn; + + break; + } + + /** + * "The outbound SCTP packet containing this INIT ACK chunk MUST carry + * a Verification Tag value equal to the Initiate Tag found in the + * unexpected INIT chunk. And the INIT ACK chunk MUST contain a new + * Initiate Tag (randomly generated; see Section 5.3.1). Other + * parameters for the endpoint SHOULD be copied from the existing + * parameters of the association (e.g., number of outbound streams) + * into the INIT ACK chunk and cookie." + * + * @see https://datatracker.ietf.org/doc/html/rfc9260#section-5.2.2 + */ + default: + { + AssertHasTcb(); + + MS_DEBUG_TAG(sctp, "INIT Chunk received (probably peer restarted)"); + + localVerificationTag = + Utils::Crypto::GetRandomUInt(1, std::numeric_limits::max()); + + // TODO: Implement this. + // Make the initial TSN make a large jump, so that there is no overlap + // with the old and new association. + // my_initial_tsn = TSN(*tcb_->retransmission_queue().next_tsn() + 1000000); + + // TODO: Remove this when the above TODO is done. + localInitialTsn = + Utils::Crypto::GetRandomUInt(0, std::numeric_limits::max()); + + tieTag = this->tcb->GetTieTag(); + } + } + + MS_DEBUG_TAG( + sctp, + "initiating association [localVerificationTag:%" PRIu32 ", localInitialTsn:%" PRIu32 + ", remoteVerificationTag:%" PRIu32 ", remoteInitialTsn:%" PRIu32 "]", + localVerificationTag, + localInitialTsn, + receivedInitChunk->GetInitiateTag(), + receivedInitChunk->GetInitialTsn()); + + /* Send a Packet with an INIT_ACK Chunk. */ + + auto* packet = CreatePacketWithVerificationTag(receivedInitChunk->GetInitiateTag()); + + // Insert an INIT_ACK Chunk in the Packet. + auto* initAckChunk = packet->BuildChunkInPlace(); + + initAckChunk->SetInitiateTag(localVerificationTag); + initAckChunk->SetAdvertisedReceiverWindowCredit( + this->options.localAdvertisedReceiverWindowCredit); + initAckChunk->SetNumberOfOutboundStreams(this->options.maxOutboundStreams); + initAckChunk->SetNumberOfInboundStreams(this->options.maxInboundStreams); + initAckChunk->SetInitialTsn(localInitialTsn); + + // Insert a StateCookie Parameter in the INIT_ACK Chunk. + auto* stateCookieParameter = initAckChunk->BuildParameterInPlace(); + + const auto negotiatedCapabilities = + NegotiatedCapabilities::Factory(this->options, receivedInitChunk); + + // Write the StateCookie in place in the Parameter. + stateCookieParameter->WriteStateCookieInPlace( + localVerificationTag, + receivedInitChunk->GetInitiateTag(), + localInitialTsn, + receivedInitChunk->GetInitialTsn(), + receivedInitChunk->GetAdvertisedReceiverWindowCredit(), + tieTag, + negotiatedCapabilities); + + stateCookieParameter->Consolidate(); + + // Insert capabilities related Parameters in the INIT_ACK Chunk. + AddCapabilitiesParametersToInitOrInitAckChunk(initAckChunk); + + initAckChunk->Consolidate(); + + SendPacket(packet, /*writeChecksum*/ !negotiatedCapabilities.zeroChecksum); + + delete packet; + } + + void Socket::ProcessReceivedInitAckChunk( + const Packet* /*receivedPacket*/, const InitAckChunk* receivedInitAckChunk) + { + MS_TRACE(); + + // "If an INIT ACK chunk is received by an endpoint in any state other + // than the COOKIE-WAIT or CLOSED state, the endpoint SHOULD discard the + // INIT ACK chunk." + // + // @see https://datatracker.ietf.org/doc/html/rfc9260#name-unexpected-init-ack-chunk + if (this->state != State::COOKIE_WAIT) + { + MS_DEBUG_TAG(sctp, "ignoring received INIT_ACK Chunk when not in COOKIE_WAIT state"); + + return; + } + + auto* stateCookieParameter = + receivedInitAckChunk->GetFirstParameterOfType(); + + if (!stateCookieParameter || !stateCookieParameter->GetCookie()) + { + MS_WARN_TAG( + sctp, "ignoring received INIT_ACK Chunk without StateCookieParameter or without Cookie"); + + auto* packet = CreatePacketWithVerificationTag(this->preTcb.localVerificationTag); + auto* abortChunk = packet->BuildChunkInPlace(); + + // NOTE: We are not setting the Verification Tag expected by the peer + // so must set be T to 1. + abortChunk->SetT(true); + + auto* protocolViolationErrorCause = + abortChunk->BuildErrorCauseInPlace(); + + protocolViolationErrorCause->SetAdditionalInformation( + "INIT_ACK without State Cookie Parameter or without Cookie"); + + protocolViolationErrorCause->Consolidate(); + abortChunk->Consolidate(); + + SendPacket(packet); + + delete packet; + + // TODO + // InternalClose(ErrorKind::kProtocolViolation, "received INIT_ACK Chunk doesn't contain a Cookie"); + + return; + } + + this->metrics.peerImplementation = StateCookie::DetermineSctpImplementation( + stateCookieParameter->GetCookie(), stateCookieParameter->GetCookieLength()); + + this->t1InitTimer->Stop(); + + const auto negotiatedCapabilities = + NegotiatedCapabilities::Factory(this->options, receivedInitAckChunk); + // TODO + // If the connection is re-established (peer restarted, but re-used old + // connection), make sure that all message identifiers are reset and any + // partly sent message is re-sent in full. The same is true when the socket + // is closed and later re-opened, which never happens in WebRTC, but is a + // valid operation on the SCTP level. Note that in case of handover, the + // send queue is already re-configured, and shouldn't be reset. + // send_queue_.Reset(); + + CreateTransmissionControlBlock( + this->preTcb.localVerificationTag, + receivedInitAckChunk->GetInitiateTag(), + this->preTcb.localInitialTsn, + receivedInitAckChunk->GetInitialTsn(), + receivedInitAckChunk->GetAdvertisedReceiverWindowCredit(), + /*tieTag*/ Utils::Crypto::GetRandomUInt(0, std::numeric_limits::max()), + negotiatedCapabilities); + + SetState(State::COOKIE_ECHOED, "INIT_ACK received"); + + // TODO + // The connection isn't fully established just yet. + // tcb_->SetCookieEchoChunk(CookieEchoChunk(cookie->data())); + // tcb_->SendBufferedPackets(callbacks_.Now()); + + this->t1CookieTimer->Start(); + } + + void Socket::ProcessReceivedSackChunk( + const Packet* /*receivedPacket*/, const SackChunk* receivedSackChunk) + { + MS_TRACE(); + + // TODO + } + + void Socket::ProcessReceivedHeartbeatRequestChunk( + const Packet* /*receivedPacket*/, const HeartbeatRequestChunk* receivedHeartbeatRequestChunk) + { + MS_TRACE(); + + // TODO + } + + bool Socket::ProcessReceivedUnknownChunk( + const Packet* /*receivedPacket*/, const UnknownChunk* receivedUnknownChunk) + { + MS_TRACE(); + + auto action = receivedUnknownChunk->GetActionForUnknownChunkType(); + auto skipProcessing = action == Chunk::ActionForUnknownChunkType::SKIP || + action == Chunk::ActionForUnknownChunkType::SKIP_AND_REPORT; + auto reportError = action == Chunk::ActionForUnknownChunkType::STOP_AND_REPORT || + action == Chunk::ActionForUnknownChunkType::SKIP_AND_REPORT; + + if (skipProcessing) + { + MS_DEBUG_TAG( + sctp, + "Chunk with unknown type %" PRIu8 + " received, skipping further processing of Chunks in the Packet", + static_cast(receivedUnknownChunk->GetType())); + } + else + { + MS_DEBUG_TAG( + sctp, + "ignoring received Chunk with unknown type %" PRIu8, + static_cast(receivedUnknownChunk->GetType())); + } + + if (reportError) + { + // TODO: Notify error. + + // If there is TCB (we need correct remote verification tag) send an + // OPERATION_ERROR Chunk with a Unrecognized Chunk Type Error Cause. + if (this->tcb) + { + auto* packet = CreatePacket(); + auto* operationErrorChunk = packet->BuildChunkInPlace(); + auto* unrecognizedChunkTypeErrorCause = + operationErrorChunk->BuildErrorCauseInPlace(); + + unrecognizedChunkTypeErrorCause->SetUnrecognizedChunk( + receivedUnknownChunk->GetBuffer(), receivedUnknownChunk->GetLength()); + + unrecognizedChunkTypeErrorCause->Consolidate(); + operationErrorChunk->Consolidate(); + + SendPacket(packet); + + delete packet; + } + } + + return !skipProcessing; + } + + void Socket::OnT1InitTimer(uint64_t& baseTimeout, bool& stop) + { + MS_TRACE(); + + MS_DEBUG_TAG( + sctp, "T1-init timer expired [timeout count:%zu]", this->t1InitTimer->GetTimeoutCount()); + + AssertState(State::COOKIE_WAIT); + + if (this->t1InitTimer->IsActive()) + { + SendInitChunk(); + } + else + { + // TODO + // InternalClose(ErrorKind::kTooManyRetries, "No INIT_ACK received"); + } + } + + void Socket::OnT1CookieTimer(uint64_t& baseTimeout, bool& stop) + { + MS_TRACE(); + + MS_DEBUG_TAG( + sctp, "T1-cookie timer expired [timeout count:%zu]", this->t1CookieTimer->GetTimeoutCount()); + + AssertState(State::COOKIE_ECHOED); + + if (this->t1CookieTimer->IsActive()) + { + // TODO + // tcb_->SendBufferedPackets(callbacks_.Now()); + } + else + { + // TODO + // InternalClose(ErrorKind::kTooManyRetries, "No COOKIE_ACK received"); + } + } + + void Socket::OnT2ShutdownTimer(uint64_t& baseTimeout, bool& stop) + { + MS_TRACE(); + + MS_DEBUG_TAG( + sctp, + "T2-shutdown timer expired [timeout count:%zu]", + this->t2ShutdownTimer->GetTimeoutCount()); + + // TODO + } + + template + void Socket::AssertState(States... expectedStates) const + { + MS_TRACE(); + + static_assert((std::is_same_v && ...), "all arguments must be of type State"); + + // NOTE: Using fold expression operator. + if ((... || (this->state == expectedStates))) + { + return; + } + + auto currentStateStringView = Socket::State2String(this->state); + std::ostringstream expectedStatesOss; + bool firstExpectedState = true; + + // NOTE: Using fold expression operator. + ((expectedStatesOss << (firstExpectedState ? "" : ", ") << Socket::State2String(expectedStates), + firstExpectedState = false), + ...); + + auto expectedStatesString = expectedStatesOss.str(); + + MS_ABORT( + "current Socket state %.*s does not match any of the given expected states (%s)", + static_cast(currentStateStringView.size()), + currentStateStringView.data(), + expectedStatesString.c_str()); + } + + template + void Socket::AssertNotState(States... unexpectedStates) const + { + MS_TRACE(); + + static_assert((std::is_same_v && ...), "all arguments must be of type State"); + + // NOTE: Using fold expression operator. + if ((... || (this->state == unexpectedStates))) + { + auto currentStateStringView = Socket::State2String(this->state); + std::ostringstream unexpectedStatesOss; + bool firstUnexpectedState = true; + + // NOTE: Using fold expression operator. + ((unexpectedStatesOss << (firstUnexpectedState ? "" : ", ") + << Socket::State2String(unexpectedStates), + firstUnexpectedState = false), + ...); + + auto unexpectedStatesString = unexpectedStatesOss.str(); + + MS_ABORT( + "current Socket state %.*s matches one of the given unexpected states (%s)", + static_cast(currentStateStringView.size()), + currentStateStringView.data(), + unexpectedStatesString.c_str()); + } + } + + void Socket::AssertHasTcb() const + { + MS_TRACE(); + + if (!this->tcb) + { + MS_ABORT("TCB doesn't exist"); + } + } + + void Socket::OnTimer(BackoffTimerHandle* backoffTimer, uint64_t& baseTimeout, bool& stop) + { + MS_TRACE(); + + if (backoffTimer == this->t1InitTimer.get()) + { + OnT1InitTimer(baseTimeout, stop); + } + else if (backoffTimer == this->t1CookieTimer.get()) + { + OnT1CookieTimer(baseTimeout, stop); + } + else if (backoffTimer == this->t2ShutdownTimer.get()) + { + OnT2ShutdownTimer(baseTimeout, stop); + } } } // namespace SCTP } // namespace RTC diff --git a/worker/src/RTC/SCTP/association/SocketMetrics.cpp b/worker/src/RTC/SCTP/association/SocketMetrics.cpp new file mode 100644 index 0000000000..0d636a2262 --- /dev/null +++ b/worker/src/RTC/SCTP/association/SocketMetrics.cpp @@ -0,0 +1,40 @@ +#define MS_CLASS "RTC::SCTP::SocketMetrics" +// #define MS_LOG_DEV_LEVEL 3 + +#include "RTC/SCTP/association/SocketMetrics.hpp" +#include "Logger.hpp" + +namespace RTC +{ + namespace SCTP + { + /* Instance methods. */ + + void SocketMetrics::Dump(int indentation) const + { + MS_TRACE(); + + auto peerImplementationStringView = + StateCookie::SctpImplementation2String(this->peerImplementation); + + MS_DUMP_CLEAN(indentation, ""); + MS_DUMP_CLEAN(indentation, " tx packets count: %" PRIu64, this->txPacketsCount); + MS_DUMP_CLEAN(indentation, " tx messages count: %" PRIu64, this->txMessagesCount); + MS_DUMP_CLEAN(indentation, " rtx packets count: %" PRIu64, this->rtxPacketsCount); + // TODO: More. + MS_DUMP_CLEAN(indentation, " rx packets count: %" PRIu64, this->rxPacketsCount); + MS_DUMP_CLEAN(indentation, " rx messages count: %" PRIu64, this->rxMessagesCount); + // TODO: More. + MS_DUMP_CLEAN( + indentation, + " peer implementation: %.*s", + static_cast(peerImplementationStringView.size()), + peerImplementationStringView.data()); + // TODO: More. + MS_DUMP_CLEAN( + indentation, " message interleaving: %s", this->messageInterleaving ? "yes" : "no"); + MS_DUMP_CLEAN(indentation, " zero checksum: %s", this->zeroChecksum ? "yes" : "no"); + MS_DUMP_CLEAN(indentation, ""); + } + } // namespace SCTP +} // namespace RTC diff --git a/worker/src/RTC/SCTP/association/StateCookie.cpp b/worker/src/RTC/SCTP/association/StateCookie.cpp index 9f289fa4d5..27317f7fd6 100644 --- a/worker/src/RTC/SCTP/association/StateCookie.cpp +++ b/worker/src/RTC/SCTP/association/StateCookie.cpp @@ -11,30 +11,38 @@ namespace RTC { /* Class methods. */ - StateCookie* StateCookie::Parse(const uint8_t* buffer, size_t bufferLength) + bool StateCookie::IsMediasoupStateCookie(const uint8_t* buffer, size_t bufferLength) { MS_TRACE(); if (bufferLength != StateCookie::StateCookieLength) { - MS_WARN_TAG(sctp, "StateCookie buffer length must be %zu", StateCookie::StateCookieLength); - - return nullptr; + return false; } - if (Utils::Byte::Get4Bytes(buffer, 0) != StateCookie::MagicValue1) + if (Utils::Byte::Get8Bytes(buffer, 0) != StateCookie::Magic1) { - MS_WARN_TAG(sctp, "incorrect Magic Value 1"); + return false; + } - return nullptr; + auto* negotiatedCapabilitiesField = reinterpret_cast( + const_cast(buffer) + StateCookie::NegotiatedCapabilitiesOffset); + + if (uint16_t{ ntohs(negotiatedCapabilitiesField->magic2) } != StateCookie::Magic2) + { + return false; } - auto* negotiatedCapabilitiesField = - reinterpret_cast(const_cast(buffer) + 32); + return true; + } + + StateCookie* StateCookie::Parse(const uint8_t* buffer, size_t bufferLength) + { + MS_TRACE(); - if (ntohs(negotiatedCapabilitiesField->magicValue2) != StateCookie::MagicValue2) + if (!StateCookie::IsMediasoupStateCookie(buffer, bufferLength)) { - MS_WARN_TAG(sctp, "incorrect Magic Value 2"); + MS_WARN_TAG(sctp, "not a StateCookie generated by mediasoup"); return nullptr; } @@ -47,11 +55,39 @@ namespace RTC StateCookie* StateCookie::Factory( uint8_t* buffer, size_t bufferLength, - uint32_t myVerificationTag, - uint32_t peerVerificationTag, - uint32_t myInitialTsn, - uint32_t peerInitialTsn, - uint32_t myAdvertisedReceiverWindowCredit, + uint32_t localVerificationTag, + uint32_t remoteVerificationTag, + uint32_t localInitialTsn, + uint32_t remoteInitialTsn, + uint32_t remoteAdvertisedReceiverWindowCredit, + uint64_t tieTag, + const NegotiatedCapabilities& negotiatedCapabilities) + { + MS_TRACE(); + + // This may throw. + StateCookie::Write( + buffer, + bufferLength, + localVerificationTag, + remoteVerificationTag, + localInitialTsn, + remoteInitialTsn, + remoteAdvertisedReceiverWindowCredit, + tieTag, + negotiatedCapabilities); + + return new StateCookie(buffer, StateCookie::StateCookieLength); + } + + void StateCookie::Write( + uint8_t* buffer, + size_t bufferLength, + uint32_t localVerificationTag, + uint32_t remoteVerificationTag, + uint32_t localInitialTsn, + uint32_t remoteInitialTsn, + uint32_t remoteAdvertisedReceiverWindowCredit, uint64_t tieTag, const NegotiatedCapabilities& negotiatedCapabilities) { @@ -62,30 +98,75 @@ namespace RTC MS_THROW_TYPE_ERROR("buffer too small"); } - Utils::Byte::Set4Bytes(buffer, 0, StateCookie::MagicValue1); - Utils::Byte::Set4Bytes(buffer, 4, myVerificationTag); - Utils::Byte::Set4Bytes(buffer, 8, peerVerificationTag); - Utils::Byte::Set4Bytes(buffer, 12, myInitialTsn); - Utils::Byte::Set4Bytes(buffer, 16, peerInitialTsn); - Utils::Byte::Set4Bytes(buffer, 20, myAdvertisedReceiverWindowCredit); - Utils::Byte::Set8Bytes(buffer, 24, tieTag); + Utils::Byte::Set8Bytes(buffer, 0, StateCookie::Magic1); + Utils::Byte::Set4Bytes(buffer, 8, localVerificationTag); + Utils::Byte::Set4Bytes(buffer, 12, remoteVerificationTag); + Utils::Byte::Set4Bytes(buffer, 16, localInitialTsn); + Utils::Byte::Set4Bytes(buffer, 20, remoteInitialTsn); + Utils::Byte::Set4Bytes(buffer, 24, remoteAdvertisedReceiverWindowCredit); + Utils::Byte::Set8Bytes(buffer, 28, tieTag); + + auto* negotiatedCapabilitiesField = reinterpret_cast( + buffer + StateCookie::NegotiatedCapabilitiesOffset); + + negotiatedCapabilitiesField->reserved = 0; + negotiatedCapabilitiesField->bitA = negotiatedCapabilities.partialReliability; + negotiatedCapabilitiesField->bitB = negotiatedCapabilities.messageInterleaving; + negotiatedCapabilitiesField->bitC = negotiatedCapabilities.reconfig; + negotiatedCapabilitiesField->bitD = negotiatedCapabilities.zeroChecksum; + negotiatedCapabilitiesField->magic2 = uint16_t{ htons(StateCookie::Magic2) }; + negotiatedCapabilitiesField->maxOutboundStreams = + uint16_t{ htons(negotiatedCapabilities.maxOutboundStreams) }; + negotiatedCapabilitiesField->maxInboundStreams = + uint16_t{ htons(negotiatedCapabilities.maxInboundStreams) }; + } + + StateCookie::SctpImplementation StateCookie::DetermineSctpImplementation( + const uint8_t* buffer, size_t bufferLength) + { + MS_TRACE(); - auto* stateCookie = new StateCookie(buffer, StateCookie::StateCookieLength); + if (bufferLength < StateCookie::Magic1Length) + { + return StateCookie::SctpImplementation::UNKNOWN; + } - auto* negotiatedCapabilitiesField = stateCookie->GetNegotiatedCapabilitiesField(); + std::string_view magic1(reinterpret_cast(buffer), StateCookie::Magic1Length); - negotiatedCapabilitiesField->reserved = 0; - negotiatedCapabilitiesField->bitA = negotiatedCapabilities.partialReliability; - negotiatedCapabilitiesField->bitB = negotiatedCapabilities.messageInterleaving; - negotiatedCapabilitiesField->bitC = negotiatedCapabilities.reconfig; - negotiatedCapabilitiesField->bitD = negotiatedCapabilities.zeroChecksum; - negotiatedCapabilitiesField->magicValue2 = htons(StateCookie::MagicValue2); - negotiatedCapabilitiesField->maxOutboundStreams = - htons(negotiatedCapabilities.maxOutboundStreams); - negotiatedCapabilitiesField->maxInboundStreams = - htons(negotiatedCapabilities.maxInboundStreams); + if (magic1 == "msworker") + { + return StateCookie::SctpImplementation::MEDIASOUP; + } + else if (magic1 == "dcSCTP00") + { + return StateCookie::SctpImplementation::DCSCTP; + } + else if (magic1 == "KAME-BSD") + { + return StateCookie::SctpImplementation::USRSCTP; + } + else + { + return StateCookie::SctpImplementation::UNKNOWN; + } + } - return stateCookie; + const std::string_view StateCookie::SctpImplementation2String( + StateCookie::SctpImplementation sctpImplementation) + { + MS_TRACE(); + + switch (sctpImplementation) + { + case StateCookie::SctpImplementation::UNKNOWN: + return "unknown"; + case StateCookie::SctpImplementation::MEDIASOUP: + return "mediasoup"; + case StateCookie::SctpImplementation::DCSCTP: + return "dcsctp"; + case StateCookie::SctpImplementation::USRSCTP: + return "usrsctp"; + } } /* Instance methods. */ @@ -111,14 +192,14 @@ namespace RTC MS_DUMP_CLEAN(indentation, ""); MS_DUMP_CLEAN(indentation, " length: %zu (buffer length: %zu)", GetLength(), GetBufferLength()); - MS_DUMP_CLEAN(indentation, " my verification tag: %" PRIu32, GetMyVerificationTag()); - MS_DUMP_CLEAN(indentation, " peer verification tag: %" PRIu32, GetPeerVerificationTag()); - MS_DUMP_CLEAN(indentation, " my initial tsn: %" PRIu32, GetMyInitialTsn()); - MS_DUMP_CLEAN(indentation, " peer initial tsn: %" PRIu32, GetPeerInitialTsn()); + MS_DUMP_CLEAN(indentation, " local verification tag: %" PRIu32, GetLocalVerificationTag()); + MS_DUMP_CLEAN(indentation, " remote verification tag: %" PRIu32, GetRemoteVerificationTag()); + MS_DUMP_CLEAN(indentation, " local initial tsn: %" PRIu32, GetLocalInitialTsn()); + MS_DUMP_CLEAN(indentation, " remote initial tsn: %" PRIu32, GetRemoteInitialTsn()); MS_DUMP_CLEAN( indentation, - " my advertised receiver window credit: %" PRIu32, - GetMyAdvertisedReceiverWindowCredit()); + " remote advertised receiver window credit: %" PRIu32, + GetRemoteAdvertisedReceiverWindowCredit()); MS_DUMP_CLEAN(indentation, " tie-tag: %" PRIu64, GetTieTag()); negotiatedCapabilities.Dump(indentation + 1); MS_DUMP_CLEAN(indentation, ""); @@ -144,9 +225,9 @@ namespace RTC NegotiatedCapabilities negotiatedCapabilities; negotiatedCapabilities.maxOutboundStreams = - ntohs(negotiatedCapabilitiesField->maxOutboundStreams); + uint16_t{ ntohs(negotiatedCapabilitiesField->maxOutboundStreams) }; negotiatedCapabilities.maxInboundStreams = - ntohs(negotiatedCapabilitiesField->maxInboundStreams); + uint16_t{ ntohs(negotiatedCapabilitiesField->maxInboundStreams) }; negotiatedCapabilities.partialReliability = negotiatedCapabilitiesField->bitA; negotiatedCapabilities.messageInterleaving = negotiatedCapabilitiesField->bitB; negotiatedCapabilities.reconfig = negotiatedCapabilitiesField->bitC; diff --git a/worker/src/RTC/SCTP/association/TransmissionControlBlock.cpp b/worker/src/RTC/SCTP/association/TransmissionControlBlock.cpp index 2292a3b09a..f6a29dcf21 100644 --- a/worker/src/RTC/SCTP/association/TransmissionControlBlock.cpp +++ b/worker/src/RTC/SCTP/association/TransmissionControlBlock.cpp @@ -11,16 +11,16 @@ namespace RTC /* Instance methods. */ TransmissionControlBlock::TransmissionControlBlock( - uint32_t myVerificationTag, - uint32_t peerVerificationTag, - uint32_t myInitialTsn, - uint32_t peerInitialTsn, - uint32_t myAdvertisedReceiverWindowCredit, + uint32_t localVerificationTag, + uint32_t remoteVerificationTag, + uint32_t localInitialTsn, + uint32_t remoteInitialTsn, + uint32_t remoteAdvertisedReceiverWindowCredit, uint64_t tieTag, const NegotiatedCapabilities& negotiatedCapabilities) - : myVerificationTag(myVerificationTag), peerVerificationTag(peerVerificationTag), - myInitialTsn(myInitialTsn), peerInitialTsn(peerInitialTsn), - myAdvertisedReceiverWindowCredit(myAdvertisedReceiverWindowCredit), tieTag(tieTag), + : localVerificationTag(localVerificationTag), remoteVerificationTag(remoteVerificationTag), + localInitialTsn(localInitialTsn), remoteInitialTsn(remoteInitialTsn), + remoteAdvertisedReceiverWindowCredit(remoteAdvertisedReceiverWindowCredit), tieTag(tieTag), negotiatedCapabilities(negotiatedCapabilities) { MS_TRACE(); @@ -36,14 +36,14 @@ namespace RTC MS_TRACE(); MS_DUMP_CLEAN(indentation, ""); - MS_DUMP_CLEAN(indentation, " my verification tag: %" PRIu32, this->myVerificationTag); - MS_DUMP_CLEAN(indentation, " peer verification tag: %" PRIu32, this->peerVerificationTag); - MS_DUMP_CLEAN(indentation, " my initial tsn: %" PRIu32, this->myInitialTsn); - MS_DUMP_CLEAN(indentation, " peer initial tsn: %" PRIu32, this->peerInitialTsn); + MS_DUMP_CLEAN(indentation, " local verification tag: %" PRIu32, this->localVerificationTag); + MS_DUMP_CLEAN(indentation, " remote verification tag: %" PRIu32, this->remoteVerificationTag); + MS_DUMP_CLEAN(indentation, " local initial tsn: %" PRIu32, this->localInitialTsn); + MS_DUMP_CLEAN(indentation, " remote initial tsn: %" PRIu32, this->remoteInitialTsn); MS_DUMP_CLEAN( indentation, - " my advertised receiver window credit: %" PRIu32, - this->myAdvertisedReceiverWindowCredit); + " remote advertised receiver window credit: %" PRIu32, + this->remoteAdvertisedReceiverWindowCredit); MS_DUMP_CLEAN(indentation, " tie-tag: %" PRIu64, this->tieTag); this->negotiatedCapabilities.Dump(indentation + 1); MS_DUMP_CLEAN(indentation, ""); diff --git a/worker/src/RTC/SCTP/packet/Packet.cpp b/worker/src/RTC/SCTP/packet/Packet.cpp index dacc4652b0..bd8500a887 100644 --- a/worker/src/RTC/SCTP/packet/Packet.cpp +++ b/worker/src/RTC/SCTP/packet/Packet.cpp @@ -392,7 +392,7 @@ namespace RTC this->chunks.push_back(clonedChunk); } - void Packet::SetCRC32cChecksum() + void Packet::WriteCRC32cChecksum() { MS_TRACE(); diff --git a/worker/src/RTC/SCTP/packet/TLV.cpp b/worker/src/RTC/SCTP/packet/TLV.cpp index d17ef6e63a..7e23840020 100644 --- a/worker/src/RTC/SCTP/packet/TLV.cpp +++ b/worker/src/RTC/SCTP/packet/TLV.cpp @@ -103,10 +103,11 @@ namespace RTC { MS_TRACE(); + MS_ASSERT(value != nullptr || valueLength == 0, "value cannot be nullptr if valueLength is > 0"); + // NOTE: This can throw. SetVariableLengthValueLength(valueLength); - // Copy the given value into the buffer. if (value) { std::memmove(GetVariableLengthValuePointer(), value, valueLength); diff --git a/worker/src/RTC/SCTP/packet/errorCauses/InvalidStreamIdentifierErrorCause.cpp b/worker/src/RTC/SCTP/packet/errorCauses/InvalidStreamIdentifierErrorCause.cpp index 9eeadaa2d3..d03407249c 100644 --- a/worker/src/RTC/SCTP/packet/errorCauses/InvalidStreamIdentifierErrorCause.cpp +++ b/worker/src/RTC/SCTP/packet/errorCauses/InvalidStreamIdentifierErrorCause.cpp @@ -140,6 +140,13 @@ namespace RTC return softClonedErrorCause; } + const std::string InvalidStreamIdentifierErrorCause::ContentToString() const + { + MS_TRACE(); + + return "stream:" + std::to_string(GetStreamIdentifier()); + } + void InvalidStreamIdentifierErrorCause::SetReserved() { MS_TRACE(); diff --git a/worker/src/RTC/SCTP/packet/errorCauses/MissingMandatoryParameterErrorCause.cpp b/worker/src/RTC/SCTP/packet/errorCauses/MissingMandatoryParameterErrorCause.cpp index ad1c978b40..d9f060aea5 100644 --- a/worker/src/RTC/SCTP/packet/errorCauses/MissingMandatoryParameterErrorCause.cpp +++ b/worker/src/RTC/SCTP/packet/errorCauses/MissingMandatoryParameterErrorCause.cpp @@ -4,6 +4,7 @@ #include "RTC/SCTP/packet/errorCauses/MissingMandatoryParameterErrorCause.hpp" #include "Logger.hpp" #include "MediaSoupErrors.hpp" +#include // std::ostringstream namespace RTC { @@ -123,11 +124,14 @@ namespace RTC indentation, " number of missing parameters: %" PRIu32, GetNumberOfMissingParameters()); for (uint32_t idx{ 0 }; idx < GetNumberOfMissingParameters(); ++idx) { + const auto parameterType = GetMissingParameterTypeAt(idx); + MS_DUMP_CLEAN( indentation, - " - idx: %" PRIu32 ", parameter type: %" PRIu16, + " - idx: %" PRIu32 ", parameter type: %s (%" PRIu16 ")", idx, - static_cast(GetMissingParameterTypeAt(idx))); + Parameter::ParameterTypeToString(parameterType).c_str(), + static_cast(parameterType)); } MS_DUMP_CLEAN(indentation, ""); } @@ -175,6 +179,28 @@ namespace RTC return softClonedErrorCause; } + const std::string MissingMandatoryParameterErrorCause::ContentToString() const + { + MS_TRACE(); + + std::ostringstream missingParameterTypesOss; + bool firstParameterType = true; + + for (uint32_t idx{ 0 }; idx < GetNumberOfMissingParameters(); ++idx) + { + const auto parameterType = GetMissingParameterTypeAt(idx); + + missingParameterTypesOss << (firstParameterType ? "" : ", ") + << RTC::SCTP::Parameter::ParameterTypeToString(parameterType).c_str() + << " (" << std::to_string(static_cast(parameterType)) + << ")"; + + firstParameterType = false; + } + + return "types:[" + missingParameterTypesOss.str() + "]"; + } + void MissingMandatoryParameterErrorCause::SetNumberOfMissingParameters(uint32_t value) { MS_TRACE(); diff --git a/worker/src/RTC/SCTP/packet/errorCauses/NoUserDataErrorCause.cpp b/worker/src/RTC/SCTP/packet/errorCauses/NoUserDataErrorCause.cpp index 61053d2e8e..3cb1127c38 100644 --- a/worker/src/RTC/SCTP/packet/errorCauses/NoUserDataErrorCause.cpp +++ b/worker/src/RTC/SCTP/packet/errorCauses/NoUserDataErrorCause.cpp @@ -131,5 +131,12 @@ namespace RTC return softClonedErrorCause; } + + const std::string NoUserDataErrorCause::ContentToString() const + { + MS_TRACE(); + + return "tsn:[" + std::to_string(GetTsn()) + "]"; + } } // namespace SCTP } // namespace RTC diff --git a/worker/src/RTC/SCTP/packet/errorCauses/ProtocolViolationErrorCause.cpp b/worker/src/RTC/SCTP/packet/errorCauses/ProtocolViolationErrorCause.cpp index 3e4957b753..8a3c4924c2 100644 --- a/worker/src/RTC/SCTP/packet/errorCauses/ProtocolViolationErrorCause.cpp +++ b/worker/src/RTC/SCTP/packet/errorCauses/ProtocolViolationErrorCause.cpp @@ -116,6 +116,13 @@ namespace RTC SetVariableLengthValue(info, infoLength); } + void ProtocolViolationErrorCause::SetAdditionalInformation(const std::string& info) + { + MS_TRACE(); + + SetVariableLengthValue(reinterpret_cast(info.c_str()), info.size()); + } + ProtocolViolationErrorCause* ProtocolViolationErrorCause::SoftClone(const uint8_t* buffer) const { MS_TRACE(); @@ -127,5 +134,23 @@ namespace RTC return softClonedErrorCause; } + + const std::string ProtocolViolationErrorCause::ContentToString() const + { + MS_TRACE(); + + if (HasAdditionalInformation()) + { + return "info:[" + + std::string( + reinterpret_cast(GetAdditionalInformation()), + GetAdditionalInformationLength()) + + "]"; + } + else + { + return ""; + } + } } // namespace SCTP } // namespace RTC diff --git a/worker/src/RTC/SCTP/packet/errorCauses/StaleCookieErrorCause.cpp b/worker/src/RTC/SCTP/packet/errorCauses/StaleCookieErrorCause.cpp index 8e2371199c..0563e5bcd1 100644 --- a/worker/src/RTC/SCTP/packet/errorCauses/StaleCookieErrorCause.cpp +++ b/worker/src/RTC/SCTP/packet/errorCauses/StaleCookieErrorCause.cpp @@ -131,5 +131,12 @@ namespace RTC return softClonedErrorCause; } + + const std::string StaleCookieErrorCause::ContentToString() const + { + MS_TRACE(); + + return "staleness:" + std::to_string(GetMeasureOfStaleness()); + } } // namespace SCTP } // namespace RTC diff --git a/worker/src/RTC/SCTP/packet/errorCauses/UserInitiatedAbortErrorCause.cpp b/worker/src/RTC/SCTP/packet/errorCauses/UserInitiatedAbortErrorCause.cpp index b19bbd3ccd..f385c9efa3 100644 --- a/worker/src/RTC/SCTP/packet/errorCauses/UserInitiatedAbortErrorCause.cpp +++ b/worker/src/RTC/SCTP/packet/errorCauses/UserInitiatedAbortErrorCause.cpp @@ -129,5 +129,23 @@ namespace RTC return softClonedErrorCause; } + + const std::string UserInitiatedAbortErrorCause::ContentToString() const + { + MS_TRACE(); + + if (HasUpperLayerAbortReason()) + { + return "reason:[" + + std::string( + reinterpret_cast(GetUpperLayerAbortReason()), + GetUpperLayerAbortReasonLength()) + + "]"; + } + else + { + return ""; + } + } } // namespace SCTP } // namespace RTC diff --git a/worker/src/RTC/SCTP/packet/parameters/StateCookieParameter.cpp b/worker/src/RTC/SCTP/packet/parameters/StateCookieParameter.cpp index d63589ea30..9e3d00132f 100644 --- a/worker/src/RTC/SCTP/packet/parameters/StateCookieParameter.cpp +++ b/worker/src/RTC/SCTP/packet/parameters/StateCookieParameter.cpp @@ -4,6 +4,7 @@ #include "RTC/SCTP/packet/parameters/StateCookieParameter.hpp" #include "Logger.hpp" #include "MediaSoupErrors.hpp" +#include "RTC/SCTP/association/StateCookie.hpp" namespace RTC { @@ -114,6 +115,40 @@ namespace RTC SetVariableLengthValue(cookie, cookieLength); } + void StateCookieParameter::WriteStateCookieInPlace( + uint32_t localVerificationTag, + uint32_t remoteVerificationTag, + uint32_t localInitialTsn, + uint32_t remoteInitialTsn, + uint32_t remoteAdvertisedReceiverWindowCredit, + uint64_t tieTag, + const NegotiatedCapabilities& negotiatedCapabilities) + { + MS_TRACE(); + + // The buffer in which the StateCookie will be written starts at the + // position of the Cookie field in the StateCookieParameter. + auto* buffer = GetVariableLengthValuePointer(); + // The available buffer length is the total buffer length of the + // StateCookieParameter minus its fixed header length (no matter there + // was a Cookie already in the Parameter since we are overriding it + // anyway). + size_t bufferLength = GetBufferLength() - Parameter::ParameterHeaderLength; + + StateCookie::Write( + buffer, + bufferLength, + localVerificationTag, + remoteVerificationTag, + localInitialTsn, + remoteInitialTsn, + remoteAdvertisedReceiverWindowCredit, + tieTag, + negotiatedCapabilities); + + SetVariableLengthValueLength(StateCookie::StateCookieLength); + } + StateCookieParameter* StateCookieParameter::SoftClone(const uint8_t* buffer) const { MS_TRACE(); diff --git a/worker/src/RTC/WebRtcTransport.cpp b/worker/src/RTC/WebRtcTransport.cpp index ddf322b489..7914856089 100644 --- a/worker/src/RTC/WebRtcTransport.cpp +++ b/worker/src/RTC/WebRtcTransport.cpp @@ -881,7 +881,7 @@ namespace RTC } else { - MS_WARN_TAG(sctp, "data to be sent is not a valid SCTP packet"); + MS_ABORT("RTC::SCTP::Packet::Parse() failed to parse sent SCTP data"); } #endif @@ -1458,7 +1458,7 @@ namespace RTC const auto* packet = RTC::SCTP::Packet::Parse(data, len); - if (!packet) + if (packet) { packet->Dump(); @@ -1466,7 +1466,7 @@ namespace RTC } else { - MS_WARN_TAG(sctp, "received data is not a valid SCTP packet"); + MS_ABORT("RTC::SCTP::Packet::Parse() failed to parse received SCTP data"); } #endif diff --git a/worker/src/handles/BackoffTimerHandle.cpp b/worker/src/handles/BackoffTimerHandle.cpp new file mode 100644 index 0000000000..fbeacb0af8 --- /dev/null +++ b/worker/src/handles/BackoffTimerHandle.cpp @@ -0,0 +1,150 @@ +#define MS_CLASS "BackoffTimerHandle" +// #define MS_LOG_DEV_LEVEL 3 + +#include "handles/BackoffTimerHandle.hpp" +#include "Logger.hpp" +#include "MediaSoupErrors.hpp" +#include // std::min() + +/* Instance methods. */ + +BackoffTimerHandle::BackoffTimerHandle( + Listener* listener, + uint64_t baseTimeout, + BackoffAlgorithm backoffAlgorithm, + std::optional maxBackoffTimeout, + std::optional maxRestarts) + : listener(listener), baseTimeout(baseTimeout), backoffAlgorithm(backoffAlgorithm), + maxBackoffTimeout(maxBackoffTimeout), maxRestarts(maxRestarts) +{ + MS_TRACE(); + + // NOTE: This may throw. + SetBaseTimeout(baseTimeout); + + this->timer = new TimerHandle(this); +} + +BackoffTimerHandle::~BackoffTimerHandle() +{ + MS_TRACE(); + + delete this->timer; + this->timer = nullptr; +} + +void BackoffTimerHandle::Start() +{ + MS_TRACE(); + + this->timer->Start(this->baseTimeout); + + this->active = true; + this->timeoutCount = 0; +} + +void BackoffTimerHandle::Stop() +{ + MS_TRACE(); + + this->timer->Stop(); + + this->active = false; + this->timeoutCount = 0; +} + +void BackoffTimerHandle::Restart() +{ + MS_TRACE(); + + this->timer->Restart(); + + this->active = true; + this->timeoutCount = 0; +} + +void BackoffTimerHandle::SetBaseTimeout(uint64_t baseTimeout) +{ + MS_TRACE(); + + if (baseTimeout > BackoffTimerHandle::MaxTimeout) + { + MS_THROW_ERROR( + "base timeout (%" PRIu64 ") cannot be greater than %" PRIu64, + baseTimeout, + BackoffTimerHandle::MaxTimeout); + } + + this->baseTimeout = baseTimeout; +} + +uint64_t BackoffTimerHandle::ComputeNextTimeout() const +{ + MS_TRACE(); + + auto timeoutCount = this->timeoutCount; + + switch (this->backoffAlgorithm) + { + case BackoffAlgorithm::FIXED: + { + return this->baseTimeout; + } + + case BackoffAlgorithm::EXPONENTIAL: + { + auto timeout = this->baseTimeout; + + while (timeoutCount > 0 && timeout < BackoffTimerHandle::MaxTimeout) + { + timeout *= 2; + --timeoutCount; + + if (this->maxBackoffTimeout.has_value() && timeout > *this->maxBackoffTimeout) + { + return *this->maxBackoffTimeout; + } + } + + return std::min(timeout, BackoffTimerHandle::MaxTimeout); + } + } +} + +void BackoffTimerHandle::OnTimer(TimerHandle* timer) +{ + MS_TRACE(); + + this->timeoutCount++; + + // Compute whether the smart timer should still be running after this timeout + // expiration so the parent can check IsActive() within the OnTimer() + // callback. + this->active = !this->maxRestarts.has_value() || this->timeoutCount <= *this->maxRestarts; + + uint64_t baseTimeout{ this->baseTimeout }; + bool stop{ false }; + + // Call the listener by passing base timeout as reference so the parent has + // a chance to change it and affect the next timeout. + this->listener->OnTimer(this, baseTimeout, stop); + + // If the parent has set `stop` to true it means that it has deleted the + // instance, so stop here. + if (stop) + { + return; + } + + // NOTE: This may throw. + SetBaseTimeout(baseTimeout); + + // The caller may have called Stop() within the callback so we must check + // the `active` flag. + if (this->active) + { + auto nextTimeout = ComputeNextTimeout(); + + this->timer->Start(nextTimeout); + } +} diff --git a/worker/src/handles/TimerHandle.cpp b/worker/src/handles/TimerHandle.cpp index a5e4c1afd9..81ec41c421 100644 --- a/worker/src/handles/TimerHandle.cpp +++ b/worker/src/handles/TimerHandle.cpp @@ -59,12 +59,20 @@ void TimerHandle::Start(uint64_t timeout, uint64_t repeat) this->timeout = timeout; this->repeat = repeat; + int err; + if (uv_is_active(reinterpret_cast(this->uvHandle)) != 0) { - Stop(); + err = uv_timer_stop(this->uvHandle); + + if (err != 0) + { + MS_THROW_ERROR("uv_timer_stop() failed: %s", uv_strerror(err)); + } } - const int err = uv_timer_start(this->uvHandle, static_cast(onTimer), timeout, repeat); + err = + uv_timer_start(this->uvHandle, static_cast(onTimer), this->timeout, this->repeat); if (err != 0) { @@ -89,34 +97,6 @@ void TimerHandle::Stop() } } -void TimerHandle::Reset() -{ - MS_TRACE(); - - if (this->closed) - { - MS_THROW_ERROR("closed"); - } - - if (uv_is_active(reinterpret_cast(this->uvHandle)) == 0) - { - return; - } - - if (this->repeat == 0u) - { - return; - } - - const int err = - uv_timer_start(this->uvHandle, static_cast(onTimer), this->repeat, this->repeat); - - if (err != 0) - { - MS_THROW_ERROR("uv_timer_start() failed: %s", uv_strerror(err)); - } -} - void TimerHandle::Restart() { MS_TRACE(); @@ -126,12 +106,19 @@ void TimerHandle::Restart() MS_THROW_ERROR("closed"); } + int err; + if (uv_is_active(reinterpret_cast(this->uvHandle)) != 0) { - Stop(); + err = uv_timer_stop(this->uvHandle); + + if (err != 0) + { + MS_THROW_ERROR("uv_timer_stop() failed: %s", uv_strerror(err)); + } } - const int err = + err = uv_timer_start(this->uvHandle, static_cast(onTimer), this->timeout, this->repeat); if (err != 0) diff --git a/worker/test/src/RTC/SCTP/association/TestNegotiatedCapabilities.cpp b/worker/test/src/RTC/SCTP/association/TestNegotiatedCapabilities.cpp new file mode 100644 index 0000000000..9a4e7cf8b4 --- /dev/null +++ b/worker/test/src/RTC/SCTP/association/TestNegotiatedCapabilities.cpp @@ -0,0 +1,114 @@ +#include "common.hpp" +#include "RTC/SCTP/association/NegotiatedCapabilities.hpp" +#include "RTC/SCTP/association/SocketOptions.hpp" +#include "RTC/SCTP/packet/Chunk.hpp" +#include "RTC/SCTP/packet/chunks/InitChunk.hpp" +#include "RTC/SCTP/packet/parameters/ForwardTsnSupportedParameter.hpp" +#include "RTC/SCTP/packet/parameters/SupportedExtensionsParameter.hpp" +#include "RTC/SCTP/packet/parameters/ZeroChecksumAcceptableParameter.hpp" +#include "RTC/SCTP/sctpCommon.hpp" +#include + +SCENARIO("SCTP Negotiated Capabilities", "[sctp][negotiatedcapabilities]") +{ + sctpCommon::ResetBuffers(); + + SECTION("NegotiatedCapabilities::Factory() succeeds (1)") + { + RTC::SCTP::SocketOptions socketOptions{}; + + socketOptions.maxOutboundStreams = 8192; + socketOptions.maxInboundStreams = 2048; + socketOptions.partialReliability = true; + socketOptions.messageInterleaving = true; + socketOptions.zeroChecksumAlternateErrorDetectionMethod = + RTC::SCTP::ZeroChecksumAcceptableParameter::AlternateErrorDetectionMethod::SCTP_OVER_DTLS; + + auto* remoteChunk = + RTC::SCTP::InitChunk::Factory(sctpCommon::FactoryBuffer, sizeof(sctpCommon::FactoryBuffer)); + + remoteChunk->SetNumberOfOutboundStreams(4096); + remoteChunk->SetNumberOfInboundStreams(1024); + + auto* remoteSupportedExtensionsParameter = + remoteChunk->BuildParameterInPlace(); + + remoteSupportedExtensionsParameter->AddChunkType(RTC::SCTP::Chunk::ChunkType::FORWARD_TSN); + remoteSupportedExtensionsParameter->AddChunkType(RTC::SCTP::Chunk::ChunkType::RE_CONFIG); + remoteSupportedExtensionsParameter->AddChunkType(RTC::SCTP::Chunk::ChunkType::I_DATA); + remoteSupportedExtensionsParameter->AddChunkType(RTC::SCTP::Chunk::ChunkType::I_FORWARD_TSN); + remoteSupportedExtensionsParameter->Consolidate(); + + auto* remoteZeroChecksumAcceptableParameter = + remoteChunk->BuildParameterInPlace(); + + remoteZeroChecksumAcceptableParameter->SetAlternateErrorDetectionMethod( + RTC::SCTP::ZeroChecksumAcceptableParameter::AlternateErrorDetectionMethod::SCTP_OVER_DTLS); + remoteZeroChecksumAcceptableParameter->Consolidate(); + + auto negotiatedCapabilities = + RTC::SCTP::NegotiatedCapabilities::Factory(socketOptions, remoteChunk); + + delete remoteChunk; + + REQUIRE(negotiatedCapabilities.maxOutboundStreams == 1024); + REQUIRE(negotiatedCapabilities.maxInboundStreams == 2048); + REQUIRE(negotiatedCapabilities.partialReliability == true); + REQUIRE(negotiatedCapabilities.messageInterleaving == true); + REQUIRE(negotiatedCapabilities.reconfig == true); + REQUIRE(negotiatedCapabilities.zeroChecksum == true); + } + + SECTION("NegotiatedCapabilities::Factory() succeeds (2)") + { + RTC::SCTP::SocketOptions socketOptions{}; + + socketOptions.maxOutboundStreams = 1000; + socketOptions.maxInboundStreams = 2000; + socketOptions.partialReliability = true; + socketOptions.messageInterleaving = true; + socketOptions.zeroChecksumAlternateErrorDetectionMethod = + RTC::SCTP::ZeroChecksumAcceptableParameter::AlternateErrorDetectionMethod::SCTP_OVER_DTLS; + + auto* remoteChunk = + RTC::SCTP::InitChunk::Factory(sctpCommon::FactoryBuffer, sizeof(sctpCommon::FactoryBuffer)); + + remoteChunk->SetNumberOfOutboundStreams(4000); + remoteChunk->SetNumberOfInboundStreams(3000); + + auto* remoteSupportedExtensionsParameter = + remoteChunk->BuildParameterInPlace(); + + // NOTE: Missing FORWARD_TSN, but peer announced support for it via + // Forward-TSN-Supported Parameter negotiation). + // NOTE: Missing RE_CONFIG (needed for Partial Reliability Extension + // negotiation). + // NOTE: Missing I_FORWARD_TSN (needed for Message Interleaving negotiation). + remoteSupportedExtensionsParameter->AddChunkType(RTC::SCTP::Chunk::ChunkType::I_DATA); + remoteSupportedExtensionsParameter->Consolidate(); + + auto* remoteForwardTsnSupportedParameter = + remoteChunk->BuildParameterInPlace(); + + remoteForwardTsnSupportedParameter->Consolidate(); + + auto* remoteZeroChecksumAcceptableParameter = + remoteChunk->BuildParameterInPlace(); + + remoteZeroChecksumAcceptableParameter->SetAlternateErrorDetectionMethod( + static_cast(666)); + remoteZeroChecksumAcceptableParameter->Consolidate(); + + auto negotiatedCapabilities = + RTC::SCTP::NegotiatedCapabilities::Factory(socketOptions, remoteChunk); + + delete remoteChunk; + + REQUIRE(negotiatedCapabilities.maxOutboundStreams == 1000); + REQUIRE(negotiatedCapabilities.maxInboundStreams == 2000); + REQUIRE(negotiatedCapabilities.partialReliability == true); + REQUIRE(negotiatedCapabilities.messageInterleaving == false); + REQUIRE(negotiatedCapabilities.reconfig == false); + REQUIRE(negotiatedCapabilities.zeroChecksum == false); + } +} diff --git a/worker/test/src/RTC/SCTP/association/TestStateCookie.cpp b/worker/test/src/RTC/SCTP/association/TestStateCookie.cpp index 64e9c422c3..cd496e2b03 100644 --- a/worker/test/src/RTC/SCTP/association/TestStateCookie.cpp +++ b/worker/test/src/RTC/SCTP/association/TestStateCookie.cpp @@ -1,7 +1,7 @@ #include "common.hpp" #include "RTC/SCTP/association/NegotiatedCapabilities.hpp" #include "RTC/SCTP/association/StateCookie.hpp" -#include "RTC/SCTP/sctpCommon.hpp" +#include "RTC/SCTP/sctpCommon.hpp" // in worker/test/include/ #include #include // std::memset() @@ -14,17 +14,18 @@ SCENARIO("SCTP State Cookie", "[sctp][statecookie]") // clang-format off uint8_t buffer[] = { - // Magic Value 1: 0xF109ABE4 - 0xF1, 0x09, 0xAB, 0xE4, - // My Verification Tag: 11223344 + // Magic 1: 0x6D73776F726B6572 + 0x6D, 0x73, 0x77, 0x6F, + 0x72, 0x6B, 0x65, 0x72, + // Local Verification Tag: 11223344 0x00, 0xAB, 0x41, 0x30, - // Peer Verification Tag: 55667788 + // Remote Verification Tag: 55667788 0x03, 0x51, 0x6C, 0x4C, - // My Initial TSN: 12345678 + // Local Initial TSN: 12345678 0x00, 0xBC, 0x61, 0x4E, - // Peer Initial TSN: 87654321 + // Remote Initial TSN: 87654321 0x05, 0x39, 0x7F, 0xB1, - // My Advertised Receiver Window Credit (a_rwnd): 66666666 + // Remote Advertised Receiver Window Credit (a_rwnd): 66666666 0x03, 0xF9, 0x40, 0xAA, // Tie-Tag: 0xABCDEF0011223344 0xAB, 0xCD, 0xEF, 0x00, @@ -34,25 +35,37 @@ SCENARIO("SCTP State Cookie", "[sctp][statecookie]") // - messageInterleaving: 0 // - reconfig: 1 // - zeroChecksum: 1 - // Magic Value 2: 0xAD81 + // Magic 2: 0xAD81 0x00, 0b00001101, 0xAD, 0x81, // Max Outbound Streams: 15000, Max Inbound Streams: 2500 0x3A, 0x98, 0x09, 0xC4 }; // clang-format on + REQUIRE(RTC::SCTP::StateCookie::IsMediasoupStateCookie(buffer, sizeof(buffer)) == true); + REQUIRE( + RTC::SCTP::StateCookie::DetermineSctpImplementation(buffer, sizeof(buffer)) == + RTC::SCTP::StateCookie::SctpImplementation::MEDIASOUP); + auto* stateCookie = RTC::SCTP::StateCookie::Parse(buffer, sizeof(buffer)); REQUIRE(stateCookie); REQUIRE(stateCookie->GetBuffer() == buffer); REQUIRE(stateCookie->GetLength() == RTC::SCTP::StateCookie::StateCookieLength); REQUIRE(stateCookie->GetBufferLength() == RTC::SCTP::StateCookie::StateCookieLength); - REQUIRE(stateCookie->GetMyVerificationTag() == 11223344); - REQUIRE(stateCookie->GetPeerVerificationTag() == 55667788); - REQUIRE(stateCookie->GetMyInitialTsn() == 12345678); - REQUIRE(stateCookie->GetPeerInitialTsn() == 87654321); - REQUIRE(stateCookie->GetMyAdvertisedReceiverWindowCredit() == 66666666); + REQUIRE(stateCookie->GetLocalVerificationTag() == 11223344); + REQUIRE(stateCookie->GetRemoteVerificationTag() == 55667788); + REQUIRE(stateCookie->GetLocalInitialTsn() == 12345678); + REQUIRE(stateCookie->GetRemoteInitialTsn() == 87654321); + REQUIRE(stateCookie->GetRemoteAdvertisedReceiverWindowCredit() == 66666666); REQUIRE(stateCookie->GetTieTag() == 0xABCDEF0011223344); + REQUIRE( + RTC::SCTP::StateCookie::IsMediasoupStateCookie( + stateCookie->GetBuffer(), stateCookie->GetLength()) == true); + REQUIRE( + RTC::SCTP::StateCookie::DetermineSctpImplementation( + stateCookie->GetBuffer(), stateCookie->GetLength()) == + RTC::SCTP::StateCookie::SctpImplementation::MEDIASOUP); auto negotiatedCapabilities = stateCookie->GetNegotiatedCapabilities(); @@ -73,12 +86,19 @@ SCENARIO("SCTP State Cookie", "[sctp][statecookie]") REQUIRE(stateCookie->GetBuffer() == sctpCommon::SerializeBuffer); REQUIRE(stateCookie->GetLength() == RTC::SCTP::StateCookie::StateCookieLength); REQUIRE(stateCookie->GetBufferLength() == sizeof(sctpCommon::SerializeBuffer)); - REQUIRE(stateCookie->GetMyVerificationTag() == 11223344); - REQUIRE(stateCookie->GetPeerVerificationTag() == 55667788); - REQUIRE(stateCookie->GetMyInitialTsn() == 12345678); - REQUIRE(stateCookie->GetPeerInitialTsn() == 87654321); - REQUIRE(stateCookie->GetMyAdvertisedReceiverWindowCredit() == 66666666); + REQUIRE(stateCookie->GetLocalVerificationTag() == 11223344); + REQUIRE(stateCookie->GetRemoteVerificationTag() == 55667788); + REQUIRE(stateCookie->GetLocalInitialTsn() == 12345678); + REQUIRE(stateCookie->GetRemoteInitialTsn() == 87654321); + REQUIRE(stateCookie->GetRemoteAdvertisedReceiverWindowCredit() == 66666666); REQUIRE(stateCookie->GetTieTag() == 0xABCDEF0011223344); + REQUIRE( + RTC::SCTP::StateCookie::IsMediasoupStateCookie( + stateCookie->GetBuffer(), stateCookie->GetLength()) == true); + REQUIRE( + RTC::SCTP::StateCookie::DetermineSctpImplementation( + stateCookie->GetBuffer(), stateCookie->GetLength()) == + RTC::SCTP::StateCookie::SctpImplementation::MEDIASOUP); negotiatedCapabilities = stateCookie->GetNegotiatedCapabilities(); @@ -102,12 +122,19 @@ SCENARIO("SCTP State Cookie", "[sctp][statecookie]") REQUIRE(clonedStateCookie->GetBuffer() == sctpCommon::CloneBuffer); REQUIRE(clonedStateCookie->GetLength() == RTC::SCTP::StateCookie::StateCookieLength); REQUIRE(clonedStateCookie->GetBufferLength() == sizeof(sctpCommon::CloneBuffer)); - REQUIRE(clonedStateCookie->GetMyVerificationTag() == 11223344); - REQUIRE(clonedStateCookie->GetPeerVerificationTag() == 55667788); - REQUIRE(clonedStateCookie->GetMyInitialTsn() == 12345678); - REQUIRE(clonedStateCookie->GetPeerInitialTsn() == 87654321); - REQUIRE(clonedStateCookie->GetMyAdvertisedReceiverWindowCredit() == 66666666); + REQUIRE(clonedStateCookie->GetLocalVerificationTag() == 11223344); + REQUIRE(clonedStateCookie->GetRemoteVerificationTag() == 55667788); + REQUIRE(clonedStateCookie->GetLocalInitialTsn() == 12345678); + REQUIRE(clonedStateCookie->GetRemoteInitialTsn() == 87654321); + REQUIRE(clonedStateCookie->GetRemoteAdvertisedReceiverWindowCredit() == 66666666); REQUIRE(clonedStateCookie->GetTieTag() == 0xABCDEF0011223344); + REQUIRE( + RTC::SCTP::StateCookie::IsMediasoupStateCookie( + clonedStateCookie->GetBuffer(), clonedStateCookie->GetLength()) == true); + REQUIRE( + RTC::SCTP::StateCookie::DetermineSctpImplementation( + clonedStateCookie->GetBuffer(), clonedStateCookie->GetLength()) == + RTC::SCTP::StateCookie::SctpImplementation::MEDIASOUP); negotiatedCapabilities = clonedStateCookie->GetNegotiatedCapabilities(); @@ -123,21 +150,22 @@ SCENARIO("SCTP State Cookie", "[sctp][statecookie]") SECTION("StateCookie::Parse() fails") { - // Wrong Magic Value 1. + // Wrong Magic 1. // clang-format off uint8_t buffer1[] = { - // Magic Value 1: 0xF109ABE5 (instead of 0xF109ABE4) - 0xF1, 0x09, 0xAB, 0xE5, - // My Verification Tag: 11223344 + // Magic 1: 0x6D73776F726B6573 (wrong) + 0x6D, 0x73, 0x77, 0x6F, + 0x72, 0x6B, 0x65, 0x73, + // Local Verification Tag: 11223344 0x00, 0xAB, 0x41, 0x30, - // Peer Verification Tag: 55667788 + // Remote Verification Tag: 55667788 0x03, 0x51, 0x6C, 0x4C, - // My Initial TSN: 12345678 + // Local Initial TSN: 12345678 0x00, 0xBC, 0x61, 0x4E, - // Peer Initial TSN: 87654321 + // Remote Initial TSN: 87654321 0x05, 0x39, 0x7F, 0xB1, - // My Advertised Receiver Window Credit (a_rwnd): 66666666 + // Remote Advertised Receiver Window Credit (a_rwnd): 66666666 0x03, 0xF9, 0x40, 0xAA, // Tie-Tag: 0xABCDEF0011223344 0xAB, 0xCD, 0xEF, 0x00, @@ -147,30 +175,35 @@ SCENARIO("SCTP State Cookie", "[sctp][statecookie]") // - messageInterleaving: 0 // - reconfig: 1 // - zeroChecksum: 1 - // Magic Value 2: 0xAD81 + // Magic 2: 0xAD81 0x00, 0b00001101, 0xAD, 0x81, // Max Outbound Streams: 15000, Max Inbound Streams: 2500 0x3A, 0x98, 0x09, 0xC4 }; // clang-format on + REQUIRE(RTC::SCTP::StateCookie::IsMediasoupStateCookie(buffer1, sizeof(buffer1)) == false); + REQUIRE( + RTC::SCTP::StateCookie::DetermineSctpImplementation(buffer1, sizeof(buffer1)) == + RTC::SCTP::StateCookie::SctpImplementation::UNKNOWN); REQUIRE(!RTC::SCTP::StateCookie::Parse(buffer1, sizeof(buffer1))); - // Wrong Magic Value 2. + // Wrong Magic 2. // clang-format off uint8_t buffer2[] = { - // Magic Value 1: 0xF109ABE4 - 0xF1, 0x09, 0xAB, 0xE4, - // My Verification Tag: 11223344 + // Magic 1: 0x6D73776F726B6572 + 0x6D, 0x73, 0x77, 0x6F, + 0x72, 0x6B, 0x65, 0x72, + // Local Verification Tag: 11223344 0x00, 0xAB, 0x41, 0x30, - // Peer Verification Tag: 55667788 + // Remote Verification Tag: 55667788 0x03, 0x51, 0x6C, 0x4C, - // My Initial TSN: 12345678 + // Local Initial TSN: 12345678 0x00, 0xBC, 0x61, 0x4E, - // Peer Initial TSN: 87654321 + // Remote Initial TSN: 87654321 0x05, 0x39, 0x7F, 0xB1, - // My Advertised Receiver Window Credit (a_rwnd): 66666666 + // Remote Advertised Receiver Window Credit (a_rwnd): 66666666 0x03, 0xF9, 0x40, 0xAA, // Tie-Tag: 0xABCDEF0011223344 0xAB, 0xCD, 0xEF, 0x00, @@ -180,30 +213,35 @@ SCENARIO("SCTP State Cookie", "[sctp][statecookie]") // - messageInterleaving: 0 // - reconfig: 1 // - zeroChecksum: 1 - // Magic Value 2: 0xAD82 (instead of 0xAD81) + // Magic 2: 0xAD82 (instead of 0xAD81) 0x00, 0b00001101, 0xAD, 0x82, // Max Outbound Streams: 15000, Max Inbound Streams: 2500 0x3A, 0x98, 0x09, 0xC4 }; // clang-format on + REQUIRE(RTC::SCTP::StateCookie::IsMediasoupStateCookie(buffer2, sizeof(buffer2)) == false); + REQUIRE( + RTC::SCTP::StateCookie::DetermineSctpImplementation(buffer2, sizeof(buffer2)) == + RTC::SCTP::StateCookie::SctpImplementation::MEDIASOUP); REQUIRE(!RTC::SCTP::StateCookie::Parse(buffer2, sizeof(buffer2))); // Buffer too big. // clang-format off uint8_t buffer3[] = { - // Magic Value 1: 0xF109ABE4 - 0xF1, 0x09, 0xAB, 0xE4, - // My Verification Tag: 11223344 + // Magic 1: 0x6D73776F726B6572 + 0x6D, 0x73, 0x77, 0x6F, + 0x72, 0x6B, 0x65, 0x72, + // Local Verification Tag: 11223344 0x00, 0xAB, 0x41, 0x30, - // Peer Verification Tag: 55667788 + // Remote Verification Tag: 55667788 0x03, 0x51, 0x6C, 0x4C, - // My Initial TSN: 12345678 + // Local Initial TSN: 12345678 0x00, 0xBC, 0x61, 0x4E, - // Peer Initial TSN: 87654321 + // Remote Initial TSN: 87654321 0x05, 0x39, 0x7F, 0xB1, - // My Advertised Receiver Window Credit (a_rwnd): 66666666 + // Remote Advertised Receiver Window Credit (a_rwnd): 66666666 0x03, 0xF9, 0x40, 0xAA, // Tie-Tag: 0xABCDEF0011223344 0xAB, 0xCD, 0xEF, 0x00, @@ -213,7 +251,7 @@ SCENARIO("SCTP State Cookie", "[sctp][statecookie]") // - messageInterleaving: 0 // - reconfig: 1 // - zeroChecksum: 1 - // Magic Value 2: 0xAD81 + // Magic 2: 0xAD81 0x00, 0b00001101, 0xAD, 0x81, // Max Outbound Streams: 15000, Max Inbound Streams: 2500 0x3A, 0x98, 0x09, 0xC4, @@ -222,6 +260,10 @@ SCENARIO("SCTP State Cookie", "[sctp][statecookie]") }; // clang-format on + REQUIRE(RTC::SCTP::StateCookie::IsMediasoupStateCookie(buffer3, sizeof(buffer3)) == false); + REQUIRE( + RTC::SCTP::StateCookie::DetermineSctpImplementation(buffer3, sizeof(buffer3)) == + RTC::SCTP::StateCookie::SctpImplementation::MEDIASOUP); REQUIRE(!RTC::SCTP::StateCookie::Parse(buffer3, sizeof(buffer3))); } @@ -237,11 +279,11 @@ SCENARIO("SCTP State Cookie", "[sctp][statecookie]") auto* stateCookie = RTC::SCTP::StateCookie::Factory( /*buffer*/ sctpCommon::FactoryBuffer, /*bufferLength*/ sizeof(sctpCommon::FactoryBuffer), - /*myVerificationTag*/ 6660666, - /*peerVerificationTag*/ 9990999, - /*myInitialTsn*/ 1110111, - /*peerInitialTsn*/ 2220222, - /*myAdvertisedReceiverWindowCredit*/ 999909999, + /*localVerificationTag*/ 6660666, + /*remoteVerificationTag*/ 9990999, + /*localInitialTsn*/ 1110111, + /*remoteInitialTsn*/ 2220222, + /*remoteAdvertisedReceiverWindowCredit*/ 999909999, /*tieTag*/ 1111222233334444, negotiatedCapabilities); @@ -254,12 +296,19 @@ SCENARIO("SCTP State Cookie", "[sctp][statecookie]") REQUIRE(stateCookie->GetBuffer() == sctpCommon::FactoryBuffer); REQUIRE(stateCookie->GetLength() == RTC::SCTP::StateCookie::StateCookieLength); REQUIRE(stateCookie->GetBufferLength() == RTC::SCTP::StateCookie::StateCookieLength); - REQUIRE(stateCookie->GetMyVerificationTag() == 6660666); - REQUIRE(stateCookie->GetPeerVerificationTag() == 9990999); - REQUIRE(stateCookie->GetMyInitialTsn() == 1110111); - REQUIRE(stateCookie->GetPeerInitialTsn() == 2220222); - REQUIRE(stateCookie->GetMyAdvertisedReceiverWindowCredit() == 999909999); + REQUIRE(stateCookie->GetLocalVerificationTag() == 6660666); + REQUIRE(stateCookie->GetRemoteVerificationTag() == 9990999); + REQUIRE(stateCookie->GetLocalInitialTsn() == 1110111); + REQUIRE(stateCookie->GetRemoteInitialTsn() == 2220222); + REQUIRE(stateCookie->GetRemoteAdvertisedReceiverWindowCredit() == 999909999); REQUIRE(stateCookie->GetTieTag() == 1111222233334444); + REQUIRE( + RTC::SCTP::StateCookie::IsMediasoupStateCookie( + stateCookie->GetBuffer(), stateCookie->GetLength()) == true); + REQUIRE( + RTC::SCTP::StateCookie::DetermineSctpImplementation( + stateCookie->GetBuffer(), stateCookie->GetLength()) == + RTC::SCTP::StateCookie::SctpImplementation::MEDIASOUP); const auto retrievedNegotiatedCapabilities = stateCookie->GetNegotiatedCapabilities(); @@ -281,12 +330,19 @@ SCENARIO("SCTP State Cookie", "[sctp][statecookie]") REQUIRE(parsedStateCookie->GetBuffer() == sctpCommon::FactoryBuffer); REQUIRE(parsedStateCookie->GetLength() == RTC::SCTP::StateCookie::StateCookieLength); REQUIRE(parsedStateCookie->GetBufferLength() == RTC::SCTP::StateCookie::StateCookieLength); - REQUIRE(parsedStateCookie->GetMyVerificationTag() == 6660666); - REQUIRE(parsedStateCookie->GetPeerVerificationTag() == 9990999); - REQUIRE(parsedStateCookie->GetMyInitialTsn() == 1110111); - REQUIRE(parsedStateCookie->GetPeerInitialTsn() == 2220222); - REQUIRE(parsedStateCookie->GetMyAdvertisedReceiverWindowCredit() == 999909999); + REQUIRE(parsedStateCookie->GetLocalVerificationTag() == 6660666); + REQUIRE(parsedStateCookie->GetRemoteVerificationTag() == 9990999); + REQUIRE(parsedStateCookie->GetLocalInitialTsn() == 1110111); + REQUIRE(parsedStateCookie->GetRemoteInitialTsn() == 2220222); + REQUIRE(parsedStateCookie->GetRemoteAdvertisedReceiverWindowCredit() == 999909999); REQUIRE(parsedStateCookie->GetTieTag() == 1111222233334444); + REQUIRE( + RTC::SCTP::StateCookie::IsMediasoupStateCookie( + parsedStateCookie->GetBuffer(), parsedStateCookie->GetLength()) == true); + REQUIRE( + RTC::SCTP::StateCookie::DetermineSctpImplementation( + parsedStateCookie->GetBuffer(), parsedStateCookie->GetLength()) == + RTC::SCTP::StateCookie::SctpImplementation::MEDIASOUP); const auto retrievedParsedNegotiatedCapabilities = parsedStateCookie->GetNegotiatedCapabilities(); @@ -299,4 +355,155 @@ SCENARIO("SCTP State Cookie", "[sctp][statecookie]") delete parsedStateCookie; } + + SECTION("StateCookie::Write() succeeds") + { + RTC::SCTP::NegotiatedCapabilities negotiatedCapabilities = { .maxOutboundStreams = 62000, + .maxInboundStreams = 55555, + .partialReliability = true, + .messageInterleaving = true, + .reconfig = true, + .zeroChecksum = false }; + + auto* buffer = sctpCommon::FactoryBuffer; + + RTC::SCTP::StateCookie::Write( + /*buffer*/ buffer, + /*bufferLength*/ RTC::SCTP::StateCookie::StateCookieLength, + /*localVerificationTag*/ 6660666, + /*remoteVerificationTag*/ 9990999, + /*localInitialTsn*/ 1110111, + /*remoteInitialTsn*/ 2220222, + /*remoteAdvertisedReceiverWindowCredit*/ 999909999, + /*tieTag*/ 1111222233334444, + negotiatedCapabilities); + + // Change values of the original NegotiatedCapabilities to assert that it + // doesn't affect the internals of StateCookie. + negotiatedCapabilities.partialReliability = false; + negotiatedCapabilities.maxOutboundStreams = 1024; + + /* Parse the buffer. */ + + auto* stateCookie = + RTC::SCTP::StateCookie::Parse(buffer, RTC::SCTP::StateCookie::StateCookieLength); + + REQUIRE(stateCookie); + REQUIRE(stateCookie->GetBuffer() == buffer); + REQUIRE(stateCookie->GetLength() == RTC::SCTP::StateCookie::StateCookieLength); + REQUIRE(stateCookie->GetBufferLength() == RTC::SCTP::StateCookie::StateCookieLength); + REQUIRE(stateCookie->GetLocalVerificationTag() == 6660666); + REQUIRE(stateCookie->GetRemoteVerificationTag() == 9990999); + REQUIRE(stateCookie->GetLocalInitialTsn() == 1110111); + REQUIRE(stateCookie->GetRemoteInitialTsn() == 2220222); + REQUIRE(stateCookie->GetRemoteAdvertisedReceiverWindowCredit() == 999909999); + REQUIRE(stateCookie->GetTieTag() == 1111222233334444); + REQUIRE( + RTC::SCTP::StateCookie::IsMediasoupStateCookie( + stateCookie->GetBuffer(), stateCookie->GetLength()) == true); + REQUIRE( + RTC::SCTP::StateCookie::DetermineSctpImplementation( + stateCookie->GetBuffer(), stateCookie->GetLength()) == + RTC::SCTP::StateCookie::SctpImplementation::MEDIASOUP); + + const auto retrievedNegotiatedCapabilities = stateCookie->GetNegotiatedCapabilities(); + + REQUIRE(retrievedNegotiatedCapabilities.maxOutboundStreams == 62000); + REQUIRE(retrievedNegotiatedCapabilities.maxInboundStreams == 55555); + REQUIRE(retrievedNegotiatedCapabilities.partialReliability == true); + REQUIRE(retrievedNegotiatedCapabilities.messageInterleaving == true); + REQUIRE(retrievedNegotiatedCapabilities.reconfig == true); + REQUIRE(retrievedNegotiatedCapabilities.zeroChecksum == false); + + delete stateCookie; + } + + SECTION("StateCookie::DetermineSctpImplementation() succeeds") + { + // usrsctp generated State Cookie. + // clang-format off + uint8_t buffer1[] = + { + // Magic 1: 0x4B414D452D425344 + 0x4B, 0x41, 0x4D, 0x45, + 0x2D, 0x42, 0x53, 0x44, + 0x11, 0x22, 0x33, 0x44, + 0x6D, 0x73, 0x77, 0x6F, + 0x72, 0x6B, 0x65, 0x72, + 0x11, 0x22, 0x33, 0x44, + 0x00, 0xAB, 0x41, 0x30, + 0x03, 0x51, 0x6C, 0x4C, + 0x00, 0xBC, 0x61, 0x4E, + 0x11, 0x22, 0x33, 0x44, + 0x05, 0x39, 0x7F, 0xB1, + 0x03, 0xF9, 0x40, 0xAA, + 0xAB, 0xCD, 0xEF, 0x00, + 0x11, 0x22, 0x33, 0x44, + // etc + }; + // clang-format on + + REQUIRE(RTC::SCTP::StateCookie::IsMediasoupStateCookie(buffer1, sizeof(buffer1)) == false); + REQUIRE( + RTC::SCTP::StateCookie::DetermineSctpImplementation(buffer1, sizeof(buffer1)) == + RTC::SCTP::StateCookie::SctpImplementation::USRSCTP); + + // dcSCTP generated State Cookie. + // clang-format off + uint8_t buffer2[] = + { + // Magic 1: 0x6463534354503030 + 0x64, 0x63, 0x53, 0x43, + 0x54, 0x50, 0x30, 0x30, + 0x5D, 0x0E, 0x21, 0xE4, + 0x0F, 0xA8, 0x44, 0x3F, + 0x11, 0x80, 0x89, 0x5D, + 0x2F, 0x4E, 0x17, 0x1F, + 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, + }; + // clang-format on + + REQUIRE(RTC::SCTP::StateCookie::IsMediasoupStateCookie(buffer2, sizeof(buffer2)) == false); + REQUIRE( + RTC::SCTP::StateCookie::DetermineSctpImplementation(buffer2, sizeof(buffer2)) == + RTC::SCTP::StateCookie::SctpImplementation::DCSCTP); + + // State Cookie generated by unknown implementation. + // clang-format off + uint8_t buffer3[] = + { + // Magic 1: 0x1122334455667788 + 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88, + 0x11, 0x80, 0x89, 0x5D, + 0x2F, 0x4E, 0x17, 0x1F, + 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, + }; + // clang-format on + + REQUIRE(RTC::SCTP::StateCookie::IsMediasoupStateCookie(buffer3, sizeof(buffer3)) == false); + REQUIRE( + RTC::SCTP::StateCookie::DetermineSctpImplementation(buffer3, sizeof(buffer3)) == + RTC::SCTP::StateCookie::SctpImplementation::UNKNOWN); + + // Too short State Cookie so we don't know. + // clang-format off + uint8_t buffer4[] = + { + // Magic 1: 0xAABBCCDD + 0xAA, 0xBB, 0xCC, 0xDD, + }; + // clang-format on + + REQUIRE(RTC::SCTP::StateCookie::IsMediasoupStateCookie(buffer4, sizeof(buffer4)) == false); + REQUIRE( + RTC::SCTP::StateCookie::DetermineSctpImplementation(buffer4, sizeof(buffer4)) == + RTC::SCTP::StateCookie::SctpImplementation::UNKNOWN); + } } diff --git a/worker/test/src/RTC/SCTP/packet/TestPacket.cpp b/worker/test/src/RTC/SCTP/packet/TestPacket.cpp index 6eccf1f532..6ab1008a57 100644 --- a/worker/test/src/RTC/SCTP/packet/TestPacket.cpp +++ b/worker/test/src/RTC/SCTP/packet/TestPacket.cpp @@ -16,7 +16,7 @@ #include #include // std::memset() -SCENARIO("SCTP Packet", "[sctp][serializable]") +SCENARIO("SCTP Packet", "[serializable][sctp][packet]") { sctpCommon::ResetBuffers(); @@ -589,7 +589,7 @@ SCENARIO("SCTP Packet", "[sctp][serializable]") REQUIRE(packet->GetFirstChunkOfType() == chunk2); // Insert CRC32C checksum. - packet->SetCRC32cChecksum(); + packet->WriteCRC32cChecksum(); auto crc32cChecksum = packet->GetChecksum(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestAbortAssociationChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestAbortAssociationChunk.cpp index f427874299..af8a06966d 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestAbortAssociationChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestAbortAssociationChunk.cpp @@ -8,7 +8,7 @@ #include #include // std::memset() -SCENARIO("SCTP Abort Association Chunk (6)", "[sctp][serializable]") +SCENARIO("SCTP Abort Association Chunk (6)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestCookieAckChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestCookieAckChunk.cpp index 6c2e31bb03..e8ce34ba09 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestCookieAckChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestCookieAckChunk.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("SCTP Cookie Acknowledgement Chunk (11)", "[sctp][serializable]") +SCENARIO("SCTP Cookie Acknowledgement Chunk (11)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestCookieEchoChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestCookieEchoChunk.cpp index 276808a65c..9845b0f2f4 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestCookieEchoChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestCookieEchoChunk.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("SCTP Cookie Echo Chunk (10)", "[sctp][serializable]") +SCENARIO("SCTP Cookie Echo Chunk (10)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestDataChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestDataChunk.cpp index 9dcac0c8fc..3939b139d6 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestDataChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestDataChunk.cpp @@ -8,7 +8,7 @@ #include #include // std::memset() -SCENARIO("SCTP Payload Data Chunk (0)", "[sctp][serializable]") +SCENARIO("SCTP Payload Data Chunk (0)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestForwardTsnChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestForwardTsnChunk.cpp index 6c521706cd..2bc6b36f26 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestForwardTsnChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestForwardTsnChunk.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Forward Cumulative TSN Chunk (192)", "[sctp][serializable]") +SCENARIO("Forward Cumulative TSN Chunk (192)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestHeartbeatAckChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestHeartbeatAckChunk.cpp index cced3a03c5..ec6916af39 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestHeartbeatAckChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestHeartbeatAckChunk.cpp @@ -9,7 +9,7 @@ #include #include // std::memset() -SCENARIO("SCTP Hearbeat Acknowledgement Chunk (5)", "[sctp][serializable]") +SCENARIO("SCTP Hearbeat Acknowledgement Chunk (5)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestHeartbeatRequestChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestHeartbeatRequestChunk.cpp index c624ce2eba..c03c21a191 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestHeartbeatRequestChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestHeartbeatRequestChunk.cpp @@ -9,7 +9,7 @@ #include #include // std::memset() -SCENARIO("SCTP Hearbeat Request Chunk (4)", "[sctp][serializable]") +SCENARIO("SCTP Hearbeat Request Chunk (4)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestIDataChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestIDataChunk.cpp index 63c507c737..e3241ef027 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestIDataChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestIDataChunk.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("SCTP I-Data Chunk (64)", "[sctp][serializable]") +SCENARIO("SCTP I-Data Chunk (64)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestIForwardTsnChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestIForwardTsnChunk.cpp index f8c58368a7..185ec6a395 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestIForwardTsnChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestIForwardTsnChunk.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("I-Forward Cumulative TSN Chunk (194)", "[sctp][serializable]") +SCENARIO("I-Forward Cumulative TSN Chunk (194)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestInitAckChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestInitAckChunk.cpp index ad1b41e3ef..9cf366c715 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestInitAckChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestInitAckChunk.cpp @@ -9,7 +9,7 @@ #include // std::memset() // NOTE: Simplified since it's similar to InitChunk. -SCENARIO("SCTP Init Acknowledgement (2)", "[sctp][serializable]") +SCENARIO("SCTP Init Acknowledgement (2)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestInitChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestInitChunk.cpp index 6e5af84c9c..212995e1c4 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestInitChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestInitChunk.cpp @@ -11,7 +11,7 @@ #include #include // std::memset() -SCENARIO("SCTP Init Chunk (1)", "[sctp][serializable]") +SCENARIO("SCTP Init Chunk (1)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestOperationErrorChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestOperationErrorChunk.cpp index 3f53ac391b..03fcfbd7d2 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestOperationErrorChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestOperationErrorChunk.cpp @@ -11,7 +11,7 @@ #include #include // std::memset() -SCENARIO("SCTP Operation Error Chunk (9)", "[sctp][serializable]") +SCENARIO("SCTP Operation Error Chunk (9)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestReConfigChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestReConfigChunk.cpp index 8a5e1c1320..162ad6b682 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestReConfigChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestReConfigChunk.cpp @@ -10,7 +10,7 @@ #include #include // std::memset() -SCENARIO("SCTP Re-Config Chunk (130)", "[sctp][serializable]") +SCENARIO("SCTP Re-Config Chunk (130)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestSackChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestSackChunk.cpp index b4c7c5068b..147c0d992d 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestSackChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestSackChunk.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Selective Acknowledgement Chunk (3)", "[sctp][serializable]") +SCENARIO("Selective Acknowledgement Chunk (3)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestShutdownAckChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestShutdownAckChunk.cpp index 39408fa606..d01d216846 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestShutdownAckChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestShutdownAckChunk.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("SCTP Shutdown Ack Chunk (8)", "[sctp][serializable]") +SCENARIO("SCTP Shutdown Ack Chunk (8)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestShutdownChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestShutdownChunk.cpp index 3b911f39c4..6c57f943b6 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestShutdownChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestShutdownChunk.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("SCTP Shutdown Association Chunk (7)", "[sctp][serializable]") +SCENARIO("SCTP Shutdown Association Chunk (7)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestShutdownCompleteChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestShutdownCompleteChunk.cpp index 7ac30780b3..70429e9478 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestShutdownCompleteChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestShutdownCompleteChunk.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("SCTP Shutdown Complete Chunk (14)", "[sctp][serializable]") +SCENARIO("SCTP Shutdown Complete Chunk (14)", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/chunks/TestUnknownChunk.cpp b/worker/test/src/RTC/SCTP/packet/chunks/TestUnknownChunk.cpp index ab94162cf0..c3646eeb27 100644 --- a/worker/test/src/RTC/SCTP/packet/chunks/TestUnknownChunk.cpp +++ b/worker/test/src/RTC/SCTP/packet/chunks/TestUnknownChunk.cpp @@ -5,7 +5,7 @@ #include #include // std::memset() -SCENARIO("SCTP Unknown Chunk", "[sctp][serializable]") +SCENARIO("SCTP Unknown Chunk", "[serializable][sctp][chunk]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/errorCauses/TestCookieReceivedWhileShuttingDownErrorCause.cpp b/worker/test/src/RTC/SCTP/packet/errorCauses/TestCookieReceivedWhileShuttingDownErrorCause.cpp index 6261bfa894..0ac9de40b0 100644 --- a/worker/test/src/RTC/SCTP/packet/errorCauses/TestCookieReceivedWhileShuttingDownErrorCause.cpp +++ b/worker/test/src/RTC/SCTP/packet/errorCauses/TestCookieReceivedWhileShuttingDownErrorCause.cpp @@ -5,7 +5,7 @@ #include #include // std::memset() -SCENARIO("Cookie Received While Shutting Down Error Cause (10)", "[sctp][serializable]") +SCENARIO("Cookie Received While Shutting Down Error Cause (10)", "[serializable][sctp][errorcause]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/errorCauses/TestInvalidMandatoryParameterErrorCause.cpp b/worker/test/src/RTC/SCTP/packet/errorCauses/TestInvalidMandatoryParameterErrorCause.cpp index a196c436ca..0e75a60cf8 100644 --- a/worker/test/src/RTC/SCTP/packet/errorCauses/TestInvalidMandatoryParameterErrorCause.cpp +++ b/worker/test/src/RTC/SCTP/packet/errorCauses/TestInvalidMandatoryParameterErrorCause.cpp @@ -5,7 +5,7 @@ #include #include // std::memset() -SCENARIO("Invalid Mandatory Parameter Error Cause (7)", "[sctp][serializable]") +SCENARIO("Invalid Mandatory Parameter Error Cause (7)", "[serializable][sctp][errorcause]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/errorCauses/TestInvalidStreamIdentifierErrorCause.cpp b/worker/test/src/RTC/SCTP/packet/errorCauses/TestInvalidStreamIdentifierErrorCause.cpp index dff9788fc2..20d1d3f5f4 100644 --- a/worker/test/src/RTC/SCTP/packet/errorCauses/TestInvalidStreamIdentifierErrorCause.cpp +++ b/worker/test/src/RTC/SCTP/packet/errorCauses/TestInvalidStreamIdentifierErrorCause.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Invalid Stream Identifier Error Cause (1)", "[sctp][serializable]") +SCENARIO("Invalid Stream Identifier Error Cause (1)", "[serializable][sctp][errorcause]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/errorCauses/TestMissingMandatoryParameterErrorCause.cpp b/worker/test/src/RTC/SCTP/packet/errorCauses/TestMissingMandatoryParameterErrorCause.cpp index 356515f9b7..69f4ea7df8 100644 --- a/worker/test/src/RTC/SCTP/packet/errorCauses/TestMissingMandatoryParameterErrorCause.cpp +++ b/worker/test/src/RTC/SCTP/packet/errorCauses/TestMissingMandatoryParameterErrorCause.cpp @@ -7,7 +7,7 @@ #include #include // std::memset() -SCENARIO("Invalid Stream Identifier Error Cause (2)", "[sctp][serializable]") +SCENARIO("Invalid Stream Identifier Error Cause (2)", "[serializable][sctp][errorcause]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/errorCauses/TestNoUserDataErrorCause.cpp b/worker/test/src/RTC/SCTP/packet/errorCauses/TestNoUserDataErrorCause.cpp index da04b8e846..d13e168b20 100644 --- a/worker/test/src/RTC/SCTP/packet/errorCauses/TestNoUserDataErrorCause.cpp +++ b/worker/test/src/RTC/SCTP/packet/errorCauses/TestNoUserDataErrorCause.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("No User Data Error Cause (9)", "[sctp][serializable]") +SCENARIO("No User Data Error Cause (9)", "[serializable][sctp][errorcause]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/errorCauses/TestOutOfResourceErrorCause.cpp b/worker/test/src/RTC/SCTP/packet/errorCauses/TestOutOfResourceErrorCause.cpp index c97d36e007..92debd4404 100644 --- a/worker/test/src/RTC/SCTP/packet/errorCauses/TestOutOfResourceErrorCause.cpp +++ b/worker/test/src/RTC/SCTP/packet/errorCauses/TestOutOfResourceErrorCause.cpp @@ -5,7 +5,7 @@ #include #include // std::memset() -SCENARIO("Out of Resource Error Cause (4)", "[sctp][serializable]") +SCENARIO("Out of Resource Error Cause (4)", "[serializable][sctp][errorcause]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/errorCauses/TestProtocolViolationErrorCause.cpp b/worker/test/src/RTC/SCTP/packet/errorCauses/TestProtocolViolationErrorCause.cpp index caf7164f3e..39e9cf0dd7 100644 --- a/worker/test/src/RTC/SCTP/packet/errorCauses/TestProtocolViolationErrorCause.cpp +++ b/worker/test/src/RTC/SCTP/packet/errorCauses/TestProtocolViolationErrorCause.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Protocol Violation Error Cause (13)", "[sctp][serializable]") +SCENARIO("Protocol Violation Error Cause (13)", "[serializable][sctp][errorcause]") { sctpCommon::ResetBuffers(); @@ -17,10 +17,10 @@ SCENARIO("Protocol Violation Error Cause (13)", "[sctp][serializable]") { // Code:13 (PROTOCOL_VIOLATION), Length: 10 0x00, 0x0D, 0x00, 0x0A, - // Additional Information: 0x1234567890AB - 0x12, 0x34, 0x56, 0x78, + // Additional Information: "error1" + 0x65, 0x72, 0x72, 0x6F, // 2 bytes of padding. - 0x90, 0xAB, 0x00, 0x00, + 0x72, 0x31, 0x00, 0x00, // Extra bytes that should be ignored 0xAA, 0xBB, 0xCC, 0xDD, 0xAA, 0xBB, 0xCC, @@ -39,12 +39,18 @@ SCENARIO("Protocol Violation Error Cause (13)", "[sctp][serializable]") REQUIRE(errorCause->HasAdditionalInformation() == true); REQUIRE(errorCause->GetAdditionalInformationLength() == 6); - REQUIRE(errorCause->GetAdditionalInformation()[0] == 0x12); - REQUIRE(errorCause->GetAdditionalInformation()[1] == 0x34); - REQUIRE(errorCause->GetAdditionalInformation()[2] == 0x56); - REQUIRE(errorCause->GetAdditionalInformation()[3] == 0x78); - REQUIRE(errorCause->GetAdditionalInformation()[4] == 0x90); - REQUIRE(errorCause->GetAdditionalInformation()[5] == 0xAB); + REQUIRE(errorCause->GetAdditionalInformation()[0] == 0x65); + REQUIRE(errorCause->GetAdditionalInformation()[1] == 0x72); + REQUIRE(errorCause->GetAdditionalInformation()[2] == 0x72); + REQUIRE(errorCause->GetAdditionalInformation()[3] == 0x6F); + REQUIRE(errorCause->GetAdditionalInformation()[4] == 0x72); + REQUIRE(errorCause->GetAdditionalInformation()[5] == 0x31); + + std::string additionalInfo( + reinterpret_cast(errorCause->GetAdditionalInformation()), + errorCause->GetAdditionalInformationLength()); + + REQUIRE(additionalInfo == "error1"); // These should be padding. REQUIRE(errorCause->GetAdditionalInformation()[6] == 0x00); REQUIRE(errorCause->GetAdditionalInformation()[7] == 0x00); @@ -65,12 +71,18 @@ SCENARIO("Protocol Violation Error Cause (13)", "[sctp][serializable]") REQUIRE(errorCause->HasAdditionalInformation() == true); REQUIRE(errorCause->GetAdditionalInformationLength() == 6); - REQUIRE(errorCause->GetAdditionalInformation()[0] == 0x12); - REQUIRE(errorCause->GetAdditionalInformation()[1] == 0x34); - REQUIRE(errorCause->GetAdditionalInformation()[2] == 0x56); - REQUIRE(errorCause->GetAdditionalInformation()[3] == 0x78); - REQUIRE(errorCause->GetAdditionalInformation()[4] == 0x90); - REQUIRE(errorCause->GetAdditionalInformation()[5] == 0xAB); + REQUIRE(errorCause->GetAdditionalInformation()[0] == 0x65); + REQUIRE(errorCause->GetAdditionalInformation()[1] == 0x72); + REQUIRE(errorCause->GetAdditionalInformation()[2] == 0x72); + REQUIRE(errorCause->GetAdditionalInformation()[3] == 0x6F); + REQUIRE(errorCause->GetAdditionalInformation()[4] == 0x72); + REQUIRE(errorCause->GetAdditionalInformation()[5] == 0x31); + + additionalInfo = std::string( + reinterpret_cast(errorCause->GetAdditionalInformation()), + errorCause->GetAdditionalInformationLength()); + + REQUIRE(additionalInfo == "error1"); // These should be padding. REQUIRE(errorCause->GetAdditionalInformation()[6] == 0x00); REQUIRE(errorCause->GetAdditionalInformation()[7] == 0x00); @@ -94,12 +106,18 @@ SCENARIO("Protocol Violation Error Cause (13)", "[sctp][serializable]") REQUIRE(clonedErrorCause->HasAdditionalInformation() == true); REQUIRE(clonedErrorCause->GetAdditionalInformationLength() == 6); - REQUIRE(clonedErrorCause->GetAdditionalInformation()[0] == 0x12); - REQUIRE(clonedErrorCause->GetAdditionalInformation()[1] == 0x34); - REQUIRE(clonedErrorCause->GetAdditionalInformation()[2] == 0x56); - REQUIRE(clonedErrorCause->GetAdditionalInformation()[3] == 0x78); - REQUIRE(clonedErrorCause->GetAdditionalInformation()[4] == 0x90); - REQUIRE(clonedErrorCause->GetAdditionalInformation()[5] == 0xAB); + REQUIRE(clonedErrorCause->GetAdditionalInformation()[0] == 0x65); + REQUIRE(clonedErrorCause->GetAdditionalInformation()[1] == 0x72); + REQUIRE(clonedErrorCause->GetAdditionalInformation()[2] == 0x72); + REQUIRE(clonedErrorCause->GetAdditionalInformation()[3] == 0x6F); + REQUIRE(clonedErrorCause->GetAdditionalInformation()[4] == 0x72); + REQUIRE(clonedErrorCause->GetAdditionalInformation()[5] == 0x31); + + additionalInfo = std::string( + reinterpret_cast(clonedErrorCause->GetAdditionalInformation()), + clonedErrorCause->GetAdditionalInformationLength()); + + REQUIRE(additionalInfo == "error1"); // These should be padding. REQUIRE(clonedErrorCause->GetAdditionalInformation()[6] == 0x00); REQUIRE(clonedErrorCause->GetAdditionalInformation()[7] == 0x00); @@ -168,7 +186,7 @@ SCENARIO("Protocol Violation Error Cause (13)", "[sctp][serializable]") REQUIRE(errorCause->GetAdditionalInformationLength() == 0); // 6 bytes + 2 bytes of padding. - errorCause->SetAdditionalInformation(sctpCommon::DataBuffer, 6); + errorCause->SetAdditionalInformation("iñaki"); CHECK_SCTP_ERROR_CAUSE( /*errorCause*/ errorCause, @@ -180,12 +198,12 @@ SCENARIO("Protocol Violation Error Cause (13)", "[sctp][serializable]") REQUIRE(errorCause->HasAdditionalInformation() == true); REQUIRE(errorCause->GetAdditionalInformationLength() == 6); - REQUIRE(errorCause->GetAdditionalInformation()[0] == 0x00); - REQUIRE(errorCause->GetAdditionalInformation()[1] == 0x01); - REQUIRE(errorCause->GetAdditionalInformation()[2] == 0x02); - REQUIRE(errorCause->GetAdditionalInformation()[3] == 0x03); - REQUIRE(errorCause->GetAdditionalInformation()[4] == 0x04); - REQUIRE(errorCause->GetAdditionalInformation()[5] == 0x05); + + std::string additionalInfo( + reinterpret_cast(errorCause->GetAdditionalInformation()), + errorCause->GetAdditionalInformationLength()); + + REQUIRE(additionalInfo == "iñaki"); // These should be padding. REQUIRE(errorCause->GetAdditionalInformation()[6] == 0x00); REQUIRE(errorCause->GetAdditionalInformation()[7] == 0x00); @@ -207,12 +225,13 @@ SCENARIO("Protocol Violation Error Cause (13)", "[sctp][serializable]") REQUIRE(parsedErrorCause->HasAdditionalInformation() == true); REQUIRE(parsedErrorCause->GetAdditionalInformationLength() == 6); - REQUIRE(parsedErrorCause->GetAdditionalInformation()[0] == 0x00); - REQUIRE(parsedErrorCause->GetAdditionalInformation()[1] == 0x01); - REQUIRE(parsedErrorCause->GetAdditionalInformation()[2] == 0x02); - REQUIRE(parsedErrorCause->GetAdditionalInformation()[3] == 0x03); - REQUIRE(parsedErrorCause->GetAdditionalInformation()[4] == 0x04); - REQUIRE(parsedErrorCause->GetAdditionalInformation()[5] == 0x05); + + additionalInfo = std::string( + reinterpret_cast(parsedErrorCause->GetAdditionalInformation()), + parsedErrorCause->GetAdditionalInformationLength()); + + REQUIRE(additionalInfo == "iñaki"); + // These should be padding. REQUIRE(parsedErrorCause->GetAdditionalInformation()[6] == 0x00); REQUIRE(parsedErrorCause->GetAdditionalInformation()[7] == 0x00); diff --git a/worker/test/src/RTC/SCTP/packet/errorCauses/TestRestartOfAnAssociationWithNewAddressesErrorCause.cpp b/worker/test/src/RTC/SCTP/packet/errorCauses/TestRestartOfAnAssociationWithNewAddressesErrorCause.cpp index cd8a52cc80..79a72dbbec 100644 --- a/worker/test/src/RTC/SCTP/packet/errorCauses/TestRestartOfAnAssociationWithNewAddressesErrorCause.cpp +++ b/worker/test/src/RTC/SCTP/packet/errorCauses/TestRestartOfAnAssociationWithNewAddressesErrorCause.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Restart of an Association with New Addresses Error Cause (11)", "[sctp][serializable]") +SCENARIO("Restart of an Association with New Addresses Error Cause (11)", "[serializable][sctp][errorcause]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/errorCauses/TestStaleCookieErrorCause.cpp b/worker/test/src/RTC/SCTP/packet/errorCauses/TestStaleCookieErrorCause.cpp index 197dcc10ba..bbbefd48e4 100644 --- a/worker/test/src/RTC/SCTP/packet/errorCauses/TestStaleCookieErrorCause.cpp +++ b/worker/test/src/RTC/SCTP/packet/errorCauses/TestStaleCookieErrorCause.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Stale Cookie Error Cause (3)", "[sctp][serializable]") +SCENARIO("Stale Cookie Error Cause (3)", "[serializable][sctp][errorcause]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnknownErrorCause.cpp b/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnknownErrorCause.cpp index 928ab66342..38d6bcc662 100644 --- a/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnknownErrorCause.cpp +++ b/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnknownErrorCause.cpp @@ -5,7 +5,7 @@ #include #include // std::memset() -SCENARIO("Unknown Error Cause", "[sctp][serializable]") +SCENARIO("Unknown Error Cause", "[serializable][sctp][errorcause]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnrecognizedChunkTypeErrorCause.cpp b/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnrecognizedChunkTypeErrorCause.cpp index 62946e0948..6ab0575868 100644 --- a/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnrecognizedChunkTypeErrorCause.cpp +++ b/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnrecognizedChunkTypeErrorCause.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Unrecognized Chunk Type Error Cause (6)", "[sctp][serializable]") +SCENARIO("Unrecognized Chunk Type Error Cause (6)", "[serializable][sctp][errorcause]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnrecognizedParametersErrorCause.cpp b/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnrecognizedParametersErrorCause.cpp index 2ee5202c5c..f4fbfba77d 100644 --- a/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnrecognizedParametersErrorCause.cpp +++ b/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnrecognizedParametersErrorCause.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Unrecognized Parameters Error Cause (8)", "[sctp][serializable]") +SCENARIO("Unrecognized Parameters Error Cause (8)", "[serializable][sctp][errorcause]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnresolvableAddressErrorCause.cpp b/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnresolvableAddressErrorCause.cpp index 8e7058d512..111f4c3ddc 100644 --- a/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnresolvableAddressErrorCause.cpp +++ b/worker/test/src/RTC/SCTP/packet/errorCauses/TestUnresolvableAddressErrorCause.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Unresolvable Address Error Cause (5)", "[sctp][serializable]") +SCENARIO("Unresolvable Address Error Cause (5)", "[serializable][sctp][errorcause]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/errorCauses/TestUserInitiatedAbortErrorCause.cpp b/worker/test/src/RTC/SCTP/packet/errorCauses/TestUserInitiatedAbortErrorCause.cpp index eeb6945898..7fedebda22 100644 --- a/worker/test/src/RTC/SCTP/packet/errorCauses/TestUserInitiatedAbortErrorCause.cpp +++ b/worker/test/src/RTC/SCTP/packet/errorCauses/TestUserInitiatedAbortErrorCause.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("User-Initiated Abort Error Cause (12)", "[sctp][serializable]") +SCENARIO("User-Initiated Abort Error Cause (12)", "[serializable][sctp][errorcause]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestAddIncomingStreamsRequestParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestAddIncomingStreamsRequestParameter.cpp index af0ba49577..bdf865e73b 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestAddIncomingStreamsRequestParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestAddIncomingStreamsRequestParameter.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Add Incoming Streams Request Parameter (18)", "[sctp][serializable]") +SCENARIO("Add Incoming Streams Request Parameter (18)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestAddOutgoingStreamsRequestParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestAddOutgoingStreamsRequestParameter.cpp index e10dbbcdce..a39e9e0e67 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestAddOutgoingStreamsRequestParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestAddOutgoingStreamsRequestParameter.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Add Outgoing Streams Request Parameter (17)", "[sctp][serializable]") +SCENARIO("Add Outgoing Streams Request Parameter (17)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestCookiePreservativeParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestCookiePreservativeParameter.cpp index 326bb25b86..b76faf9f76 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestCookiePreservativeParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestCookiePreservativeParameter.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Cookie Preservative Parameter (9)", "[sctp][serializable]") +SCENARIO("Cookie Preservative Parameter (9)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestForwardTsnSupportedParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestForwardTsnSupportedParameter.cpp index 8b42d9c318..e61a68a462 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestForwardTsnSupportedParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestForwardTsnSupportedParameter.cpp @@ -5,7 +5,7 @@ #include #include // std::memset() -SCENARIO("Forward-TSN-Supported Parameter (32769)", "[sctp][serializable]") +SCENARIO("Forward-TSN-Supported Parameter (32769)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestHeartbeatInfoParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestHeartbeatInfoParameter.cpp index 450530d37b..b78a203373 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestHeartbeatInfoParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestHeartbeatInfoParameter.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Heartbeat Info Parameter (1)", "[sctp][serializable]") +SCENARIO("Heartbeat Info Parameter (1)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestIPv4AddressParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestIPv4AddressParameter.cpp index 044c30785a..96bb9b7244 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestIPv4AddressParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestIPv4AddressParameter.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("IPv4 Adress Parameter (5)", "[sctp][serializable]") +SCENARIO("IPv4 Adress Parameter (5)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestIPv6AddressParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestIPv6AddressParameter.cpp index 28e7cd5763..94cc628a29 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestIPv6AddressParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestIPv6AddressParameter.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("IPv6 Adress Parameter (6)", "[sctp][serializable]") +SCENARIO("IPv6 Adress Parameter (6)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestIncomingSsnResetRequestParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestIncomingSsnResetRequestParameter.cpp index dcd0b05b02..2b12584a88 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestIncomingSsnResetRequestParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestIncomingSsnResetRequestParameter.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Incoming SSN Reset Request Parameter (14)", "[sctp][serializable]") +SCENARIO("Incoming SSN Reset Request Parameter (14)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestOutgoingSsnResetRequestParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestOutgoingSsnResetRequestParameter.cpp index 5442e42531..1b3459f054 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestOutgoingSsnResetRequestParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestOutgoingSsnResetRequestParameter.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Outgoing SSN Reset Request Parameter (13)", "[sctp][serializable]") +SCENARIO("Outgoing SSN Reset Request Parameter (13)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestReconfigurationResponseParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestReconfigurationResponseParameter.cpp index 3507a7b1c7..d551d33fcf 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestReconfigurationResponseParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestReconfigurationResponseParameter.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Re-configuration Response Parameter (16)", "[sctp][serializable]") +SCENARIO("Re-configuration Response Parameter (16)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestSsnTsnResetRequestParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestSsnTsnResetRequestParameter.cpp index 1b9ed14e25..a08d94e274 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestSsnTsnResetRequestParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestSsnTsnResetRequestParameter.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("SSN/TSN Reset Request Parameter (15)", "[sctp][serializable]") +SCENARIO("SSN/TSN Reset Request Parameter (15)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestStateCookieParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestStateCookieParameter.cpp index 65fc3f8244..bda9996454 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestStateCookieParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestStateCookieParameter.cpp @@ -1,12 +1,14 @@ #include "common.hpp" #include "MediaSoupErrors.hpp" +#include "RTC/SCTP/association/NegotiatedCapabilities.hpp" +#include "RTC/SCTP/association/StateCookie.hpp" #include "RTC/SCTP/packet/Parameter.hpp" #include "RTC/SCTP/packet/parameters/StateCookieParameter.hpp" #include "RTC/SCTP/sctpCommon.hpp" #include #include // std::memset() -SCENARIO("State Cookie Parameter (7)", "[sctp][serializable]") +SCENARIO("State Cookie Parameter (7)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); @@ -96,7 +98,7 @@ SCENARIO("State Cookie Parameter (7)", "[sctp][serializable]") delete clonedParameter; } - SECTION("StateCookieParameter::Factory() succeeds") + SECTION("StateCookieParameter::Factory() succeeds (1)") { auto* parameter = RTC::SCTP::StateCookieParameter::Factory( sctpCommon::FactoryBuffer, sizeof(sctpCommon::FactoryBuffer)); @@ -129,6 +131,14 @@ SCENARIO("State Cookie Parameter (7)", "[sctp][serializable]") // sctpCommon::DataBuffer + 1 which is initialized to 0x0A. parameter->SetCookie(sctpCommon::DataBuffer + 10, 1); + REQUIRE(parameter->HasCookie() == true); + REQUIRE(parameter->GetCookieLength() == 1); + REQUIRE(parameter->GetCookie()[0] == 0x0A); + // These should be padding. + REQUIRE(parameter->GetCookie()[1] == 0x00); + REQUIRE(parameter->GetCookie()[2] == 0x00); + REQUIRE(parameter->GetCookie()[3] == 0x00); + /* Parse itself and compare. */ auto* parsedParameter = @@ -155,4 +165,63 @@ SCENARIO("State Cookie Parameter (7)", "[sctp][serializable]") delete parsedParameter; } + + SECTION("StateCookieParameter::Factory() succeeds (2)") + { + auto* parameter = RTC::SCTP::StateCookieParameter::Factory( + sctpCommon::FactoryBuffer, sizeof(sctpCommon::FactoryBuffer)); + + CHECK_SCTP_PARAMETER( + /*parameter*/ parameter, + /*buffer*/ sctpCommon::FactoryBuffer, + /*bufferLength*/ sizeof(sctpCommon::FactoryBuffer), + /*length*/ 4, + /*parameterType*/ RTC::SCTP::Parameter::ParameterType::STATE_COOKIE, + /*unknownType*/ false, + /*actionForUnknownParameterType*/ RTC::SCTP::Parameter::ActionForUnknownParameterType::STOP); + + /* Modify it. */ + + // Create a StateCookie. + RTC::SCTP::NegotiatedCapabilities negotiatedCapabilities = { .maxOutboundStreams = 62000, + .maxInboundStreams = 55555, + .partialReliability = true, + .messageInterleaving = true, + .reconfig = true, + .zeroChecksum = false }; + + // Build the StateCookie in place within the StateCookieParameter. + parameter->WriteStateCookieInPlace( + /*localVerificationTag*/ 6660666, + /*remoteVerificationTag*/ 9990999, + /*localInitialTsn*/ 1110111, + /*remoteInitialTsn*/ 2220222, + /*remoteAdvertisedReceiverWindowCredit*/ 999909999, + /*tieTag*/ 1111222233334444, + negotiatedCapabilities); + + REQUIRE(parameter->HasCookie() == true); + REQUIRE(parameter->GetCookieLength() == RTC::SCTP::StateCookie::StateCookieLength); + + /* Parse itself and compare. */ + + auto* parsedParameter = + RTC::SCTP::StateCookieParameter::Parse(parameter->GetBuffer(), parameter->GetLength()); + + delete parameter; + + CHECK_SCTP_PARAMETER( + /*parameter*/ parsedParameter, + /*buffer*/ sctpCommon::FactoryBuffer, + /*bufferLength*/ 4 + RTC::SCTP::StateCookie::StateCookieLength, + /*length*/ 4 + RTC::SCTP::StateCookie::StateCookieLength, + /*parameterType*/ RTC::SCTP::Parameter::ParameterType::STATE_COOKIE, + /*unknownType*/ false, + /*actionForUnknownParameterType*/ RTC::SCTP::Parameter::ActionForUnknownParameterType::STOP); + + REQUIRE(parsedParameter->HasCookie() == true); + REQUIRE(parsedParameter->GetCookieLength() == RTC::SCTP::StateCookie::StateCookieLength); + + delete parsedParameter; + } } diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestSupportedAddressTypesParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestSupportedAddressTypesParameter.cpp index f64c9afe34..d3c85d692a 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestSupportedAddressTypesParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestSupportedAddressTypesParameter.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Supported Address Types Parameter (12)", "[sctp][serializable]") +SCENARIO("Supported Address Types Parameter (12)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestSupportedExtensionsParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestSupportedExtensionsParameter.cpp index 08d66f1793..b6f8338c34 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestSupportedExtensionsParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestSupportedExtensionsParameter.cpp @@ -3,11 +3,11 @@ #include "RTC/SCTP/packet/Chunk.hpp" #include "RTC/SCTP/packet/Parameter.hpp" #include "RTC/SCTP/packet/parameters/SupportedExtensionsParameter.hpp" -#include "RTC/SCTP/sctpCommon.hpp" +#include "RTC/SCTP/sctpCommon.hpp" // in worker/test/include/ #include #include // std::memset() -SCENARIO("Supported Extensions Parameter (32776)", "[sctp][serializable]") +SCENARIO("Supported Extensions Parameter (32776)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); @@ -40,7 +40,11 @@ SCENARIO("Supported Extensions Parameter (32776)", "[sctp][serializable]") REQUIRE(parameter->GetNumberOfChunkTypes() == 3); REQUIRE(parameter->GetChunkTypeAt(0) == RTC::SCTP::Chunk::ChunkType::RE_CONFIG); REQUIRE(parameter->GetChunkTypeAt(1) == RTC::SCTP::Chunk::ChunkType::ECNE); - REQUIRE(static_cast(parameter->GetChunkTypeAt(2)) == 0x42); + REQUIRE(parameter->GetChunkTypeAt(2) == static_cast(0x42)); + REQUIRE(parameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::RE_CONFIG) == true); + REQUIRE(parameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::ECNE) == true); + REQUIRE(parameter->IncludesChunkType(static_cast(0x42)) == true); + REQUIRE(parameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::I_DATA) == false); /* Serialize it. */ @@ -60,7 +64,11 @@ SCENARIO("Supported Extensions Parameter (32776)", "[sctp][serializable]") REQUIRE(parameter->GetNumberOfChunkTypes() == 3); REQUIRE(parameter->GetChunkTypeAt(0) == RTC::SCTP::Chunk::ChunkType::RE_CONFIG); REQUIRE(parameter->GetChunkTypeAt(1) == RTC::SCTP::Chunk::ChunkType::ECNE); - REQUIRE(static_cast(parameter->GetChunkTypeAt(2)) == 0x42); + REQUIRE(parameter->GetChunkTypeAt(2) == static_cast(0x42)); + REQUIRE(parameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::RE_CONFIG) == true); + REQUIRE(parameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::ECNE) == true); + REQUIRE(parameter->IncludesChunkType(static_cast(0x42)) == true); + REQUIRE(parameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::I_DATA) == false); /* Clone it. */ @@ -83,7 +91,12 @@ SCENARIO("Supported Extensions Parameter (32776)", "[sctp][serializable]") REQUIRE(clonedParameter->GetNumberOfChunkTypes() == 3); REQUIRE(clonedParameter->GetChunkTypeAt(0) == RTC::SCTP::Chunk::ChunkType::RE_CONFIG); REQUIRE(clonedParameter->GetChunkTypeAt(1) == RTC::SCTP::Chunk::ChunkType::ECNE); - REQUIRE(static_cast(clonedParameter->GetChunkTypeAt(2)) == 0x42); + REQUIRE(clonedParameter->GetChunkTypeAt(2) == static_cast(0x42)); + REQUIRE(clonedParameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::RE_CONFIG) == true); + REQUIRE(clonedParameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::ECNE) == true); + REQUIRE( + clonedParameter->IncludesChunkType(static_cast(0x42)) == true); + REQUIRE(clonedParameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::I_DATA) == false); delete clonedParameter; } @@ -103,6 +116,9 @@ SCENARIO("Supported Extensions Parameter (32776)", "[sctp][serializable]") /*actionForUnknownParameterType*/ RTC::SCTP::Parameter::ActionForUnknownParameterType::SKIP); REQUIRE(parameter->GetNumberOfChunkTypes() == 0); + REQUIRE(parameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::RE_CONFIG) == false); + REQUIRE(parameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::ECNE) == false); + REQUIRE(parameter->IncludesChunkType(static_cast(0x42)) == false); /* Modify it. */ @@ -121,10 +137,11 @@ SCENARIO("Supported Extensions Parameter (32776)", "[sctp][serializable]") REQUIRE(parameter->GetNumberOfChunkTypes() == 2); REQUIRE(parameter->GetChunkTypeAt(0) == RTC::SCTP::Chunk::ChunkType::RE_CONFIG); REQUIRE(parameter->GetChunkTypeAt(1) == RTC::SCTP::Chunk::ChunkType::CWR); + REQUIRE(parameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::RE_CONFIG) == true); + REQUIRE(parameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::CWR) == true); parameter->AddChunkType(RTC::SCTP::Chunk::ChunkType::OPERATION_ERROR); parameter->AddChunkType(RTC::SCTP::Chunk::ChunkType::COOKIE_ACK); - // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) parameter->AddChunkType(static_cast(99)); CHECK_SCTP_PARAMETER( @@ -141,7 +158,12 @@ SCENARIO("Supported Extensions Parameter (32776)", "[sctp][serializable]") REQUIRE(parameter->GetChunkTypeAt(1) == RTC::SCTP::Chunk::ChunkType::CWR); REQUIRE(parameter->GetChunkTypeAt(2) == RTC::SCTP::Chunk::ChunkType::OPERATION_ERROR); REQUIRE(parameter->GetChunkTypeAt(3) == RTC::SCTP::Chunk::ChunkType::COOKIE_ACK); - REQUIRE(static_cast(parameter->GetChunkTypeAt(4)) == 99); + REQUIRE(parameter->GetChunkTypeAt(4) == static_cast(99)); + REQUIRE(parameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::RE_CONFIG) == true); + REQUIRE(parameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::CWR) == true); + REQUIRE(parameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::OPERATION_ERROR) == true); + REQUIRE(parameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::COOKIE_ACK) == true); + REQUIRE(parameter->IncludesChunkType(static_cast(99)) == true); /* Parse itself and compare. */ @@ -164,7 +186,12 @@ SCENARIO("Supported Extensions Parameter (32776)", "[sctp][serializable]") REQUIRE(parsedParameter->GetChunkTypeAt(1) == RTC::SCTP::Chunk::ChunkType::CWR); REQUIRE(parsedParameter->GetChunkTypeAt(2) == RTC::SCTP::Chunk::ChunkType::OPERATION_ERROR); REQUIRE(parsedParameter->GetChunkTypeAt(3) == RTC::SCTP::Chunk::ChunkType::COOKIE_ACK); - REQUIRE(static_cast(parsedParameter->GetChunkTypeAt(4)) == 99); + REQUIRE(parsedParameter->GetChunkTypeAt(4) == static_cast(99)); + REQUIRE(parsedParameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::RE_CONFIG) == true); + REQUIRE(parsedParameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::CWR) == true); + REQUIRE(parsedParameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::OPERATION_ERROR) == true); + REQUIRE(parsedParameter->IncludesChunkType(RTC::SCTP::Chunk::ChunkType::COOKIE_ACK) == true); + REQUIRE(parsedParameter->IncludesChunkType(static_cast(99)) == true); delete parsedParameter; } diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestUnknownParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestUnknownParameter.cpp index 0094b3abc7..c8f35618df 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestUnknownParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestUnknownParameter.cpp @@ -5,7 +5,7 @@ #include #include // std::memset() -SCENARIO("Unknown Parameter", "[sctp][serializable]") +SCENARIO("Unknown Parameter", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestUnrecognizedParameterParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestUnrecognizedParameterParameter.cpp index c44796cfd1..b921cdd2ba 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestUnrecognizedParameterParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestUnrecognizedParameterParameter.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Unrecognized Parameter Parameter (7)", "[sctp][serializable]") +SCENARIO("Unrecognized Parameter Parameter (7)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/RTC/SCTP/packet/parameters/TestZeroChecksumAcceptableParameter.cpp b/worker/test/src/RTC/SCTP/packet/parameters/TestZeroChecksumAcceptableParameter.cpp index 39fcb2b4ec..f015e36579 100644 --- a/worker/test/src/RTC/SCTP/packet/parameters/TestZeroChecksumAcceptableParameter.cpp +++ b/worker/test/src/RTC/SCTP/packet/parameters/TestZeroChecksumAcceptableParameter.cpp @@ -6,7 +6,7 @@ #include #include // std::memset() -SCENARIO("Zero Checksum Acceptable Parameter (32769)", "[sctp][serializable]") +SCENARIO("Zero Checksum Acceptable Parameter (32769)", "[serializable][sctp][parameter]") { sctpCommon::ResetBuffers(); diff --git a/worker/test/src/Utils/TestCrypto.cpp b/worker/test/src/Utils/TestCrypto.cpp index f3057d5c59..636bdd09a0 100644 --- a/worker/test/src/Utils/TestCrypto.cpp +++ b/worker/test/src/Utils/TestCrypto.cpp @@ -1,6 +1,8 @@ #include "common.hpp" #include "Utils.hpp" #include +#include // std::numeric_limits() +#include SCENARIO("Utils::Crypto", "[utils][crypto]") { @@ -74,3 +76,29 @@ SCENARIO("Utils::Crypto", "[utils][crypto]") REQUIRE(Utils::Crypto::GetCRC32c(dataSCSICommandPDU, sizeof(dataSCSICommandPDU)) == 0x563A96D9); } } + +SCENARIO("Utils::Crypto::GetRandomUInt()", "[utils][crypto]") +{ + std::set randomUint32Numbers; + std::set randomUint64Numbers; + + for (size_t i = 0; i < 200; ++i) + { + auto randomNumber = + Utils::Crypto::GetRandomUInt(0, std::numeric_limits::max()); + + REQUIRE(randomUint32Numbers.find(randomNumber) == randomUint32Numbers.end()); + + randomUint32Numbers.insert(randomNumber); + } + + for (size_t i = 0; i < 200; ++i) + { + auto randomNumber = + Utils::Crypto::GetRandomUInt(0, std::numeric_limits::max()); + + REQUIRE(randomUint64Numbers.find(randomNumber) == randomUint64Numbers.end()); + + randomUint64Numbers.insert(randomNumber); + } +}