Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
d29e073
[SCTP] Socket part 2
ibc May 6, 2025
a2e9f4b
more
ibc May 7, 2025
86f55b4
more
ibc May 7, 2025
908b1d1
more
ibc May 9, 2025
01feb43
add StateCookie::IsMediasoupStateCookie() static method
ibc May 9, 2025
04f0758
more
ibc May 9, 2025
6e87917
add StateCookie::DetermineSctpImplementation()
ibc May 9, 2025
76222cf
fix fuzzer
ibc May 9, 2025
c5eda5f
add BackoffTimerHandle
ibc May 9, 2025
932c843
Improve TestStateCookie.cpp
ibc May 10, 2025
bb2fa4e
cosmetic
ibc May 11, 2025
494113d
more
ibc May 11, 2025
e0bece2
cosmetic
ibc May 12, 2025
0f42e64
add SocketMetrics
ibc May 12, 2025
44c537b
minimal
ibc May 12, 2025
27af7f1
rename my => local, peer => remote
ibc May 12, 2025
8cd7451
more
ibc May 12, 2025
0259ecb
more
ibc May 12, 2025
afffe96
more
ibc May 12, 2025
953fc56
cosmetic
ibc May 12, 2025
8a03da5
fix fuzzer
ibc May 12, 2025
17c3cc2
more
ibc May 14, 2025
c66d453
more
ibc May 14, 2025
a0af838
more
ibc May 14, 2025
ccaabf1
Merge branch 'v3' into sctp-socket-part-2
ibc May 15, 2025
5f589c8
more
ibc May 15, 2025
67e3e6a
more
ibc May 16, 2025
4e1148e
Merge branch 'v3' into sctp-socket-part-2
ibc May 16, 2025
c7c47cc
improve C++ random number generator and add GetRandomUInt64()
ibc May 16, 2025
d0d314a
apply changes in classes
ibc May 16, 2025
70091d6
SCTP Socket: handle INIT_ACK
ibc May 16, 2025
97054cb
improve
ibc May 16, 2025
7e58bd0
fix fuzzer
ibc May 18, 2025
7f2a2ba
Merge branch 'v3' into sctp-socket-part-2
ibc May 21, 2025
859e178
not much
ibc May 21, 2025
f8a7bde
add ErrorCause::ToString() method
ibc May 21, 2025
8862887
Merge branch 'v3' into sctp-socket-part-2
ibc Jun 14, 2025
aaaf77a
Merge branch 'v3' into sctp-socket-part-2
ibc Jun 27, 2025
2f4b36a
Merge branch 'v3' into sctp-socket-part-2
ibc Jul 30, 2025
1dd6624
Merge branch 'v3' into sctp-socket-part-2
ibc Sep 1, 2025
6b704df
Merge branch 'v3' into sctp-socket-part-2
ibc Sep 7, 2025
4fce777
Merge branch 'v3' into sctp-socket-part-2
ibc Sep 9, 2025
6075d66
Merge branch 'v3' into sctp-socket-part-2
ibc Sep 10, 2025
9c6d8e2
fix
ibc Sep 10, 2025
d3672a3
fix https://github.com/versatica/mediasoup/pull/1533/#issuecomment-32…
ibc Sep 11, 2025
88ded0c
Merge branch 'v3' into sctp-socket-part-2
ibc Sep 11, 2025
5ee722d
Merge branch 'v3' into sctp-socket-part-2
ibc Feb 17, 2026
2076b9c
cosmetic
ibc Feb 17, 2026
0a6cbd4
fix
ibc Feb 17, 2026
63f8029
cosmetic
ibc Feb 17, 2026
a9fd491
fix and "cosmetic"
ibc Feb 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 20 additions & 17 deletions worker/fuzzer/src/RTC/SCTP/association/FuzzerStateCookie.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand All @@ -36,33 +39,33 @@ 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();

auto* clonedStateCookie = stateCookie->Clone(StateCookieCloneBuffer, 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();

Expand Down
4 changes: 2 additions & 2 deletions worker/fuzzer/src/RTC/SCTP/packet/FuzzerPacket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
Expand Down
6 changes: 0 additions & 6 deletions worker/include/RTC/SCTP/TODO_SCTP.md
Original file line number Diff line number Diff line change
Expand Up @@ -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?
17 changes: 17 additions & 0 deletions worker/include/RTC/SCTP/association/NegotiatedCapabilities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <variant> // std::variant, std::visit()

namespace RTC
{
Expand All @@ -14,6 +18,19 @@ namespace RTC
*/
struct NegotiatedCapabilities
{
using InitOrInitAckChunkVariant = std::variant<const InitChunk*, const InitAckChunk*>;

/**
* 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).
*/
Expand Down
222 changes: 162 additions & 60 deletions worker/include/RTC/SCTP/association/Socket.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <string>
#include <string_view>

namespace RTC
{
Expand All @@ -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<bool> 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<typename... States>
void AssertState(States... expectedStates) const;

template<typename... States>
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<TransmissionControlBlock> tcb;
// T1-init timer.
const std::unique_ptr<BackoffTimerHandle> t1InitTimer;
// T1-cookie timer.
const std::unique_ptr<BackoffTimerHandle> t1CookieTimer;
// T2-shutdown timer.
const std::unique_ptr<BackoffTimerHandle> t2ShutdownTimer;
};
} // namespace SCTP
} // namespace RTC
Expand Down
Loading
Loading