Skip to content

Commit 9b84c0b

Browse files
authored
SCTP part 9 (#1782)
1 parent edb8c72 commit 9b84c0b

33 files changed

Lines changed: 4365 additions & 225 deletions

worker/.clangd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
CompileFlags:
22
CompilationDatabase: "out/Release/build"
3+
4+
CompletionOptions:
5+
HeaderInsertion: Never

worker/include/RTC/SCTP/TODO_SCTP.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
## Related to mediasoup SCTP implementation
44

5+
- `DataChunk`, IDataChunk`and`AnyDataChunk`: Add `SetUserData(UserData)`.
6+
7+
- Remove all default values of class memners in .hpp of all classes in case the constructor must give them initial value.
8+
9+
- Lot of stuff missing in `TransmissionControlBock` class and I forgot to add "TODO: SCTP" in them.
10+
11+
- dcsctp uses µs (webrtc::Timestamp::Micros()) internally, while mediasoup uses ms (`DepLibUV::GetTimeMs()`). When porting dcsctp timeout/duration logic, make sure to convert accordingly. Do not mix units in the same field.
12+
513
- `Association`: When transitioning to CLOSED (due to failure while connecting or closure) we should emit a new event "stcpclosed" in all `DataProducers/Consumers`.
614

715
- When receiving SCTP RE-CONFIG, we should emit "streamclosed" in those `DataProducers/DataConsumers` whose stream ID have been closed.
@@ -19,6 +27,10 @@
1927
- We must also remove `device.sctpCapabilities` getter from mediasoup-client because anyway we are making up those values!
2028
- Also must update the website documentation.
2129

30+
- Replicate `retransmission_queue_test.cc` of dcsctp.
31+
32+
- When we invoke `close()` on a `DataProducer/Consumer` in server, we must end calling `sctpAssociation->ResetStream([streamId])` so it sends `ReConfig` to peer.
33+
2234
- In `transport.dump()` (maybe also in `getStats()`) we must properly obtain `OS` and `MIS` according to the number of SCTP streams negotiated via INIT + INIT_ACK. And if SCTP is not yet established, then... not sure.
2335
- In `Association::FillBuffer()` we should not pass `this->sctpOptions.negotiatedMaxOutboundStreams/negotiatedMaxInboundStreams` but the current values.
2436

worker/include/RTC/SCTP/association/StreamResetHandler.hpp

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "RTC/SCTP/packet/parameters/OutgoingSsnResetRequestParameter.hpp"
1111
#include "RTC/SCTP/packet/parameters/ReconfigurationResponseParameter.hpp"
1212
#include "RTC/SCTP/public/AssociationListener.hpp"
13+
#include "RTC/SCTP/tx/RetransmissionQueue.hpp"
1314
#include "handles/BackoffTimerHandle.hpp"
1415
#include <span>
1516
#include <vector>
@@ -49,7 +50,7 @@ namespace RTC
4950
* not-yet-sent messages will be discarded, but that may change in the future.
5051
* RFC8831 allows both behaviors.
5152
*/
52-
class StreamResetHandler : public TCBContext, public BackoffTimerHandle::Listener
53+
class StreamResetHandler : public BackoffTimerHandle::Listener
5354
{
5455
private:
5556
enum class ReqSeqNbrValidationResult : uint8_t
@@ -164,12 +165,12 @@ namespace RTC
164165

165166
public:
166167
StreamResetHandler(
167-
AssociationListener& associationListener, TCBContext* tcbContext
168+
AssociationListener& associationListener,
169+
TCBContext* tcbContext,
168170
// TODO: SCTP: Implement
169171
// DataTracker* dataTracker,
170172
// ReassemblyQueue* reassemblyQueue,
171-
// RetransmissionQueue* retransmissionQueue
172-
);
173+
RetransmissionQueue* retransmissionQueue);
173174

174175
~StreamResetHandler() override;
175176

@@ -183,6 +184,23 @@ namespace RTC
183184
*/
184185
void ResetStreams(std::span<const uint16_t> outgoingStreamIds);
185186

187+
/**
188+
* Whether a Reset Streams request should be send. Will return `false` if
189+
* there is no need to create a request (no streams to reset) or if there
190+
* already is an ongoing stream reset request that hasn't completed yet.
191+
*/
192+
bool ShouldCreateStreamResetRequest() const;
193+
194+
/**
195+
* Creates a Reset Streams request that must be sent if returned. Will
196+
* start the reconfig timer.
197+
*
198+
* @remarks
199+
* - The caller must check `ShouldCreateStreamResetRequest()` first and
200+
* only invoke this method if the former returns `true`.
201+
*/
202+
void CreateStreamResetRequest(Packet* packet);
203+
186204
/**
187205
* Called when handling and incoming RE-CONFIG chunk. Processes a stream
188206
* reconfiguration chunk and may send a RE-CONFIG back to the peer with
@@ -197,18 +215,10 @@ namespace RTC
197215
bool ValidateReceivedReConfigChunk(const ReConfigChunk* receivedReConfigChunk);
198216

199217
/**
200-
* Creates a Reset Streams request that must be sent if returned. Will
201-
* start the reconfig timer. Will return `nullptr` if there is no need
202-
* to create a request (no streams to reset) or if there already is an
203-
* ongoing stream reset request that hasn't completed yet.
204-
*/
205-
ReConfigChunk* CreateStreamResetRequest();
206-
207-
/**
208-
* Creates the actual RE-CONFIG chunk. A request (which set
209-
* `currentRequest`) must have been created prior.
218+
* Adds the actual RE-CONFIG chunk to the given Packet. A request (which
219+
* set `this->currentRequest`) must have been created prior.
210220
*/
211-
ReConfigChunk* CreateReconfigChunk();
221+
void CreateReConfigChunk(Packet* packet);
212222

213223
/**
214224
* Called to validate the `reqSeqNbr`, that it's the next in sequence.
@@ -250,11 +260,12 @@ namespace RTC
250260

251261
private:
252262
AssociationListener& associationListener;
253-
TCBContext* tcbContext{ nullptr };
263+
TCBContext* tcbContext;
264+
// TODO: SCTP: Implement
265+
// DataTracker* dataTracker;,
254266
// TODO: SCTP: Implement
255-
// DataTracker* dataTracker{ nullptr };,
256-
// ReassemblyQueue* reassemblyQueue{ nullptr };,
257-
// RetransmissionQueue* retransmissionQueue{ nullptr };
267+
// ReassemblyQueue* reassemblyQueue;,
268+
RetransmissionQueue* retransmissionQueue;
258269
UnwrappedReConfigRequestSn::Unwrapper incomingReConfigRequestSnUnwrapper;
259270
const std::unique_ptr<BackoffTimerHandle> reConfigTimer;
260271
// The next sequence number for outgoing stream requests.

worker/include/RTC/SCTP/association/TCBContext.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ namespace RTC
3232
virtual uint32_t GetRemoteInitialTsn() const = 0;
3333

3434
/**
35-
* To be called when a RTT has been measured, to update the RTO value.
35+
* To be called when a RTT (ms) has been measured, to update the RTO
36+
* value.
3637
*/
37-
virtual void ObserveRtt(uint64_t rtt) = 0;
38+
virtual void ObserveRttMs(uint64_t rttMs) = 0;
3839

3940
/**
4041
* Returns the Retransmission Timeout (RTO) value.

worker/include/RTC/SCTP/association/TransmissionControlBlock.hpp

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
#include "RTC/SCTP/association/HeartbeatHandler.hpp"
66
#include "RTC/SCTP/association/NegotiatedCapabilities.hpp"
77
#include "RTC/SCTP/association/PacketSender.hpp"
8+
#include "RTC/SCTP/association/StreamResetHandler.hpp"
89
#include "RTC/SCTP/association/TCBContext.hpp"
910
#include "RTC/SCTP/packet/Packet.hpp"
1011
#include "RTC/SCTP/public/AssociationListener.hpp"
1112
#include "RTC/SCTP/public/SctpOptions.hpp"
1213
#include "RTC/SCTP/tx/RetransmissionErrorCounter.hpp"
14+
#include "RTC/SCTP/tx/RetransmissionQueue.hpp"
1315
#include "RTC/SCTP/tx/RetransmissionTimeout.hpp"
1416
#include "handles/BackoffTimerHandle.hpp"
1517
#include <string_view>
@@ -25,12 +27,16 @@ namespace RTC
2527
*
2628
* @see https://datatracker.ietf.org/doc/html/rfc9260#section-14
2729
*/
28-
class TransmissionControlBlock : public TCBContext, public BackoffTimerHandle::Listener
30+
class TransmissionControlBlock : public TCBContext,
31+
public RetransmissionQueue::Listener,
32+
public BackoffTimerHandle::Listener
2933
{
3034
public:
3135
TransmissionControlBlock(
3236
AssociationListener& associationListener,
3337
const SctpOptions& sctpOptions,
38+
// TODO: SCTP: Implement it.
39+
// SendQueue& sendQueue,
3440
PacketSender& packetSender,
3541
uint32_t localVerificationTag,
3642
uint32_t remoteVerificationTag,
@@ -128,7 +134,12 @@ namespace RTC
128134
* @remarks
129135
* - Implements TCBContext interface.
130136
*/
131-
void ObserveRtt(uint64_t rtt) override;
137+
void ObserveRttMs(uint64_t rttMs) override;
138+
139+
size_t GetCwnd() const
140+
{
141+
return this->retransmissionQueue.GetCwnd();
142+
}
132143

133144
/**
134145
* @remarks
@@ -158,22 +169,68 @@ namespace RTC
158169
*/
159170
void Send(Packet* packet) override;
160171

172+
// TODO: SCTP: Implement it.
173+
// DataTracker& GetDataTracker()
174+
// {
175+
// return this->dataTracker;
176+
// }
177+
178+
// TODO: SCTP: Implement it.
179+
// ReassemblyQueue& GetReassemblyQueue()
180+
// {
181+
// return this->reassemblyQueue;
182+
// }
183+
184+
RetransmissionQueue& GetRetransmissionQueue()
185+
{
186+
return this->retransmissionQueue;
187+
}
188+
189+
StreamResetHandler& GetStreamResetHandler()
190+
{
191+
return this->streamResetHandler;
192+
}
193+
161194
HeartbeatHandler& GetHeartbeatHandler()
162195
{
163196
return this->heartbeatHandler;
164197
}
165198

199+
/**
200+
* Will be set while the Association is in COOKIE_ECHOED state. In this
201+
* state, there can only be a single Packet outstanding, and it must
202+
* contain the COOKIE_ECHO Chunk as the first Chunk in that Packet, until
203+
* the COOKIE_ACK has been received, which will make the socket call
204+
* `ClearRemoteStateCookie()`.
205+
*/
166206
void SetRemoteStateCookie(std::vector<uint8_t> remoteStateCookie);
167207

208+
/**
209+
* Called when the COOKIE_ACK Chunk has been received, to allow further
210+
* Packets to be sent.
211+
*/
168212
void ClearRemoteStateCookie();
169213

170214
bool HasRemoteStateCookie() const
171215
{
172216
return this->remoteStateCookie.has_value();
173217
}
174218

219+
/**
220+
* Sends a SACK Chunk, if there is a need to.
221+
*/
175222
void MaySendSackChunk();
176223

224+
/**
225+
* Sends a FORWARD-TSN or I-FORWARD-TSN Chunk if it is needed and allowed
226+
* (rate-limited).
227+
*/
228+
void MaybeSendForwardTsnChunk(Packet* packet, uint64_t nowMs);
229+
230+
void MaySendFastRetransmit();
231+
232+
// TODO: SCTP: Mamy more methods.
233+
177234
/**
178235
* @remarks
179236
* - Implements TCBContext interface.
@@ -206,6 +263,12 @@ namespace RTC
206263

207264
void OnDelayedAckTimer(uint64_t& baseTimeoutMs, bool& stop);
208265

266+
/* Pure virtual methods inherited from RetransmissionQueue::Listener. */
267+
public:
268+
void OnRetransmissionQueueNewRttMs(uint64_t newRttMs) override;
269+
void OnRetransmissionQueueClearRetransmissionCounter() override;
270+
;
271+
209272
/* Pure virtual methods inherited from BackoffTimerHandle::Listener. */
210273
public:
211274
void OnTimer(BackoffTimerHandle* backoffTimer, uint64_t& baseTimeoutMs, bool& stop) override;
@@ -230,11 +293,18 @@ namespace RTC
230293
const std::unique_ptr<BackoffTimerHandle> delayedAckTimer;
231294
RetransmissionTimeout rto;
232295
RetransmissionErrorCounter txErrorCounter;
296+
// TODO: SCTP: Implement.
297+
// DataTracker dataTracker;
298+
// TODO: SCTP: Implement.
299+
// ReassemblyQueue reassemblyQueue;
300+
// TODO: SCTP: Implement.
301+
RetransmissionQueue retransmissionQueue;
302+
StreamResetHandler streamResetHandler;
233303
HeartbeatHandler heartbeatHandler;
234304
// Rate limiting of FORWARD_TSN. Next can be sent at or after this
235305
// timestamp.
236306
// TODO: SCTP: Uncomment.
237-
// uint64_t limitForwardTsnUntilMs{ 0 };
307+
uint64_t limitForwardTsnUntilMs{ 0 };
238308
// Only valid when state is State::COOKIE_ECHOED. In this state, the
239309
// Association must wait for COOKIE_ACK to continue sending any packets (not
240310
// including a COOKIE_ECHO). So if this state cookie is present, the

worker/include/RTC/SCTP/packet/UserData.hpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define MS_RTC_SCTP_USER_DATA_HPP
33

44
#include "common.hpp"
5+
#include <ostream>
56
#include <vector>
67

78
namespace RTC
@@ -39,6 +40,15 @@ namespace RTC
3940
// Disable copy assignment.
4041
UserData& operator=(const UserData&) = delete;
4142

43+
bool operator==(const UserData& other) const
44+
{
45+
return (
46+
this->streamId == other.streamId && this->ssn == other.ssn && this->mid == other.mid &&
47+
this->fsn == other.fsn && this->ppid == other.ppid && this->payload == other.payload &&
48+
this->isBeginning == other.isBeginning && this->isEnd == other.isEnd &&
49+
this->isUnordered == other.isUnordered);
50+
}
51+
4252
~UserData();
4353

4454
public:
@@ -91,6 +101,20 @@ namespace RTC
91101
return this->payload.size();
92102
}
93103

104+
UserData Clone() const
105+
{
106+
return UserData(
107+
this->streamId,
108+
this->ssn,
109+
this->mid,
110+
this->fsn,
111+
this->ppid,
112+
this->payload,
113+
this->isBeginning,
114+
this->isEnd,
115+
this->isUnordered);
116+
}
117+
94118
/**
95119
* Useful to extract the payload and its ownership when destructing the
96120
* Message.
@@ -135,6 +159,18 @@ namespace RTC
135159
bool isEnd{ false };
136160
bool isUnordered{ false };
137161
};
162+
163+
/**
164+
* For Catch2 to print it nicely.
165+
*/
166+
inline std::ostream& operator<<(std::ostream& os, const UserData& d)
167+
{
168+
return os << "{streamId:" << d.GetStreamId() << ", ssn:" << d.GetStreamSequenceNumber()
169+
<< ", mid:" << d.GetMessageId() << ", fsn:" << d.GetFragmentSequenceNumber()
170+
<< ", ppid:" << d.GetPayloadProtocolId() << ", payloadLen:" << d.GetPayloadLength()
171+
<< ", B:" << d.IsBeginning() << ", E:" << d.IsEnd() << ", U:" << d.IsUnordered()
172+
<< "}";
173+
}
138174
} // namespace SCTP
139175
} // namespace RTC
140176

worker/include/RTC/SCTP/packet/chunks/ForwardTsnChunk.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ namespace RTC
114114

115115
std::vector<AnyForwardTsnChunk::SkippedStream> GetSkippedStreams() const final;
116116

117-
void AddStream(uint16_t stream, uint16_t streamSequence);
117+
void AddStream(uint16_t streamId, uint16_t streamSequence);
118118

119119
protected:
120120
ForwardTsnChunk* SoftClone(const uint8_t* buffer) const final;

worker/include/RTC/SCTP/packet/chunks/IForwardTsnChunk.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ namespace RTC
126126

127127
std::vector<AnyForwardTsnChunk::SkippedStream> GetSkippedStreams() const final;
128128

129-
void AddStream(uint16_t stream, bool uFlag, uint32_t messageIdentifier);
129+
void AddStream(uint16_t streamId, bool uFlag, uint32_t messageIdentifier);
130130

131131
protected:
132132
IForwardTsnChunk* SoftClone(const uint8_t* buffer) const final;

worker/include/RTC/SCTP/public/SctpOptions.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define MS_RTC_SCTP_OPTIONS_HPP
33

44
#include "common.hpp"
5+
#include "Utils.hpp"
56
#include "RTC/Consts.hpp"
67
#include "RTC/SCTP/packet/parameters/ZeroChecksumAcceptableParameter.hpp"
78

@@ -44,7 +45,7 @@ namespace RTC
4445
* Maximum size of an SCTP Packet. It doesn't include any overhead of
4546
* DTLS, TURN, UDP or IP headers.
4647
*/
47-
size_t mtu{ RTC::Consts::MaxSafeMtuSizeForSctp };
48+
size_t mtu{ Utils::Byte::PadDownTo4Bytes(RTC::Consts::MaxSafeMtuSizeForSctp) };
4849

4950
/**
5051
* The largest allowed message payload to be sent. Messages will be rejected

0 commit comments

Comments
 (0)