mediasoup 3.19.21 → 3.19.22
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/node/lib/Worker.d.ts +1 -0
- package/node/lib/Worker.d.ts.map +1 -1
- package/node/lib/Worker.js +14 -0
- package/package.json +4 -2
- package/worker/fuzzer/src/RTC/FuzzerDtlsTransport.cpp +9 -3
- package/worker/fuzzer/src/RTC/RTP/FuzzerRtpStreamSend.cpp +9 -1
- package/worker/include/Channel/ChannelMessageRegistrator.hpp +39 -0
- package/worker/include/Channel/ChannelMessageRegistratorInterface.hpp +32 -0
- package/worker/include/Channel/ChannelSocket.hpp +1 -1
- package/worker/include/DepUsrSCTP.hpp +8 -7
- package/worker/include/RTC/ActiveSpeakerObserver.hpp +7 -7
- package/worker/include/RTC/AudioLevelObserver.hpp +7 -7
- package/worker/include/RTC/Consumer.hpp +3 -3
- package/worker/include/RTC/DataConsumer.hpp +3 -3
- package/worker/include/RTC/DataProducer.hpp +3 -3
- package/worker/include/RTC/DirectTransport.hpp +2 -2
- package/worker/include/RTC/DtlsTransport.hpp +8 -6
- package/worker/include/RTC/ICE/IceServer.hpp +8 -5
- package/worker/include/RTC/KeyFrameRequestManager.hpp +15 -12
- package/worker/include/RTC/NackGenerator.hpp +7 -6
- package/worker/include/RTC/PipeConsumer.hpp +1 -2
- package/worker/include/RTC/PipeTransport.hpp +2 -2
- package/worker/include/RTC/PlainTransport.hpp +2 -2
- package/worker/include/RTC/Producer.hpp +3 -3
- package/worker/include/RTC/RTP/RtpStream.hpp +7 -1
- package/worker/include/RTC/RTP/RtpStreamRecv.hpp +6 -5
- package/worker/include/RTC/RTP/RtpStreamSend.hpp +4 -1
- package/worker/include/RTC/Router.hpp +3 -3
- package/worker/include/RTC/RtpObserver.hpp +3 -3
- package/worker/include/RTC/SCTP/TODO_SCTP.md +18 -6
- package/worker/include/RTC/SCTP/association/Association.hpp +11 -8
- package/worker/include/RTC/SCTP/association/HeartbeatHandler.hpp +9 -6
- package/worker/include/RTC/SCTP/association/StreamResetHandler.hpp +37 -23
- package/worker/include/RTC/SCTP/association/TCBContext.hpp +3 -2
- package/worker/include/RTC/SCTP/association/TransmissionControlBlock.hpp +81 -8
- package/worker/include/RTC/SCTP/packet/UserData.hpp +36 -0
- package/worker/include/RTC/SCTP/packet/chunks/ForwardTsnChunk.hpp +1 -1
- package/worker/include/RTC/SCTP/packet/chunks/IForwardTsnChunk.hpp +1 -1
- package/worker/include/RTC/SCTP/public/SctpOptions.hpp +2 -1
- package/worker/include/RTC/SCTP/tx/OutstandingData.hpp +604 -0
- package/worker/include/RTC/SCTP/tx/RetransmissionQueue.hpp +336 -0
- package/worker/include/RTC/SCTP/tx/RetransmissionTimeout.hpp +5 -4
- package/worker/include/RTC/Serializable.hpp +8 -0
- package/worker/include/RTC/SimpleConsumer.hpp +1 -2
- package/worker/include/RTC/SimulcastConsumer.hpp +1 -2
- package/worker/include/RTC/SvcConsumer.hpp +1 -2
- package/worker/include/RTC/Transport.hpp +8 -8
- package/worker/include/RTC/TransportCongestionControlClient.hpp +8 -5
- package/worker/include/RTC/TransportCongestionControlServer.hpp +8 -5
- package/worker/include/RTC/WebRtcServer.hpp +3 -3
- package/worker/include/RTC/WebRtcTransport.hpp +3 -3
- package/worker/include/Shared.hpp +40 -0
- package/worker/include/SharedInterface.hpp +44 -0
- package/worker/include/Utils.hpp +6 -0
- package/worker/include/Worker.hpp +3 -3
- package/worker/include/common.hpp +1 -1
- package/worker/include/handles/BackoffTimerHandle.hpp +27 -65
- package/worker/include/handles/BackoffTimerHandleInterface.hpp +116 -0
- package/worker/include/handles/TimerHandle.hpp +36 -20
- package/worker/include/handles/TimerHandleInterface.hpp +43 -0
- package/worker/meson.build +21 -4
- package/worker/meson_options.txt +2 -1
- package/worker/mocks/include/Channel/MockChannelMessageRegistrator.hpp +45 -0
- package/worker/mocks/include/MockShared.hpp +43 -0
- package/worker/mocks/src/Channel/MockChannelMessageRegistrator.cpp +128 -0
- package/worker/mocks/src/MockShared.cpp +26 -0
- package/worker/scripts/clang-scripts.mjs +4 -1
- package/worker/src/Channel/ChannelMessageRegistrator.cpp +125 -0
- package/worker/src/Channel/ChannelSocket.cpp +1 -1
- package/worker/src/DepUsrSCTP.cpp +10 -4
- package/worker/src/RTC/ActiveSpeakerObserver.cpp +7 -7
- package/worker/src/RTC/AudioLevelObserver.cpp +12 -10
- package/worker/src/RTC/Consumer.cpp +23 -20
- package/worker/src/RTC/DataConsumer.cpp +11 -11
- package/worker/src/RTC/DataProducer.cpp +3 -3
- package/worker/src/RTC/DirectTransport.cpp +16 -16
- package/worker/src/RTC/DtlsTransport.cpp +4 -4
- package/worker/src/RTC/ICE/IceServer.cpp +4 -3
- package/worker/src/RTC/KeyFrameRequestManager.cpp +15 -15
- package/worker/src/RTC/NackGenerator.cpp +3 -3
- package/worker/src/RTC/PipeConsumer.cpp +5 -4
- package/worker/src/RTC/PipeTransport.cpp +3 -3
- package/worker/src/RTC/PlainTransport.cpp +10 -9
- package/worker/src/RTC/Producer.cpp +30 -28
- package/worker/src/RTC/RTCP/FeedbackPsRpsi.cpp +1 -2
- package/worker/src/RTC/RTP/RtpStream.cpp +9 -2
- package/worker/src/RTC/RTP/RtpStreamRecv.cpp +5 -4
- package/worker/src/RTC/RTP/RtpStreamSend.cpp +5 -2
- package/worker/src/RTC/Router.cpp +3 -3
- package/worker/src/RTC/RtpObserver.cpp +2 -1
- package/worker/src/RTC/SCTP/association/Association.cpp +94 -114
- package/worker/src/RTC/SCTP/association/HeartbeatHandler.cpp +27 -21
- package/worker/src/RTC/SCTP/association/StreamResetHandler.cpp +52 -55
- package/worker/src/RTC/SCTP/association/TransmissionControlBlock.cpp +144 -25
- package/worker/src/RTC/SCTP/packet/chunks/ForwardTsnChunk.cpp +2 -2
- package/worker/src/RTC/SCTP/packet/chunks/IForwardTsnChunk.cpp +2 -2
- package/worker/src/RTC/SCTP/tx/OutstandingData.cpp +905 -0
- package/worker/src/RTC/SCTP/tx/RetransmissionQueue.cpp +799 -0
- package/worker/src/RTC/SCTP/tx/RetransmissionTimeout.cpp +1 -1
- package/worker/src/RTC/SctpAssociation.cpp +1 -1
- package/worker/src/RTC/SimpleConsumer.cpp +8 -7
- package/worker/src/RTC/SimulcastConsumer.cpp +11 -10
- package/worker/src/RTC/SvcConsumer.cpp +11 -10
- package/worker/src/RTC/Transport.cpp +36 -26
- package/worker/src/RTC/TransportCongestionControlClient.cpp +4 -2
- package/worker/src/RTC/TransportCongestionControlServer.cpp +4 -3
- package/worker/src/RTC/WebRtcServer.cpp +5 -4
- package/worker/src/RTC/WebRtcTransport.cpp +39 -26
- package/worker/src/Shared.cpp +35 -0
- package/worker/src/Worker.cpp +10 -23
- package/worker/src/handles/BackoffTimerHandle.cpp +11 -16
- package/worker/src/handles/TimerHandle.cpp +5 -4
- package/worker/src/lib.cpp +14 -1
- package/worker/tasks.py +1 -1
- package/worker/test/include/RTC/ICE/iceCommon.hpp +1 -0
- package/worker/test/include/RTC/RTP/rtpCommon.hpp +1 -0
- package/worker/test/include/RTC/SCTP/sctpCommon.hpp +6 -0
- package/worker/test/src/RTC/RTP/TestRtpStreamRecv.cpp +12 -5
- package/worker/test/src/RTC/RTP/TestRtpStreamSend.cpp +34 -23
- package/worker/test/src/RTC/SCTP/tx/TestOutstandingData.cpp +1196 -0
- package/worker/test/src/RTC/SCTP/tx/TestRetransmissionTimeout.cpp +33 -33
- package/worker/test/src/RTC/TestKeyFrameRequestManager.cpp +14 -6
- package/worker/test/src/RTC/TestNackGenerator.cpp +6 -2
- package/worker/test/src/RTC/TestSimpleConsumer.cpp +6 -10
- package/worker/test/src/RTC/TestTransportCongestionControlServer.cpp +9 -2
- package/worker/test/src/Utils/TestByte.cpp +98 -0
- package/worker/include/ChannelMessageRegistrator.hpp +0 -30
- package/worker/include/RTC/Shared.hpp +0 -23
- package/worker/src/ChannelMessageRegistrator.cpp +0 -119
- package/worker/src/RTC/Shared.cpp +0 -23
|
@@ -0,0 +1,799 @@
|
|
|
1
|
+
#define MS_CLASS "RTC::SCTP::RetransmissionQueue"
|
|
2
|
+
// TODO: SCTP: Comment.
|
|
3
|
+
#define MS_LOG_DEV_LEVEL 3
|
|
4
|
+
|
|
5
|
+
#include "RTC/SCTP/tx/RetransmissionQueue.hpp"
|
|
6
|
+
#include "Logger.hpp"
|
|
7
|
+
#include "Utils.hpp"
|
|
8
|
+
#include "RTC/SCTP/packet/chunks/DataChunk.hpp"
|
|
9
|
+
#include "RTC/SCTP/packet/chunks/IDataChunk.hpp"
|
|
10
|
+
#include <cmath> // std::min()
|
|
11
|
+
#include <numeric> // std::accumulate()
|
|
12
|
+
#include <string>
|
|
13
|
+
|
|
14
|
+
namespace RTC
|
|
15
|
+
{
|
|
16
|
+
namespace SCTP
|
|
17
|
+
{
|
|
18
|
+
/* Instance methods. */
|
|
19
|
+
|
|
20
|
+
RetransmissionQueue::RetransmissionQueue(
|
|
21
|
+
Listener* listener,
|
|
22
|
+
AssociationListener& associationListener,
|
|
23
|
+
uint32_t localInitialTsn,
|
|
24
|
+
uint32_t remoteAdvertisedReceiverWindowCredit,
|
|
25
|
+
// TODO: SCTP: Implement
|
|
26
|
+
// SendQueue& sendQueue,
|
|
27
|
+
BackoffTimerHandleInterface* t3RtxTimer,
|
|
28
|
+
const SctpOptions& sctpOptions,
|
|
29
|
+
// NOTE: I don't like default argument values in dcsctp (true and false),
|
|
30
|
+
// let's be explicit.
|
|
31
|
+
bool supportsPartialReliability,
|
|
32
|
+
bool useMessageInterleaving)
|
|
33
|
+
: listener(listener),
|
|
34
|
+
associationListener(associationListener),
|
|
35
|
+
sctpOptions(sctpOptions),
|
|
36
|
+
supportsPartialReliability(supportsPartialReliability),
|
|
37
|
+
dataChunkHeaderLength(
|
|
38
|
+
useMessageInterleaving ? IDataChunk::IDataChunkHeaderLength
|
|
39
|
+
: DataChunk::DataChunkHeaderLength),
|
|
40
|
+
t3RtxTimer(t3RtxTimer),
|
|
41
|
+
cwnd(sctpOptions.initialCwndMtus * sctpOptions.mtu),
|
|
42
|
+
rwnd(remoteAdvertisedReceiverWindowCredit),
|
|
43
|
+
// https://datatracker.ietf.org/doc/html/rfc9260#section-7.2.1
|
|
44
|
+
//
|
|
45
|
+
// "The initial value of ssthresh MAY be arbitrarily high (for example,
|
|
46
|
+
// implementations MAY use the size of the receiver advertised window)."
|
|
47
|
+
ssthresh(this->rwnd),
|
|
48
|
+
// TODO: SCTP: Implement.
|
|
49
|
+
// sendQueue(sendQueue),
|
|
50
|
+
outstandingData(
|
|
51
|
+
this->dataChunkHeaderLength,
|
|
52
|
+
this->tsnUnwrapper.Unwrap(localInitialTsn - 1),
|
|
53
|
+
[/*this*/](uint16_t /*streamId*/, uint32_t /*outgoingMessageId*/)
|
|
54
|
+
{
|
|
55
|
+
// TODO: SCTP: Implement.
|
|
56
|
+
// return this->sendQueue.Discard(streamId, outgoingMessageId);
|
|
57
|
+
|
|
58
|
+
// TODO: SCTP: Remove when the above is uncommented.
|
|
59
|
+
return false;
|
|
60
|
+
})
|
|
61
|
+
{
|
|
62
|
+
MS_TRACE();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
RetransmissionQueue::~RetransmissionQueue()
|
|
66
|
+
{
|
|
67
|
+
MS_TRACE();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
bool RetransmissionQueue::HandleReceivedSackChunk(uint64_t nowMs, const SackChunk* receivedSackChunk)
|
|
71
|
+
{
|
|
72
|
+
MS_TRACE();
|
|
73
|
+
|
|
74
|
+
if (!IsSackChunkValid(receivedSackChunk))
|
|
75
|
+
{
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const UnwrappedTsn oldLastCumulativeTsnAck = this->outstandingData.GetLastCumulativeTsnAck();
|
|
80
|
+
const size_t oldUnackedPacketBytes = this->outstandingData.GetUnackedPacketBytes();
|
|
81
|
+
#if MS_LOG_DEV_LEVEL == 3
|
|
82
|
+
const size_t oldRwnd = this->rwnd;
|
|
83
|
+
#endif
|
|
84
|
+
const UnwrappedTsn cumulativeTsnAck =
|
|
85
|
+
this->tsnUnwrapper.Unwrap(receivedSackChunk->GetCumulativeTsnAck());
|
|
86
|
+
|
|
87
|
+
if (receivedSackChunk->GetValidatedGapAckBlocks().empty())
|
|
88
|
+
{
|
|
89
|
+
UpdateRttMs(nowMs, cumulativeTsnAck);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Exit fast recovery before continuing processing, in case it needs to go
|
|
93
|
+
// into fast recovery again due to new reported packet loss.
|
|
94
|
+
MayExitFastRecovery(cumulativeTsnAck);
|
|
95
|
+
|
|
96
|
+
const OutstandingData::AckInfo ackInfo = this->outstandingData.HandleSack(
|
|
97
|
+
cumulativeTsnAck, receivedSackChunk->GetValidatedGapAckBlocks(), IsInFastRecovery());
|
|
98
|
+
|
|
99
|
+
// Add lifecycle events for delivered messages.
|
|
100
|
+
for (const uint64_t lifecycleId : ackInfo.ackedLifecycleIds)
|
|
101
|
+
{
|
|
102
|
+
MS_DEBUG_TAG(
|
|
103
|
+
sctp,
|
|
104
|
+
"triggering OnAssociationLifecycleMessageDelivered() [lifecycleId:%" PRIu64 "]",
|
|
105
|
+
lifecycleId);
|
|
106
|
+
|
|
107
|
+
this->associationListener.OnAssociationLifecycleMessageDelivered(lifecycleId);
|
|
108
|
+
this->associationListener.OnAssociationLifecycleMessageEnd(lifecycleId);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for (const uint64_t lifecycleId : ackInfo.abandonedLifecycleIds)
|
|
112
|
+
{
|
|
113
|
+
MS_DEBUG_TAG(
|
|
114
|
+
sctp,
|
|
115
|
+
"triggering OnLifecycleMessageExpired() [lifecycleId:%" PRIu64 ", maybeDelivered:true]",
|
|
116
|
+
lifecycleId);
|
|
117
|
+
|
|
118
|
+
this->associationListener.OnAssociationLifecycleMessageExpired(
|
|
119
|
+
lifecycleId, /*maybeDelivered*/ true);
|
|
120
|
+
this->associationListener.OnAssociationLifecycleMessageEnd(lifecycleId);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Update of this->outstandingData is now done. Congestion control remains.
|
|
124
|
+
UpdateReceiverWindow(receivedSackChunk->GetAdvertisedReceiverWindowCredit());
|
|
125
|
+
|
|
126
|
+
MS_DEBUG_DEV(
|
|
127
|
+
"Received SACK [cumulativeTsnAck:%" PRIu32 ", oldLastCumulativeTsnAck:%" PRIu32
|
|
128
|
+
", unackedPacketBytes:%zu, oldUnackedPacketBytes:%zu, rwnd:%zu, oldRwnd:%zu]",
|
|
129
|
+
cumulativeTsnAck.Wrap(),
|
|
130
|
+
oldLastCumulativeTsnAck.Wrap(),
|
|
131
|
+
this->outstandingData.GetUnackedPacketBytes(),
|
|
132
|
+
oldUnackedPacketBytes,
|
|
133
|
+
this->rwnd,
|
|
134
|
+
oldRwnd);
|
|
135
|
+
|
|
136
|
+
if (cumulativeTsnAck > oldLastCumulativeTsnAck)
|
|
137
|
+
{
|
|
138
|
+
// https://datatracker.ietf.org/doc/html/rfc9260#section-6.3.2
|
|
139
|
+
//
|
|
140
|
+
// "Whenever a SACK is received that acknowledges the DATA chunk with
|
|
141
|
+
// the earliest outstanding TSN for that address, restart the T3-rtx
|
|
142
|
+
// timer for that address with its current RTO (if there is still
|
|
143
|
+
// outstanding data on that address)."
|
|
144
|
+
this->t3RtxTimer->Stop();
|
|
145
|
+
|
|
146
|
+
HandleIncreasedCumulativeTsnAck(oldUnackedPacketBytes, ackInfo.bytesAcked);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (ackInfo.hasPacketLoss)
|
|
150
|
+
{
|
|
151
|
+
HandlePacketLoss(ackInfo.highestTsnAcked);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// https://datatracker.ietf.org/doc/html/rfc9260#section-8.2
|
|
155
|
+
//
|
|
156
|
+
// "When an outstanding TSN is acknowledged [...] the endpoint shall clear
|
|
157
|
+
// the error counter ...".
|
|
158
|
+
if (ackInfo.bytesAcked > 0)
|
|
159
|
+
{
|
|
160
|
+
this->listener->OnRetransmissionQueueClearRetransmissionCounter();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
StartT3RtxTimerIfOutstandingData();
|
|
164
|
+
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
void RetransmissionQueue::HandleT3RtxTimerExpiry()
|
|
169
|
+
{
|
|
170
|
+
MS_TRACE();
|
|
171
|
+
|
|
172
|
+
const size_t oldCwnd = this->cwnd;
|
|
173
|
+
const size_t oldUnackedPacketBytes = GetUnackedPacketBytes();
|
|
174
|
+
|
|
175
|
+
// https://datatracker.ietf.org/doc/html/rfc9260#section-6.3.3
|
|
176
|
+
//
|
|
177
|
+
// "For the destination address for which the timer expires, adjust
|
|
178
|
+
// its ssthresh with rules defined in Section 7.2.3 and set the cwnd
|
|
179
|
+
// <- MTU."
|
|
180
|
+
this->ssthresh = std::max(this->cwnd / 2, 4 * this->sctpOptions.mtu);
|
|
181
|
+
this->cwnd = 1 * this->sctpOptions.mtu;
|
|
182
|
+
|
|
183
|
+
// Errata: https://datatracker.ietf.org/doc/html/rfc8540#section-3.11
|
|
184
|
+
this->partialBytesAcked = 0;
|
|
185
|
+
|
|
186
|
+
// https://datatracker.ietf.org/doc/html/rfc9260#section-6.3.3
|
|
187
|
+
//
|
|
188
|
+
// "For the destination address for which the timer expires, set RTO
|
|
189
|
+
// <- RTO * 2 ("back off the timer"). The maximum value discussed in
|
|
190
|
+
// rule C7 above (RTO.max) may be used to provide an upper bound to this
|
|
191
|
+
// doubling operation."
|
|
192
|
+
|
|
193
|
+
// Already done by the BackoffTimerHandle implementation.
|
|
194
|
+
|
|
195
|
+
// https://datatracker.ietf.org/doc/html/rfc9260#section-6.3.3
|
|
196
|
+
//
|
|
197
|
+
// "Determine how many of the earliest (i.e., lowest TSN) outstanding
|
|
198
|
+
// DATA chunks for the address for which the T3-rtx has expired will fit
|
|
199
|
+
// into a single Packet"
|
|
200
|
+
|
|
201
|
+
// https://datatracker.ietf.org/doc/html/rfc9260#section-6.3.3
|
|
202
|
+
//
|
|
203
|
+
// "Note: Any DATA chunks that were sent to the address for which the
|
|
204
|
+
// T3-rtx timer expired but did not fit in one MTU (rule E3 above) should
|
|
205
|
+
// be marked for retransmission and sent as soon as cwnd allows (normally,
|
|
206
|
+
// when a SACK arrives)."
|
|
207
|
+
this->outstandingData.NackAll();
|
|
208
|
+
|
|
209
|
+
// https://datatracker.ietf.org/doc/html/rfc9260#section-6.3.3
|
|
210
|
+
//
|
|
211
|
+
// "Start the retransmission timer T3-rtx on the destination address to
|
|
212
|
+
// which the retransmission is sent, if rule R1 above indicates to do so."
|
|
213
|
+
|
|
214
|
+
// Already done by the BackoffTimerHandle implementation.
|
|
215
|
+
|
|
216
|
+
MS_DEBUG_TAG(
|
|
217
|
+
sctp,
|
|
218
|
+
"T3-rtx timer has expired [cwnd:%zu, oldCwnd:%zu, ssthresh:%zu, unackedPacketBytes:%zu, oldUnackedPacketBytes:%zu]",
|
|
219
|
+
this->cwnd,
|
|
220
|
+
oldCwnd,
|
|
221
|
+
this->ssthresh,
|
|
222
|
+
GetUnackedPacketBytes(),
|
|
223
|
+
oldUnackedPacketBytes);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
std::vector<std::pair<uint32_t /*tsn*/, UserData>> RetransmissionQueue::GetChunksForFastRetransmit(
|
|
227
|
+
size_t maxLength)
|
|
228
|
+
{
|
|
229
|
+
MS_TRACE();
|
|
230
|
+
|
|
231
|
+
MS_ASSERT(
|
|
232
|
+
this->outstandingData.HasDataToBeFastRetransmitted(), "no data to be fast-retransmitted");
|
|
233
|
+
MS_ASSERT(
|
|
234
|
+
Utils::Byte::IsPaddedTo4Bytes(maxLength),
|
|
235
|
+
"given maxLength %zu is not divisible by 4",
|
|
236
|
+
maxLength);
|
|
237
|
+
|
|
238
|
+
std::vector<std::pair<uint32_t /*tsn*/, UserData>> result;
|
|
239
|
+
|
|
240
|
+
#if MS_LOG_DEV_LEVEL == 3
|
|
241
|
+
const size_t oldUnackedPacketBytes = GetUnackedPacketBytes();
|
|
242
|
+
#endif
|
|
243
|
+
|
|
244
|
+
result = this->outstandingData.GetChunksToBeFastRetransmitted(maxLength);
|
|
245
|
+
|
|
246
|
+
MS_ASSERT(!result.empty(), "result cannot be empty");
|
|
247
|
+
|
|
248
|
+
// https://datatracker.ietf.org/doc/html/rfc9260#section-7.2.4
|
|
249
|
+
//
|
|
250
|
+
// "4) Restart the T3-rtx timer only if ... the endpoint is retransmitting
|
|
251
|
+
// the first outstanding DATA chunk sent to that address."
|
|
252
|
+
if (result[0].first == this->outstandingData.GetLastCumulativeTsnAck().GetNextValue().Wrap())
|
|
253
|
+
{
|
|
254
|
+
MS_DEBUG_DEV("first outstanding data to be retransmitted, restarting T3-rtx timer");
|
|
255
|
+
|
|
256
|
+
this->t3RtxTimer->Stop();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// https://datatracker.ietf.org/doc/html/rfc9260#section-6.3.2
|
|
260
|
+
//
|
|
261
|
+
// "Every time a DATA chunk is sent to any address (including a
|
|
262
|
+
// retransmission), if the T3-rtx timer of that address is not running,
|
|
263
|
+
// start it running so that it will expire after the RTO of that address."
|
|
264
|
+
if (!this->t3RtxTimer->IsRunning())
|
|
265
|
+
{
|
|
266
|
+
this->t3RtxTimer->Start();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const size_t bytesRetransmitted = std::accumulate(
|
|
270
|
+
result.begin(),
|
|
271
|
+
result.end(),
|
|
272
|
+
size_t{ 0 },
|
|
273
|
+
[&](size_t r, const std::pair<uint32_t /*tsn*/, UserData>& data)
|
|
274
|
+
{
|
|
275
|
+
return r + GetSerializedChunkLength(data.second);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
++this->rtxPacketsCount;
|
|
279
|
+
this->rtxBytesCount += bytesRetransmitted;
|
|
280
|
+
|
|
281
|
+
#if MS_LOG_DEV_LEVEL == 3
|
|
282
|
+
std::string tsnList;
|
|
283
|
+
|
|
284
|
+
for (const auto& [tsn, data] : result)
|
|
285
|
+
{
|
|
286
|
+
if (!tsnList.empty())
|
|
287
|
+
{
|
|
288
|
+
tsnList += ',';
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
tsnList += std::to_string(tsn);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
MS_DEBUG_DEV(
|
|
295
|
+
"fast-retransmitting TSN %s - %zu bytes [unackedPacketBytes:%zu, oldUnackedPacketBytes:%zu]",
|
|
296
|
+
tsnList.c_str(),
|
|
297
|
+
bytesRetransmitted,
|
|
298
|
+
GetUnackedPacketBytes(),
|
|
299
|
+
oldUnackedPacketBytes);
|
|
300
|
+
#endif
|
|
301
|
+
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
std::vector<std::pair<uint32_t /*tsn*/, UserData>> RetransmissionQueue::GetChunksToSend(
|
|
306
|
+
uint64_t /*nowMs*/, size_t maxLength)
|
|
307
|
+
{
|
|
308
|
+
MS_TRACE();
|
|
309
|
+
|
|
310
|
+
MS_ASSERT(
|
|
311
|
+
Utils::Byte::IsPaddedTo4Bytes(maxLength),
|
|
312
|
+
"given maxLength %zu is not divisible by 4",
|
|
313
|
+
maxLength);
|
|
314
|
+
|
|
315
|
+
std::vector<std::pair<uint32_t /*tsn*/, UserData>> result;
|
|
316
|
+
|
|
317
|
+
const size_t oldUnackedPacketBytes = GetUnackedPacketBytes();
|
|
318
|
+
#if MS_LOG_DEV_LEVEL == 3
|
|
319
|
+
const size_t oldRwnd = this->rwnd;
|
|
320
|
+
#endif
|
|
321
|
+
|
|
322
|
+
// Calculate the bandwidth budget (how many bytes that is allowed to be
|
|
323
|
+
// sent).
|
|
324
|
+
const size_t maxPacketBytesAllowedByCwnd =
|
|
325
|
+
oldUnackedPacketBytes >= this->cwnd ? 0 : this->cwnd - oldUnackedPacketBytes;
|
|
326
|
+
|
|
327
|
+
size_t maxPacketBytesAllowedByRwnd =
|
|
328
|
+
Utils::Byte::PadTo4Bytes(GetRwnd() + this->dataChunkHeaderLength);
|
|
329
|
+
|
|
330
|
+
// https://datatracker.ietf.org/doc/html/rfc9260#section-6.1
|
|
331
|
+
//
|
|
332
|
+
// "However, regardless of the value of rwnd (including if it is 0), the
|
|
333
|
+
// data sender can always have one DATA chunk in flight to the receiver if
|
|
334
|
+
// allowed by cwnd (see rule B, below)."
|
|
335
|
+
if (this->outstandingData.GetUnackedItems() == 0)
|
|
336
|
+
{
|
|
337
|
+
maxPacketBytesAllowedByRwnd = this->sctpOptions.mtu;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
size_t maxBytes = Utils::Byte::PadDownTo4Bytes(
|
|
341
|
+
std::min({ maxPacketBytesAllowedByCwnd, maxPacketBytesAllowedByRwnd, maxLength }));
|
|
342
|
+
|
|
343
|
+
result = this->outstandingData.GetChunksToBeRetransmitted(maxBytes);
|
|
344
|
+
|
|
345
|
+
const size_t bytesRetransmitted = std::accumulate(
|
|
346
|
+
result.begin(),
|
|
347
|
+
result.end(),
|
|
348
|
+
size_t{ 0 },
|
|
349
|
+
[&](size_t r, const std::pair<uint32_t /*tsn*/, UserData>& data)
|
|
350
|
+
{
|
|
351
|
+
return r + GetSerializedChunkLength(data.second);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
maxBytes -= bytesRetransmitted;
|
|
355
|
+
|
|
356
|
+
if (!result.empty())
|
|
357
|
+
{
|
|
358
|
+
++this->rtxPacketsCount;
|
|
359
|
+
this->rtxBytesCount += bytesRetransmitted;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
while (maxBytes > this->dataChunkHeaderLength)
|
|
363
|
+
{
|
|
364
|
+
MS_ASSERT(
|
|
365
|
+
Utils::Byte::IsPaddedTo4Bytes(maxBytes),
|
|
366
|
+
"computed maxBytes %zu during the loop is not divisible by 4",
|
|
367
|
+
maxBytes);
|
|
368
|
+
|
|
369
|
+
// TODO: SCTP: Implement and uncomment.
|
|
370
|
+
|
|
371
|
+
// std::optional<SendQueue::DataToSend> chunkOpt =
|
|
372
|
+
// this->sendQueue.Produce(nowMs, maxBytes - this->dataChunkHeaderLength);
|
|
373
|
+
|
|
374
|
+
// if (!chunkOpt.has_value())
|
|
375
|
+
// {
|
|
376
|
+
// break;
|
|
377
|
+
// }
|
|
378
|
+
|
|
379
|
+
// const size_t chunkSize = GetSerializedChunkSize(chunkOpt->data);
|
|
380
|
+
|
|
381
|
+
// maxBytes -= chunkSize;
|
|
382
|
+
|
|
383
|
+
// this->rwnd -= chunkOpt->data.size();
|
|
384
|
+
|
|
385
|
+
// const std::optional<UnwrappedTsn> tsn = this->outstandingData.Insert(
|
|
386
|
+
// chunkOpt->messageId,
|
|
387
|
+
// chunkOpt->data,
|
|
388
|
+
// nowMs,
|
|
389
|
+
// this->supportsPartialReliability ? chunkOpt->maxRetransmissions
|
|
390
|
+
// : OutstandingData::MaxRetransmitsNoLimit,
|
|
391
|
+
// this->supportsPartialReliability ? chunkOpt->expiresAtMs
|
|
392
|
+
// : OutstandingData::ExpiresAtMsInfinite,
|
|
393
|
+
// chunkOpt->lifecycleId);
|
|
394
|
+
|
|
395
|
+
// if (tsn.has_value())
|
|
396
|
+
// {
|
|
397
|
+
// if (chunkOpt->lifecycleId != 0)
|
|
398
|
+
// {
|
|
399
|
+
// MS_ASSERT(chunkOpt->data.IsEnd(), "data.IsEnd() should return true");
|
|
400
|
+
|
|
401
|
+
// this->associationListener.OnAssociationLifecycleMessageFullySent(chunkOpt->lifecycleId);
|
|
402
|
+
// }
|
|
403
|
+
|
|
404
|
+
// result.emplace_back(tsn->Wrap(), std::move(chunkOpt->data));
|
|
405
|
+
// }
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// https://tools.ietf.org/html/rfc9260#section-6.3.2
|
|
409
|
+
//
|
|
410
|
+
// "Every time a DATA chunk is sent to any address (including a
|
|
411
|
+
// retransmission), if the T3-rtx timer of that address is not running,
|
|
412
|
+
// start it running so that it will expire after the RTO of that address."
|
|
413
|
+
if (!result.empty())
|
|
414
|
+
{
|
|
415
|
+
if (!this->t3RtxTimer->IsRunning())
|
|
416
|
+
{
|
|
417
|
+
this->t3RtxTimer->Start();
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
#if MS_LOG_DEV_LEVEL == 3
|
|
421
|
+
std::string tsnList;
|
|
422
|
+
|
|
423
|
+
for (const auto& [tsn, data] : result)
|
|
424
|
+
{
|
|
425
|
+
if (!tsnList.empty())
|
|
426
|
+
{
|
|
427
|
+
tsnList += ',';
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
tsnList += std::to_string(tsn);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const size_t bytesRetransmitted = std::accumulate(
|
|
434
|
+
result.begin(),
|
|
435
|
+
result.end(),
|
|
436
|
+
size_t{ 0 },
|
|
437
|
+
[&](size_t r, const std::pair<uint32_t, UserData>& d)
|
|
438
|
+
{
|
|
439
|
+
return r + GetSerializedChunkLength(d.second);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
MS_DEBUG_DEV(
|
|
443
|
+
"sending TSN %s - %zu bytes [unackedPacketBytes:%zu, oldUnackedPacketBytes:%zu, cwnd:%zu, rwnd:%zu, oldRwnd:%zu]",
|
|
444
|
+
tsnList.c_str(),
|
|
445
|
+
bytesRetransmitted,
|
|
446
|
+
GetUnackedPacketBytes(),
|
|
447
|
+
oldUnackedPacketBytes,
|
|
448
|
+
cwnd,
|
|
449
|
+
rwnd,
|
|
450
|
+
oldRwnd);
|
|
451
|
+
#endif
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return result;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
bool RetransmissionQueue::ShouldSendForwardTsn(uint64_t nowMs)
|
|
458
|
+
{
|
|
459
|
+
MS_TRACE();
|
|
460
|
+
|
|
461
|
+
if (!this->supportsPartialReliability)
|
|
462
|
+
{
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
this->outstandingData.ExpireOutstandingChunks(nowMs);
|
|
467
|
+
|
|
468
|
+
return this->outstandingData.ShouldSendForwardTsn();
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
void RetransmissionQueue::PrepareResetStream(uint16_t /*streamId*/)
|
|
472
|
+
{
|
|
473
|
+
MS_TRACE();
|
|
474
|
+
|
|
475
|
+
// TODO: As per TODO comment in same method in dcsctp:
|
|
476
|
+
//
|
|
477
|
+
// "These calls are now only affecting the send queue. The packet buffer
|
|
478
|
+
// can also change behavior - for example draining the chunk producer and
|
|
479
|
+
// eagerly assign TSNs so that an "Outgoing SSN Reset Request" can be sent
|
|
480
|
+
// quickly, with a known `sender_last_assigned_tsn`.
|
|
481
|
+
// TODO: SCTP: Implement it.
|
|
482
|
+
// this->sendQueue.PrepareResetStream(streamId);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
bool RetransmissionQueue::HasStreamsReadyToBeReset() const
|
|
486
|
+
{
|
|
487
|
+
MS_TRACE();
|
|
488
|
+
|
|
489
|
+
// TODO: SCTP: Implement it.
|
|
490
|
+
// return this->sendQueue.HasStreamsReadyToBeReset();
|
|
491
|
+
|
|
492
|
+
// TODO: SCTP: Remove.
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
std::vector<uint16_t /*streamId*/> RetransmissionQueue::BeginResetStreams()
|
|
497
|
+
{
|
|
498
|
+
MS_TRACE();
|
|
499
|
+
|
|
500
|
+
this->outstandingData.BeginResetStreams();
|
|
501
|
+
|
|
502
|
+
// TODO: SCTP: Implement it.
|
|
503
|
+
// this->sendQueue.GetStreamsReadyToBeReset();
|
|
504
|
+
|
|
505
|
+
// TODO: SCTP: Remove.
|
|
506
|
+
return {};
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
void RetransmissionQueue::CommitResetStreams()
|
|
510
|
+
{
|
|
511
|
+
MS_TRACE();
|
|
512
|
+
|
|
513
|
+
// TODO: SCTP: Implement it.
|
|
514
|
+
// this->sendQueue.CommitResetStreams();
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
void RetransmissionQueue::RollbackResetStreams()
|
|
518
|
+
{
|
|
519
|
+
MS_TRACE();
|
|
520
|
+
|
|
521
|
+
// TODO: SCTP: Implement it.
|
|
522
|
+
// this->sendQueue.RollbackResetStreams();
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
size_t RetransmissionQueue::GetSerializedChunkLength(const UserData& data) const
|
|
526
|
+
{
|
|
527
|
+
MS_TRACE();
|
|
528
|
+
|
|
529
|
+
return Utils::Byte::PadTo4Bytes(this->dataChunkHeaderLength + data.GetPayloadLength());
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
bool RetransmissionQueue::IsSackChunkValid(const SackChunk* sackChunk) const
|
|
533
|
+
{
|
|
534
|
+
MS_TRACE();
|
|
535
|
+
|
|
536
|
+
// https://tools.ietf.org/html/rfc9260#section-6.2.1
|
|
537
|
+
//
|
|
538
|
+
// "If Cumulative TSN Ack is less than the Cumulative TSN Ack Point, then
|
|
539
|
+
// drop the SACK. Since Cumulative TSN Ack is monotonically increasing, a
|
|
540
|
+
// SACK whose Cumulative TSN Ack is less than the Cumulative TSN Ack Point
|
|
541
|
+
// indicates an out-of- order SACK."
|
|
542
|
+
//
|
|
543
|
+
// @remarks
|
|
544
|
+
// - Important not to drop SACKs with identical TSN to that previously
|
|
545
|
+
// received, as the gap-ack-blocks or dup tsn fields may have changed.
|
|
546
|
+
const UnwrappedTsn cumulativeTsnAck =
|
|
547
|
+
this->tsnUnwrapper.PeekUnwrap(sackChunk->GetCumulativeTsnAck());
|
|
548
|
+
|
|
549
|
+
if (cumulativeTsnAck < this->outstandingData.GetLastCumulativeTsnAck())
|
|
550
|
+
{
|
|
551
|
+
// https://tools.ietf.org/html/rfc9260#section-6.2.1
|
|
552
|
+
//
|
|
553
|
+
// "If Cumulative TSN Ack is less than the Cumulative TSN Ack Point,
|
|
554
|
+
// then drop the SACK. Since Cumulative TSN Ack is monotonically
|
|
555
|
+
// increasing, a SACK whose Cumulative TSN Ack is less than the
|
|
556
|
+
// Cumulative TSN Ack Point indicates an out-of- order SACK."
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
559
|
+
else if (cumulativeTsnAck > this->outstandingData.GetHighestOutstandingTsn())
|
|
560
|
+
{
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
return std::ranges::all_of(
|
|
565
|
+
sackChunk->GetValidatedGapAckBlocks(),
|
|
566
|
+
[&](const auto& block)
|
|
567
|
+
{
|
|
568
|
+
return UnwrappedTsn::AddTo(cumulativeTsnAck, block.end) <=
|
|
569
|
+
this->outstandingData.GetHighestOutstandingTsn();
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
void RetransmissionQueue::UpdateRttMs(uint64_t nowMs, UnwrappedTsn cumulativeTsnAck)
|
|
574
|
+
{
|
|
575
|
+
MS_TRACE();
|
|
576
|
+
|
|
577
|
+
// RTT updating is flawed in SCTP, as explained in e.g. Pedersen J, Griwodz C,
|
|
578
|
+
// Halvorsen P (2006) Considerations of SCTP retransmission delays for thin
|
|
579
|
+
// streams.
|
|
580
|
+
// Due to delayed acknowledgement, the SACK may be sent much later which
|
|
581
|
+
// increases the calculated RTT.
|
|
582
|
+
//
|
|
583
|
+
// TODO: As per TODO comment in same method in dcsctp:
|
|
584
|
+
//
|
|
585
|
+
// "Consider occasionally sending DATA chunks with I-bit set and use only
|
|
586
|
+
// those packets for measurement.
|
|
587
|
+
|
|
588
|
+
const auto rttMs = this->outstandingData.MeasureRtt(nowMs, cumulativeTsnAck);
|
|
589
|
+
|
|
590
|
+
if (rttMs.has_value())
|
|
591
|
+
{
|
|
592
|
+
this->listener->OnRetransmissionQueueNewRttMs(rttMs.value());
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
void RetransmissionQueue::MayExitFastRecovery(UnwrappedTsn cumulativeTsnAck)
|
|
597
|
+
{
|
|
598
|
+
MS_TRACE();
|
|
599
|
+
|
|
600
|
+
// https://tools.ietf.org/html/rfc9260#section-7.2.4
|
|
601
|
+
//
|
|
602
|
+
// "When a SACK acknowledges all TSNs up to and including this [fast
|
|
603
|
+
// recovery] exit point, Fast Recovery is exited."
|
|
604
|
+
if (this->fastRecoveryExitTsn.has_value() && cumulativeTsnAck >= this->fastRecoveryExitTsn.value())
|
|
605
|
+
{
|
|
606
|
+
MS_DEBUG_DEV(
|
|
607
|
+
"exit point %" PRIu32 " reached, exiting fast recovery",
|
|
608
|
+
this->fastRecoveryExitTsn.value().Wrap());
|
|
609
|
+
|
|
610
|
+
this->fastRecoveryExitTsn = std::nullopt;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
void RetransmissionQueue::StopT3RtxTimerOnIncreasedCumulativeTsnAck(UnwrappedTsn /*cumulativeTsnAck*/)
|
|
615
|
+
{
|
|
616
|
+
MS_TRACE();
|
|
617
|
+
|
|
618
|
+
// TODO: This method is NOT defined in dcsctp!
|
|
619
|
+
//
|
|
620
|
+
// @see https://issues.webrtc.org/issues/505751236
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
void RetransmissionQueue::HandleIncreasedCumulativeTsnAck(
|
|
624
|
+
size_t unackedPacketBytes, size_t totalBytesAcked)
|
|
625
|
+
{
|
|
626
|
+
MS_TRACE();
|
|
627
|
+
|
|
628
|
+
// Allow some margin for classifying as fully utilized, due to e.g. that
|
|
629
|
+
// too small packets (less than kMinimumFragmentedPayload) are not sent +
|
|
630
|
+
// overhead.
|
|
631
|
+
const bool isFullyUtilized = unackedPacketBytes + this->sctpOptions.mtu >= this->cwnd;
|
|
632
|
+
#if MS_LOG_DEV_LEVEL == 3
|
|
633
|
+
const size_t oldCwnd = this->cwnd;
|
|
634
|
+
#endif
|
|
635
|
+
|
|
636
|
+
if (GetCongestionAlgorithmPhase() == CongestionAlgorithmPhase::SLOW_START)
|
|
637
|
+
{
|
|
638
|
+
if (isFullyUtilized && !IsInFastRecovery())
|
|
639
|
+
{
|
|
640
|
+
// https://tools.ietf.org/html/rfc9260#section-7.2.1
|
|
641
|
+
//
|
|
642
|
+
// "Only when these three conditions are met can the cwnd be
|
|
643
|
+
// increased; otherwise, the cwnd MUST not be increased. If these
|
|
644
|
+
// conditions are met, then cwnd MUST be increased by, at most, the
|
|
645
|
+
// lesser of 1) the total size of the previously outstanding DATA
|
|
646
|
+
// chunk(s) acknowledged, and 2) the destination's path MTU."
|
|
647
|
+
this->cwnd += std::min(totalBytesAcked, this->sctpOptions.mtu);
|
|
648
|
+
|
|
649
|
+
MS_DEBUG_DEV("SS increase [cwnd:%zu, oldCwnd:%zu]", this->cwnd, oldCwnd);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
else if (GetCongestionAlgorithmPhase() == CongestionAlgorithmPhase::CONGESTION_AVOIDANCE)
|
|
653
|
+
{
|
|
654
|
+
// https://tools.ietf.org/html/rfc9260#section-7.2.2
|
|
655
|
+
//
|
|
656
|
+
// "Whenever cwnd is greater than ssthresh, upon each SACK arrival
|
|
657
|
+
// that advances the Cumulative TSN Ack Point, increase
|
|
658
|
+
// partial_bytes_acked by the total number of bytes of all new chunks
|
|
659
|
+
// acknowledged in that SACK including chunks acknowledged by the new
|
|
660
|
+
// Cumulative TSN Ack and by Gap Ack Blocks."
|
|
661
|
+
#if MS_LOG_DEV_LEVEL == 3
|
|
662
|
+
const size_t oldPba = this->partialBytesAcked;
|
|
663
|
+
#endif
|
|
664
|
+
|
|
665
|
+
this->partialBytesAcked += totalBytesAcked;
|
|
666
|
+
|
|
667
|
+
if (this->partialBytesAcked >= this->cwnd && isFullyUtilized)
|
|
668
|
+
{
|
|
669
|
+
// https://tools.ietf.org/html/rfc9260#section-7.2.2
|
|
670
|
+
//
|
|
671
|
+
// "When partial_bytes_acked is equal to or greater than cwnd and
|
|
672
|
+
// before the arrival of the SACK the sender had cwnd or more bytes of
|
|
673
|
+
// data outstanding (i.e., before arrival of the SACK, flightsize was
|
|
674
|
+
// greater than or equal to cwnd), increase cwnd by MTU, and reset
|
|
675
|
+
// partial_bytes_acked to (partial_bytes_acked - cwnd)."
|
|
676
|
+
|
|
677
|
+
// Errata: https://datatracker.ietf.org/doc/html/rfc8540#section-3.12
|
|
678
|
+
this->partialBytesAcked -= this->cwnd;
|
|
679
|
+
this->cwnd += this->sctpOptions.mtu;
|
|
680
|
+
|
|
681
|
+
MS_DEBUG_DEV(
|
|
682
|
+
"CA increase [cwnd:%zu, oldCwnd:%zu, ssthresh:%zu, pba:%zu, oldPba:%zu]",
|
|
683
|
+
this->cwnd,
|
|
684
|
+
oldCwnd,
|
|
685
|
+
this->ssthresh,
|
|
686
|
+
this->partialBytesAcked,
|
|
687
|
+
oldPba);
|
|
688
|
+
}
|
|
689
|
+
else
|
|
690
|
+
{
|
|
691
|
+
MS_DEBUG_DEV(
|
|
692
|
+
"CA unchanged [cwnd:%zu, oldCwnd:%zu, ssthresh:%zu, pba:%zu, oldPba:%zu]",
|
|
693
|
+
this->cwnd,
|
|
694
|
+
oldCwnd,
|
|
695
|
+
this->ssthresh,
|
|
696
|
+
this->partialBytesAcked,
|
|
697
|
+
oldPba);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
void RetransmissionQueue::HandlePacketLoss(UnwrappedTsn /*highestTsnAcked*/)
|
|
703
|
+
{
|
|
704
|
+
MS_TRACE();
|
|
705
|
+
|
|
706
|
+
// https://tools.ietf.org/html/rfc9260#section-7.2.4
|
|
707
|
+
//
|
|
708
|
+
// "If not in Fast Recovery, adjust the ssthresh and cwnd of the
|
|
709
|
+
// destination address(es) to which the missing DATA chunks were last
|
|
710
|
+
// sent, according to the formula described in Section 7.2.3."
|
|
711
|
+
if (!IsInFastRecovery())
|
|
712
|
+
{
|
|
713
|
+
#if MS_LOG_DEV_LEVEL == 3
|
|
714
|
+
const size_t oldCwnd = this->cwnd;
|
|
715
|
+
const size_t oldPba = this->partialBytesAcked;
|
|
716
|
+
#endif
|
|
717
|
+
|
|
718
|
+
this->ssthresh =
|
|
719
|
+
std::max(this->cwnd / 2, this->sctpOptions.minCwndMtus * this->sctpOptions.mtu);
|
|
720
|
+
this->cwnd = this->ssthresh;
|
|
721
|
+
this->partialBytesAcked = 0;
|
|
722
|
+
|
|
723
|
+
MS_DEBUG_DEV(
|
|
724
|
+
"packet loss detected (not fast recovery) [cwnd:%zu, oldCwnd:%zu, ssthresh:%zu, pba:%zu, oldPba:%zu]",
|
|
725
|
+
this->cwnd,
|
|
726
|
+
oldCwnd,
|
|
727
|
+
this->ssthresh,
|
|
728
|
+
this->partialBytesAcked,
|
|
729
|
+
oldPba);
|
|
730
|
+
|
|
731
|
+
// https://tools.ietf.org/html/rfc9260#section-7.2.4
|
|
732
|
+
//
|
|
733
|
+
// "If not in Fast Recovery, enter Fast Recovery and mark the highest
|
|
734
|
+
// outstanding TSN as the Fast Recovery exit point."
|
|
735
|
+
this->fastRecoveryExitTsn = this->outstandingData.GetHighestOutstandingTsn();
|
|
736
|
+
|
|
737
|
+
MS_DEBUG_DEV(
|
|
738
|
+
"fast recovery initiated with exit point %" PRIu32,
|
|
739
|
+
this->fastRecoveryExitTsn.value().Wrap());
|
|
740
|
+
}
|
|
741
|
+
// https://tools.ietf.org/html/rfc9260#section-7.2.4
|
|
742
|
+
//
|
|
743
|
+
// "While in Fast Recovery, the ssthresh and cwnd SHOULD NOT change for
|
|
744
|
+
// any destinations due to a subsequent Fast Recovery event (i.e., one
|
|
745
|
+
// SHOULD NOT reduce the cwnd further due to a subsequent Fast
|
|
746
|
+
// Retransmit)."
|
|
747
|
+
else
|
|
748
|
+
{
|
|
749
|
+
MS_DEBUG_DEV("packet loss detected (fast recovery), no changes");
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
void RetransmissionQueue::UpdateReceiverWindow(uint32_t aRwnd)
|
|
754
|
+
{
|
|
755
|
+
MS_TRACE();
|
|
756
|
+
|
|
757
|
+
this->rwnd = this->outstandingData.GetUnackedPayloadBytes() >= aRwnd
|
|
758
|
+
? 0
|
|
759
|
+
: aRwnd - this->outstandingData.GetUnackedPayloadBytes();
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
void RetransmissionQueue::StartT3RtxTimerIfOutstandingData()
|
|
763
|
+
{
|
|
764
|
+
MS_TRACE();
|
|
765
|
+
|
|
766
|
+
// Note: Can't use `GetUnackedPacketBytes()` as that one doesn't count
|
|
767
|
+
// chunks to be retransmitted.
|
|
768
|
+
|
|
769
|
+
// https://tools.ietf.org/html/rfc9260#section-6.3.2
|
|
770
|
+
// "Whenever all outstanding data sent to an address have been
|
|
771
|
+
// acknowledged, turn off the T3-rtx timer of that address.
|
|
772
|
+
if (this->outstandingData.IsEmpty())
|
|
773
|
+
{
|
|
774
|
+
// Note: Already stopped in `StopT3RtxTimerOnIncreasedCumulativeTsnAck()`."
|
|
775
|
+
//
|
|
776
|
+
// TODO: As said above, `StopT3RtxTimerOnIncreasedCumulativeTsnAck()`
|
|
777
|
+
// is NOT defined in dcsctp and of course it's never called from
|
|
778
|
+
// anywhere.
|
|
779
|
+
}
|
|
780
|
+
else
|
|
781
|
+
{
|
|
782
|
+
// https://tools.ietf.org/html/rfc9260#section-6.3.2
|
|
783
|
+
//
|
|
784
|
+
// "Whenever a SACK is received that acknowledges the DATA chunk with
|
|
785
|
+
// the earliest outstanding TSN for that address, restart the T3-rtx
|
|
786
|
+
// timer for that address with its current RTO (if there is still
|
|
787
|
+
// outstanding data on that address)."
|
|
788
|
+
// "Whenever a SACK is received missing a TSN that was previously
|
|
789
|
+
// acknowledged via a Gap Ack Block, start the T3-rtx for the
|
|
790
|
+
// destination address to which the DATA chunk was originally
|
|
791
|
+
// transmitted if it is not already running."
|
|
792
|
+
if (!this->t3RtxTimer->IsRunning())
|
|
793
|
+
{
|
|
794
|
+
this->t3RtxTimer->Start();
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
} // namespace SCTP
|
|
799
|
+
} // namespace RTC
|