mediasoup 3.20.3 → 3.20.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/worker/include/RTC/SCTP/association/Association.hpp +16 -2
- package/worker/include/RTC/SCTP/association/TransmissionControlBlock.hpp +16 -1
- package/worker/include/RTC/SCTP/association/TransmissionControlBlockContextInterface.hpp +19 -2
- package/worker/include/RTC/SCTP/public/SctpTypes.hpp +2 -0
- package/worker/include/RTC/SCTP/rx/ReassemblyQueue.hpp +7 -0
- package/worker/include/RTC/SCTP/tx/RetransmissionErrorCounter.hpp +3 -4
- package/worker/include/RTC/SeqManager.hpp +2 -2
- package/worker/meson.build +3 -0
- package/worker/mocks/include/RTC/SCTP/association/MockAssociationListener.hpp +68 -7
- package/worker/mocks/include/handles/MockBackoffTimerHandle.hpp +10 -4
- package/worker/mocks/src/handles/MockBackoffTimerHandle.cpp +7 -0
- package/worker/src/RTC/SCTP/association/Association.cpp +56 -14
- package/worker/src/RTC/SCTP/association/HeartbeatHandler.cpp +33 -13
- package/worker/src/RTC/SCTP/association/StreamResetHandler.cpp +19 -11
- package/worker/src/RTC/SCTP/association/TransmissionControlBlock.cpp +34 -11
- package/worker/src/RTC/SCTP/rx/DataTracker.cpp +4 -1
- package/worker/src/RTC/SCTP/rx/ReassemblyQueue.cpp +17 -8
- package/worker/src/RTC/SeqManager.cpp +42 -29
- package/worker/src/RTC/Transport.cpp +1 -1
- package/worker/test/src/RTC/SCTP/association/TestAssociation.cpp +1755 -0
- package/worker/test/src/RTC/SCTP/association/TestPacketSender.cpp +121 -0
- package/worker/test/src/RTC/SCTP/association/TestStreamResetHandler.cpp +369 -0
- package/worker/test/src/RTC/SCTP/association/TestTransmissionControlBlock.cpp +11 -0
- package/worker/test/src/RTC/SCTP/rx/TestReassemblyQueue.cpp +24 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mediasoup",
|
|
3
|
-
"version": "3.20.
|
|
3
|
+
"version": "3.20.5",
|
|
4
4
|
"description": "Cutting Edge WebRTC Video Conferencing",
|
|
5
5
|
"contributors": [
|
|
6
6
|
"Iñaki Baz Castillo <ibc@aliax.net> (https://inakibaz.me)",
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
"globals": "^17.6.0",
|
|
123
123
|
"ini": "^6.0.0",
|
|
124
124
|
"jest": "^30.4.2",
|
|
125
|
-
"knip": "^6.
|
|
125
|
+
"knip": "^6.16.1",
|
|
126
126
|
"marked": "^18.0.5",
|
|
127
127
|
"open-cli": "^9.0.0",
|
|
128
128
|
"pick-port": "^2.2.0",
|
|
@@ -53,7 +53,8 @@ namespace RTC
|
|
|
53
53
|
*/
|
|
54
54
|
class Association : public AssociationInterface,
|
|
55
55
|
public PacketSender::Listener,
|
|
56
|
-
public BackoffTimerHandleInterface::Listener
|
|
56
|
+
public BackoffTimerHandleInterface::Listener,
|
|
57
|
+
public TransmissionControlBlockContextInterface::Listener
|
|
57
58
|
{
|
|
58
59
|
public:
|
|
59
60
|
/**
|
|
@@ -172,7 +173,13 @@ namespace RTC
|
|
|
172
173
|
const SctpOptions& sctpOptions,
|
|
173
174
|
AssociationListenerInterface* listener,
|
|
174
175
|
SharedInterface* shared,
|
|
175
|
-
bool isDataChannel
|
|
176
|
+
bool isDataChannel,
|
|
177
|
+
// Whether `MayConnect()` should be called when SCTP data is received.
|
|
178
|
+
// This is always true in production (the association auto-initiates the
|
|
179
|
+
// connection as soon as the transport is ready or data arrives). It's
|
|
180
|
+
// only set to false in tests that need a purely passive peer to mimic
|
|
181
|
+
// dcsctp's asymmetric handshake.
|
|
182
|
+
bool mayConnectOnReceivedSctpData);
|
|
176
183
|
|
|
177
184
|
~Association() override;
|
|
178
185
|
|
|
@@ -494,6 +501,10 @@ namespace RTC
|
|
|
494
501
|
void OnBackoffTimer(
|
|
495
502
|
BackoffTimerHandleInterface* backoffTimer, uint64_t& baseTimeoutMs, bool& stop) override;
|
|
496
503
|
|
|
504
|
+
/* Pure virtual methods inherited from TransmissionControlBlockContextInterface::Listener. */
|
|
505
|
+
public:
|
|
506
|
+
void OnTransmissionControlBlockTooManyTxErrors() override;
|
|
507
|
+
|
|
497
508
|
private:
|
|
498
509
|
// SCTP options given in the constructor.
|
|
499
510
|
SctpOptions sctpOptions;
|
|
@@ -526,6 +537,9 @@ namespace RTC
|
|
|
526
537
|
const size_t maxPacketLength;
|
|
527
538
|
// Whether this is DataChannel based SCTP.
|
|
528
539
|
bool isDataChannel;
|
|
540
|
+
// Whether `MayConnect()` should be called when SCTP data is received.
|
|
541
|
+
// See the constructor for details.
|
|
542
|
+
bool mayConnectOnReceivedSctpData;
|
|
529
543
|
};
|
|
530
544
|
} // namespace SCTP
|
|
531
545
|
} // namespace RTC
|
|
@@ -37,6 +37,7 @@ namespace RTC
|
|
|
37
37
|
{
|
|
38
38
|
public:
|
|
39
39
|
TransmissionControlBlock(
|
|
40
|
+
TransmissionControlBlockContextInterface::Listener* listener,
|
|
40
41
|
AssociationListenerDeferrer& associationListenerDeferrer,
|
|
41
42
|
const SctpOptions& sctpOptions,
|
|
42
43
|
SharedInterface* shared,
|
|
@@ -250,7 +251,20 @@ namespace RTC
|
|
|
250
251
|
*/
|
|
251
252
|
bool IncrementTxErrorCounter(std::string_view reason) override
|
|
252
253
|
{
|
|
253
|
-
|
|
254
|
+
const bool withinLimit = this->txErrorCounter.Increment(reason);
|
|
255
|
+
|
|
256
|
+
if (!withinLimit)
|
|
257
|
+
{
|
|
258
|
+
// NOTE: This closes (and destroys) this TCB synchronously. It's safe to
|
|
259
|
+
// do so from within a timer handler because the handler sets the
|
|
260
|
+
// BackoffTimerHandle `stop` flag and doesn't touch any member
|
|
261
|
+
// afterwards, so the (now destroyed) firing timer won't be accessed.
|
|
262
|
+
this->listener->OnTransmissionControlBlockTooManyTxErrors();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// NOTE: `withinLimit` is a local, so this is safe even if `this` was
|
|
266
|
+
// destroyed above.
|
|
267
|
+
return withinLimit;
|
|
254
268
|
}
|
|
255
269
|
|
|
256
270
|
/**
|
|
@@ -288,6 +302,7 @@ namespace RTC
|
|
|
288
302
|
BackoffTimerHandleInterface* backoffTimer, uint64_t& baseTimeoutMs, bool& stop) override;
|
|
289
303
|
|
|
290
304
|
private:
|
|
305
|
+
TransmissionControlBlockContextInterface::Listener* listener;
|
|
291
306
|
AssociationListenerDeferrer& associationListenerDeferrer;
|
|
292
307
|
const SctpOptions sctpOptions;
|
|
293
308
|
SharedInterface* shared;
|
|
@@ -11,6 +11,23 @@ namespace RTC
|
|
|
11
11
|
{
|
|
12
12
|
class TransmissionControlBlockContextInterface
|
|
13
13
|
{
|
|
14
|
+
public:
|
|
15
|
+
class Listener
|
|
16
|
+
{
|
|
17
|
+
public:
|
|
18
|
+
virtual ~Listener() = default;
|
|
19
|
+
|
|
20
|
+
public:
|
|
21
|
+
/**
|
|
22
|
+
* Called when the transmission error counter has exceeded its limit and
|
|
23
|
+
* the association must therefore be closed.
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* - It mirrors dcsctp's `CloseConnectionBecauseOfTooManyTransmissionErrors()`.
|
|
27
|
+
*/
|
|
28
|
+
virtual void OnTransmissionControlBlockTooManyTxErrors() = 0;
|
|
29
|
+
};
|
|
30
|
+
|
|
14
31
|
public:
|
|
15
32
|
virtual ~TransmissionControlBlockContextInterface() = default;
|
|
16
33
|
|
|
@@ -44,8 +61,8 @@ namespace RTC
|
|
|
44
61
|
|
|
45
62
|
/**
|
|
46
63
|
* Increments the transmission error counter, given a human readable
|
|
47
|
-
* reason. Returns `
|
|
48
|
-
* `
|
|
64
|
+
* reason. Returns `false` if the maximum error count has been reached,
|
|
65
|
+
* `true` otherwise.
|
|
49
66
|
*/
|
|
50
67
|
virtual bool IncrementTxErrorCounter(std::string_view reason) = 0;
|
|
51
68
|
|
|
@@ -178,6 +178,13 @@ namespace RTC
|
|
|
178
178
|
ReassemblyStreamsInterface::OnAssembledMessage onAssembledMessage,
|
|
179
179
|
bool useMessageInterleaving);
|
|
180
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Treat Forward-TSN message as payload. size is calculated based on wire
|
|
183
|
+
* size rather than used memory size: 32bit for TSN + 16+16 bits for each
|
|
184
|
+
* skipped stream entry.
|
|
185
|
+
*/
|
|
186
|
+
size_t ForwardTsnCost(size_t numStreams);
|
|
187
|
+
|
|
181
188
|
void AddReassembledMessage(std::span<const Types::UnwrappedTsn> tsns, Message message);
|
|
182
189
|
|
|
183
190
|
void AssertIsConsistent() const;
|
|
@@ -18,7 +18,7 @@ namespace RTC
|
|
|
18
18
|
class RetransmissionErrorCounter
|
|
19
19
|
{
|
|
20
20
|
public:
|
|
21
|
-
RetransmissionErrorCounter(const SctpOptions& sctpOptions);
|
|
21
|
+
explicit RetransmissionErrorCounter(const SctpOptions& sctpOptions);
|
|
22
22
|
|
|
23
23
|
~RetransmissionErrorCounter();
|
|
24
24
|
|
|
@@ -26,14 +26,13 @@ namespace RTC
|
|
|
26
26
|
void Dump(int indentation = 0) const;
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
* Increments the retransmission timer. Returns `
|
|
30
|
-
* error count has been reached, `
|
|
29
|
+
* Increments the retransmission timer. Returns `false` if the maximum
|
|
30
|
+
* error count has been reached, `true` otherwise.
|
|
31
31
|
*/
|
|
32
32
|
bool Increment(std::string_view reason);
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Whether maximum error count has been reached.
|
|
36
|
-
* @return [description]
|
|
37
36
|
*/
|
|
38
37
|
bool IsExhausted() const
|
|
39
38
|
{
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
#include "common.hpp"
|
|
5
5
|
#include <limits> // std::numeric_limits
|
|
6
|
-
#include <
|
|
6
|
+
#include <vector>
|
|
7
7
|
|
|
8
8
|
namespace RTC
|
|
9
9
|
{
|
|
@@ -53,7 +53,7 @@ namespace RTC
|
|
|
53
53
|
T maxInput{ 0 };
|
|
54
54
|
T maxDropped{ 0 };
|
|
55
55
|
T maxForwarded{ 0 };
|
|
56
|
-
std::
|
|
56
|
+
std::vector<T> dropped;
|
|
57
57
|
};
|
|
58
58
|
} // namespace RTC
|
|
59
59
|
|
package/worker/meson.build
CHANGED
|
@@ -494,9 +494,12 @@ test_sources = [
|
|
|
494
494
|
'test/src/RTC/RTCP/TestPacket.cpp',
|
|
495
495
|
'test/src/RTC/RTCP/TestXr.cpp',
|
|
496
496
|
'test/src/RTC/SCTP/sctpCommon.cpp',
|
|
497
|
+
'test/src/RTC/SCTP/association/TestAssociation.cpp',
|
|
497
498
|
'test/src/RTC/SCTP/association/TestHeartbeatHandler.cpp',
|
|
498
499
|
'test/src/RTC/SCTP/association/TestNegotiatedCapabilities.cpp',
|
|
500
|
+
'test/src/RTC/SCTP/association/TestPacketSender.cpp',
|
|
499
501
|
'test/src/RTC/SCTP/association/TestStateCookie.cpp',
|
|
502
|
+
'test/src/RTC/SCTP/association/TestStreamResetHandler.cpp',
|
|
500
503
|
'test/src/RTC/SCTP/association/TestTransmissionControlBlock.cpp',
|
|
501
504
|
'test/src/RTC/SCTP/rx/TestDataTracker.cpp',
|
|
502
505
|
'test/src/RTC/SCTP/rx/TestInterleavedReassemblyStreams.cpp',
|
|
@@ -21,7 +21,7 @@ namespace mocks
|
|
|
21
21
|
{
|
|
22
22
|
this->sentPackets.emplace_back(data, data + len);
|
|
23
23
|
|
|
24
|
-
return
|
|
24
|
+
return this->sendDataResult;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
void OnAssociationConnecting() override
|
|
@@ -75,20 +75,35 @@ namespace mocks
|
|
|
75
75
|
this->receivedMessages.emplace_back(std::move(message));
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
void OnAssociationStreamsResetPerformed(std::span<const uint16_t>
|
|
78
|
+
void OnAssociationStreamsResetPerformed(std::span<const uint16_t> outboundStreamIds) override
|
|
79
79
|
{
|
|
80
|
-
|
|
80
|
+
++this->onStreamsResetPerformedCalls;
|
|
81
|
+
|
|
82
|
+
for (const auto streamId : outboundStreamIds)
|
|
83
|
+
{
|
|
84
|
+
this->streamsResetPerformed.insert(streamId);
|
|
85
|
+
}
|
|
81
86
|
}
|
|
82
87
|
|
|
83
88
|
void OnAssociationStreamsResetFailed(
|
|
84
|
-
std::span<const uint16_t>
|
|
89
|
+
std::span<const uint16_t> outboundStreamIds, std::string_view /*errorMessage*/) override
|
|
85
90
|
{
|
|
86
|
-
|
|
91
|
+
++this->onStreamsResetFailedCalls;
|
|
92
|
+
|
|
93
|
+
for (const auto streamId : outboundStreamIds)
|
|
94
|
+
{
|
|
95
|
+
this->streamsResetFailed.insert(streamId);
|
|
96
|
+
}
|
|
87
97
|
}
|
|
88
98
|
|
|
89
|
-
void OnAssociationInboundStreamsReset(std::span<const uint16_t>
|
|
99
|
+
void OnAssociationInboundStreamsReset(std::span<const uint16_t> inboundStreamIds) override
|
|
90
100
|
{
|
|
91
|
-
|
|
101
|
+
++this->onInboundStreamsResetCalls;
|
|
102
|
+
|
|
103
|
+
for (const auto streamId : inboundStreamIds)
|
|
104
|
+
{
|
|
105
|
+
this->inboundStreamsReset.insert(streamId);
|
|
106
|
+
}
|
|
92
107
|
}
|
|
93
108
|
|
|
94
109
|
void OnAssociationStreamBufferedAmountLow(uint16_t streamId) override
|
|
@@ -208,6 +223,45 @@ namespace mocks
|
|
|
208
223
|
return this->onTotalBufferedAmountLowCalls;
|
|
209
224
|
}
|
|
210
225
|
|
|
226
|
+
size_t CountOnStreamsResetPerformedCalls() const
|
|
227
|
+
{
|
|
228
|
+
return this->onStreamsResetPerformedCalls;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
bool HasStreamsResetPerformedForStreamId(uint16_t streamId) const
|
|
232
|
+
{
|
|
233
|
+
return this->streamsResetPerformed.contains(streamId);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
size_t CountOnStreamsResetFailedCalls() const
|
|
237
|
+
{
|
|
238
|
+
return this->onStreamsResetFailedCalls;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
bool HasStreamsResetFailedForStreamId(uint16_t streamId) const
|
|
242
|
+
{
|
|
243
|
+
return this->streamsResetFailed.contains(streamId);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
size_t CountOnInboundStreamsResetCalls() const
|
|
247
|
+
{
|
|
248
|
+
return this->onInboundStreamsResetCalls;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
bool HasInboundStreamsResetForStreamId(uint16_t streamId) const
|
|
252
|
+
{
|
|
253
|
+
return this->inboundStreamsReset.contains(streamId);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Sets the value that `OnAssociationSendData()` will return, allowing
|
|
258
|
+
* tests to simulate a failed send.
|
|
259
|
+
*/
|
|
260
|
+
void SetSendDataResult(bool sendDataResult)
|
|
261
|
+
{
|
|
262
|
+
this->sendDataResult = sendDataResult;
|
|
263
|
+
}
|
|
264
|
+
|
|
211
265
|
bool HasSentPackets() const
|
|
212
266
|
{
|
|
213
267
|
return !this->sentPackets.empty();
|
|
@@ -293,6 +347,13 @@ namespace mocks
|
|
|
293
347
|
std::string erroredErrorMessage;
|
|
294
348
|
std::map<uint16_t /*streamId*/, size_t /*count*/> onStreamBufferedAmountLowCalls;
|
|
295
349
|
size_t onTotalBufferedAmountLowCalls{ 0 };
|
|
350
|
+
size_t onStreamsResetPerformedCalls{ 0 };
|
|
351
|
+
std::set<uint16_t /*streamId*/> streamsResetPerformed;
|
|
352
|
+
size_t onStreamsResetFailedCalls{ 0 };
|
|
353
|
+
std::set<uint16_t /*streamId*/> streamsResetFailed;
|
|
354
|
+
size_t onInboundStreamsResetCalls{ 0 };
|
|
355
|
+
std::set<uint16_t /*streamId*/> inboundStreamsReset;
|
|
356
|
+
bool sendDataResult{ true };
|
|
296
357
|
std::deque<std::vector<uint8_t>> sentPackets;
|
|
297
358
|
std::deque<::RTC::SCTP::Message> receivedMessages;
|
|
298
359
|
bool transportReady{ true };
|
|
@@ -36,14 +36,20 @@ namespace mocks
|
|
|
36
36
|
|
|
37
37
|
void Start() override
|
|
38
38
|
{
|
|
39
|
-
this->running
|
|
40
|
-
|
|
39
|
+
this->running = true;
|
|
40
|
+
// NOTE: Reset the expiration count, just like the real BackoffTimerHandle
|
|
41
|
+
// does, so that the backoff starts over from the base timeout.
|
|
42
|
+
this->expirationCount = 0;
|
|
43
|
+
this->expiresAtMs = this->getTimeMs() + ComputeNextTimeoutMs();
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
void Stop() override
|
|
44
47
|
{
|
|
45
|
-
this->running
|
|
46
|
-
|
|
48
|
+
this->running = false;
|
|
49
|
+
// NOTE: Reset the expiration count, just like the real BackoffTimerHandle
|
|
50
|
+
// does.
|
|
51
|
+
this->expirationCount = 0;
|
|
52
|
+
this->expiresAtMs = std::numeric_limits<uint64_t>::max();
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
/**
|
|
@@ -123,5 +123,12 @@ namespace mocks
|
|
|
123
123
|
{
|
|
124
124
|
this->expiresAtMs = this->getTimeMs() + ComputeNextTimeoutMs();
|
|
125
125
|
}
|
|
126
|
+
// Once the timer is no longer running (e.g. max restarts reached), the real
|
|
127
|
+
// BackoffTimerHandle doesn't restart the underlying timer, so it won't fire
|
|
128
|
+
// again. Mirror that here so a stopped timer doesn't keep expiring.
|
|
129
|
+
else
|
|
130
|
+
{
|
|
131
|
+
this->expiresAtMs = std::numeric_limits<uint64_t>::max();
|
|
132
|
+
}
|
|
126
133
|
}
|
|
127
134
|
} // namespace mocks
|
|
@@ -40,7 +40,8 @@ namespace RTC
|
|
|
40
40
|
const SctpOptions& sctpOptions,
|
|
41
41
|
AssociationListenerInterface* listener,
|
|
42
42
|
SharedInterface* shared,
|
|
43
|
-
bool isDataChannel
|
|
43
|
+
bool isDataChannel,
|
|
44
|
+
bool mayConnectOnReceivedSctpData)
|
|
44
45
|
: sctpOptions(sctpOptions),
|
|
45
46
|
// Our `listener` member is a `AssociationListenerDeferrer` which takes
|
|
46
47
|
// `listener` argument as constructor argument.
|
|
@@ -78,7 +79,8 @@ namespace RTC
|
|
|
78
79
|
.maxBackoffTimeoutMs = sctpOptions.timerMaxBackoffTimeoutMs,
|
|
79
80
|
.maxRestarts = sctpOptions.maxRetransmissions })),
|
|
80
81
|
maxPacketLength(Utils::Byte::PadDownTo4Bytes(this->sctpOptions.mtu)),
|
|
81
|
-
isDataChannel(isDataChannel)
|
|
82
|
+
isDataChannel(isDataChannel),
|
|
83
|
+
mayConnectOnReceivedSctpData(mayConnectOnReceivedSctpData)
|
|
82
84
|
{
|
|
83
85
|
MS_TRACE();
|
|
84
86
|
}
|
|
@@ -542,7 +544,13 @@ namespace RTC
|
|
|
542
544
|
|
|
543
545
|
// If we are received SCTP data from the remote peer it means that we may
|
|
544
546
|
// initiate the SCTP association (if not already connected).
|
|
545
|
-
|
|
547
|
+
//
|
|
548
|
+
// NOTE: This is disabled in tests that need a purely passive peer to
|
|
549
|
+
// mimic dcsctp's asymmetric handshake.
|
|
550
|
+
if (this->mayConnectOnReceivedSctpData)
|
|
551
|
+
{
|
|
552
|
+
MayConnect();
|
|
553
|
+
}
|
|
546
554
|
|
|
547
555
|
// NOTE: It's important to create the deferrer here, otherwise it may
|
|
548
556
|
// happen that MayConnect() ends calling to Connect() so we end with two
|
|
@@ -749,6 +757,7 @@ namespace RTC
|
|
|
749
757
|
MS_TRACE();
|
|
750
758
|
|
|
751
759
|
this->tcb = std::make_unique<TransmissionControlBlock>(
|
|
760
|
+
this,
|
|
752
761
|
this->associationListenerDeferrer,
|
|
753
762
|
this->sctpOptions,
|
|
754
763
|
this->shared,
|
|
@@ -1453,7 +1462,9 @@ namespace RTC
|
|
|
1453
1462
|
|
|
1454
1463
|
case State::CLOSED:
|
|
1455
1464
|
{
|
|
1456
|
-
MS_WARN_TAG(sctp, "ignoring INIT chunk received in CLOSED state
|
|
1465
|
+
MS_WARN_TAG(sctp, "ignoring INIT chunk received in CLOSED state");
|
|
1466
|
+
|
|
1467
|
+
return;
|
|
1457
1468
|
}
|
|
1458
1469
|
|
|
1459
1470
|
// https://datatracker.ietf.org/doc/html/rfc9260#section-5.2.1
|
|
@@ -1803,7 +1814,7 @@ namespace RTC
|
|
|
1803
1814
|
else if (
|
|
1804
1815
|
receivedPacket->GetVerificationTag() != this->tcb->GetLocalVerificationTag() &&
|
|
1805
1816
|
cookie->GetRemoteVerificationTag() == this->tcb->GetRemoteVerificationTag() &&
|
|
1806
|
-
cookie->GetTieTag() ==
|
|
1817
|
+
cookie->GetTieTag() == 0)
|
|
1807
1818
|
{
|
|
1808
1819
|
MS_DEBUG_DEV("received COOKIE-ECHO indicating a late COOKIE-ECHO, discarding");
|
|
1809
1820
|
|
|
@@ -2425,6 +2436,15 @@ namespace RTC
|
|
|
2425
2436
|
{
|
|
2426
2437
|
MS_TRACE();
|
|
2427
2438
|
|
|
2439
|
+
const auto maxRestarts = this->t1InitTimer->GetMaxRestarts();
|
|
2440
|
+
|
|
2441
|
+
MS_DEBUG_TAG(
|
|
2442
|
+
sctp,
|
|
2443
|
+
"%s timer has expired [expirations:%zu, maxRestarts:%s]",
|
|
2444
|
+
this->t1InitTimer->GetLabel().c_str(),
|
|
2445
|
+
this->t1InitTimer->GetExpirationCount(),
|
|
2446
|
+
maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
|
|
2447
|
+
|
|
2428
2448
|
const AssociationListenerDeferrer::ScopedDeferrer deferrer(this->associationListenerDeferrer);
|
|
2429
2449
|
|
|
2430
2450
|
AssertState(State::COOKIE_WAIT);
|
|
@@ -2445,6 +2465,15 @@ namespace RTC
|
|
|
2445
2465
|
{
|
|
2446
2466
|
MS_TRACE();
|
|
2447
2467
|
|
|
2468
|
+
const auto maxRestarts = this->t1CookieTimer->GetMaxRestarts();
|
|
2469
|
+
|
|
2470
|
+
MS_DEBUG_TAG(
|
|
2471
|
+
sctp,
|
|
2472
|
+
"%s timer has expired [expirations:%zu, maxRestarts:%s]",
|
|
2473
|
+
this->t1CookieTimer->GetLabel().c_str(),
|
|
2474
|
+
this->t1CookieTimer->GetExpirationCount(),
|
|
2475
|
+
maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
|
|
2476
|
+
|
|
2448
2477
|
const AssociationListenerDeferrer::ScopedDeferrer deferrer(this->associationListenerDeferrer);
|
|
2449
2478
|
|
|
2450
2479
|
AssertState(State::COOKIE_ECHOED);
|
|
@@ -2467,6 +2496,15 @@ namespace RTC
|
|
|
2467
2496
|
{
|
|
2468
2497
|
MS_TRACE();
|
|
2469
2498
|
|
|
2499
|
+
const auto maxRestarts = this->t2ShutdownTimer->GetMaxRestarts();
|
|
2500
|
+
|
|
2501
|
+
MS_DEBUG_TAG(
|
|
2502
|
+
sctp,
|
|
2503
|
+
"%s timer has expired [expirations:%zu, maxRestarts:%s]",
|
|
2504
|
+
this->t2ShutdownTimer->GetLabel().c_str(),
|
|
2505
|
+
this->t2ShutdownTimer->GetExpirationCount(),
|
|
2506
|
+
maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
|
|
2507
|
+
|
|
2470
2508
|
AssertState(State::SHUTDOWN_SENT, State::SHUTDOWN_ACK_SENT);
|
|
2471
2509
|
AssertHasTcb();
|
|
2472
2510
|
|
|
@@ -2811,15 +2849,6 @@ namespace RTC
|
|
|
2811
2849
|
{
|
|
2812
2850
|
MS_TRACE();
|
|
2813
2851
|
|
|
2814
|
-
const auto maxRestarts = backoffTimer->GetMaxRestarts();
|
|
2815
|
-
|
|
2816
|
-
MS_DEBUG_TAG(
|
|
2817
|
-
sctp,
|
|
2818
|
-
"%s timer has expired [expìrations:%zu/%s]",
|
|
2819
|
-
backoffTimer->GetLabel().c_str(),
|
|
2820
|
-
backoffTimer->GetExpirationCount(),
|
|
2821
|
-
maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
|
|
2822
|
-
|
|
2823
2852
|
if (backoffTimer == this->t1InitTimer.get())
|
|
2824
2853
|
{
|
|
2825
2854
|
OnT1InitTimer(baseTimeoutMs, stop);
|
|
@@ -2833,5 +2862,18 @@ namespace RTC
|
|
|
2833
2862
|
OnT2ShutdownTimer(baseTimeoutMs, stop);
|
|
2834
2863
|
}
|
|
2835
2864
|
}
|
|
2865
|
+
|
|
2866
|
+
void Association::OnTransmissionControlBlockTooManyTxErrors()
|
|
2867
|
+
{
|
|
2868
|
+
MS_TRACE();
|
|
2869
|
+
|
|
2870
|
+
// NOTE: This is invoked from within a TCB timer handler (t3-rtx, heartbeat
|
|
2871
|
+
// or RE-CONFIG timeout). `InternalClose()` destroys the TCB synchronously,
|
|
2872
|
+
// which is safe because the calling timer handler sets the BackoffTimerHandle
|
|
2873
|
+
// `stop` flag and doesn't touch any member afterwards. `InternalClose()` does
|
|
2874
|
+
// not establish its own deferrer scope, so it relies on the active one set up
|
|
2875
|
+
// by the timer handler.
|
|
2876
|
+
InternalClose(Types::ErrorKind::TOO_MANY_RETRIES, "too many transmission errors");
|
|
2877
|
+
}
|
|
2836
2878
|
} // namespace SCTP
|
|
2837
2879
|
} // namespace RTC
|
|
@@ -34,7 +34,7 @@ namespace RTC
|
|
|
34
34
|
.listener = this,
|
|
35
35
|
.label = "sctp-heartbeat-interval",
|
|
36
36
|
.baseTimeoutMs = sctpOptions.initialRtoMs,
|
|
37
|
-
.backoffAlgorithm = BackoffTimerHandleInterface::BackoffAlgorithm::
|
|
37
|
+
.backoffAlgorithm = BackoffTimerHandleInterface::BackoffAlgorithm::FIXED,
|
|
38
38
|
.maxBackoffTimeoutMs = sctpOptions.timerMaxBackoffTimeoutMs,
|
|
39
39
|
.maxRestarts = std::nullopt })),
|
|
40
40
|
timeoutTimer(this->shared->CreateBackoffTimer(
|
|
@@ -42,7 +42,7 @@ namespace RTC
|
|
|
42
42
|
.listener = this,
|
|
43
43
|
.label = "sctp-heartbeat-timeout",
|
|
44
44
|
.baseTimeoutMs = sctpOptions.initialRtoMs,
|
|
45
|
-
.backoffAlgorithm = BackoffTimerHandleInterface::BackoffAlgorithm::
|
|
45
|
+
.backoffAlgorithm = BackoffTimerHandleInterface::BackoffAlgorithm::EXPONENTIAL,
|
|
46
46
|
.maxBackoffTimeoutMs = std::nullopt,
|
|
47
47
|
.maxRestarts = 0 }))
|
|
48
48
|
{
|
|
@@ -180,6 +180,18 @@ namespace RTC
|
|
|
180
180
|
{
|
|
181
181
|
MS_TRACE();
|
|
182
182
|
|
|
183
|
+
#if MS_LOG_DEV_LEVEL == 3
|
|
184
|
+
const auto maxRestarts = this->intervalTimer->GetMaxRestarts();
|
|
185
|
+
#endif
|
|
186
|
+
|
|
187
|
+
// NOTE: This timer expires periodically on idle connections (forever), so
|
|
188
|
+
// it's logged at dev level to avoid being noisy.
|
|
189
|
+
MS_DEBUG_DEV(
|
|
190
|
+
"%s timer has expired [expirations:%zu, maxRestarts:%s]",
|
|
191
|
+
this->intervalTimer->GetLabel().c_str(),
|
|
192
|
+
this->intervalTimer->GetExpirationCount(),
|
|
193
|
+
maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
|
|
194
|
+
|
|
183
195
|
// This is a top-level timer entry point (invoked by libuv outside any other
|
|
184
196
|
// SCTP API call), so it must establish the deferrer scope itself, just like
|
|
185
197
|
// Association does in its own timer handlers.
|
|
@@ -215,10 +227,19 @@ namespace RTC
|
|
|
215
227
|
this->tcbContext->SendPacket(packet.get());
|
|
216
228
|
}
|
|
217
229
|
|
|
218
|
-
void HeartbeatHandler::OnTimeoutTimer(uint64_t& /*baseTimeoutMs*/, bool&
|
|
230
|
+
void HeartbeatHandler::OnTimeoutTimer(uint64_t& /*baseTimeoutMs*/, bool& stop)
|
|
219
231
|
{
|
|
220
232
|
MS_TRACE();
|
|
221
233
|
|
|
234
|
+
const auto maxRestarts = this->timeoutTimer->GetMaxRestarts();
|
|
235
|
+
|
|
236
|
+
MS_DEBUG_TAG(
|
|
237
|
+
sctp,
|
|
238
|
+
"%s timer has expired [expirations:%zu, maxRestarts:%s]",
|
|
239
|
+
this->timeoutTimer->GetLabel().c_str(),
|
|
240
|
+
this->timeoutTimer->GetExpirationCount(),
|
|
241
|
+
maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
|
|
242
|
+
|
|
222
243
|
// This is a top-level timer entry point (invoked by libuv outside any other
|
|
223
244
|
// SCTP API call), so it must establish the deferrer scope itself, just like
|
|
224
245
|
// Association does in its own timer handlers.
|
|
@@ -228,7 +249,15 @@ namespace RTC
|
|
|
228
249
|
// the interval timer expires.
|
|
229
250
|
MS_ASSERT(!this->timeoutTimer->IsRunning(), "timeout timer shouldn't be running");
|
|
230
251
|
|
|
231
|
-
this->tcbContext->IncrementTxErrorCounter("hearbeat timeout")
|
|
252
|
+
if (!this->tcbContext->IncrementTxErrorCounter("hearbeat timeout"))
|
|
253
|
+
{
|
|
254
|
+
// `IncrementTxErrorCounter()` has closed (and destroyed) the TCB (and
|
|
255
|
+
// hence this HeartbeatHandler and its timers). Signal the firing timer to
|
|
256
|
+
// stop and don't touch any member afterwards.
|
|
257
|
+
stop = true;
|
|
258
|
+
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
232
261
|
}
|
|
233
262
|
|
|
234
263
|
void HeartbeatHandler::OnBackoffTimer(
|
|
@@ -236,15 +265,6 @@ namespace RTC
|
|
|
236
265
|
{
|
|
237
266
|
MS_TRACE();
|
|
238
267
|
|
|
239
|
-
const auto maxRestarts = backoffTimer->GetMaxRestarts();
|
|
240
|
-
|
|
241
|
-
MS_DEBUG_TAG(
|
|
242
|
-
sctp,
|
|
243
|
-
"%s timer has expired [expìrations:%zu/%s]",
|
|
244
|
-
backoffTimer->GetLabel().c_str(),
|
|
245
|
-
backoffTimer->GetExpirationCount(),
|
|
246
|
-
maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
|
|
247
|
-
|
|
248
268
|
if (backoffTimer == this->intervalTimer.get())
|
|
249
269
|
{
|
|
250
270
|
OnIntervalTimer(baseTimeoutMs, stop);
|
|
@@ -376,6 +376,10 @@ namespace RTC
|
|
|
376
376
|
ReconfigurationResponseParameter::Result::SUCCESS_NOTHING_TO_DO);
|
|
377
377
|
|
|
378
378
|
reconfigurationResponseParameter->Consolidate();
|
|
379
|
+
|
|
380
|
+
this->lastProcessedReqSeqNbr = requestSn;
|
|
381
|
+
this->lastProcessedReqResult =
|
|
382
|
+
ReconfigurationResponseParameter::Result::SUCCESS_NOTHING_TO_DO;
|
|
379
383
|
}
|
|
380
384
|
else
|
|
381
385
|
{
|
|
@@ -465,10 +469,19 @@ namespace RTC
|
|
|
465
469
|
}
|
|
466
470
|
}
|
|
467
471
|
|
|
468
|
-
void StreamResetHandler::OnReConfigTimer(uint64_t& baseTimeoutMs, bool&
|
|
472
|
+
void StreamResetHandler::OnReConfigTimer(uint64_t& baseTimeoutMs, bool& stop)
|
|
469
473
|
{
|
|
470
474
|
MS_TRACE();
|
|
471
475
|
|
|
476
|
+
const auto maxRestarts = this->reConfigTimer->GetMaxRestarts();
|
|
477
|
+
|
|
478
|
+
MS_DEBUG_TAG(
|
|
479
|
+
sctp,
|
|
480
|
+
"%s timer has expired [expirations:%zu, maxRestarts:%s]",
|
|
481
|
+
this->reConfigTimer->GetLabel().c_str(),
|
|
482
|
+
this->reConfigTimer->GetExpirationCount(),
|
|
483
|
+
maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
|
|
484
|
+
|
|
472
485
|
// This is a top-level timer entry point (invoked by libuv outside any other
|
|
473
486
|
// SCTP API call), so it must establish the deferrer scope itself, just like
|
|
474
487
|
// Association does in its own timer handlers.
|
|
@@ -486,7 +499,11 @@ namespace RTC
|
|
|
486
499
|
// response.
|
|
487
500
|
else if (!this->tcbContext->IncrementTxErrorCounter("RECONFIG timeout"))
|
|
488
501
|
{
|
|
489
|
-
//
|
|
502
|
+
// `IncrementTxErrorCounter()` has closed (and destroyed) the TCB (and
|
|
503
|
+
// hence this StreamResetHandler and its timer). Signal the firing timer
|
|
504
|
+
// to stop and don't touch any member afterwards.
|
|
505
|
+
stop = true;
|
|
506
|
+
|
|
490
507
|
return;
|
|
491
508
|
}
|
|
492
509
|
}
|
|
@@ -511,15 +528,6 @@ namespace RTC
|
|
|
511
528
|
{
|
|
512
529
|
MS_TRACE();
|
|
513
530
|
|
|
514
|
-
const auto maxRestarts = backoffTimer->GetMaxRestarts();
|
|
515
|
-
|
|
516
|
-
MS_DEBUG_TAG(
|
|
517
|
-
sctp,
|
|
518
|
-
"%s timer has expired [expìrations:%zu/%s]",
|
|
519
|
-
backoffTimer->GetLabel().c_str(),
|
|
520
|
-
backoffTimer->GetExpirationCount(),
|
|
521
|
-
maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
|
|
522
|
-
|
|
523
531
|
if (backoffTimer == this->reConfigTimer.get())
|
|
524
532
|
{
|
|
525
533
|
OnReConfigTimer(baseTimeoutMs, stop);
|