mediasoup 3.20.4 → 3.20.6
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 +4 -4
- package/worker/include/RTC/Consumer.hpp +80 -45
- package/worker/include/RTC/PipeProducerStreamManager.hpp +78 -0
- package/worker/include/RTC/ProducerStreamManager.hpp +181 -0
- package/worker/include/RTC/SCTP/association/Association.hpp +24 -0
- package/worker/include/RTC/SCTP/association/StateCookie.hpp +104 -11
- package/worker/include/RTC/SCTP/packet/parameters/StateCookieParameter.hpp +4 -1
- package/worker/include/RTC/SCTP/public/SctpOptions.hpp +13 -0
- package/worker/include/RTC/SeqManager.hpp +2 -2
- package/worker/include/RTC/SimpleProducerStreamManager.hpp +72 -0
- package/worker/include/RTC/SimulcastProducerStreamManager.hpp +93 -0
- package/worker/include/RTC/SvcProducerStreamManager.hpp +72 -0
- package/worker/include/RTC/Transport.hpp +7 -1
- package/worker/meson.build +9 -5
- package/worker/src/RTC/Consumer.cpp +1404 -30
- package/worker/src/RTC/DirectTransport.cpp +4 -1
- package/worker/src/RTC/PipeProducerStreamManager.cpp +266 -0
- package/worker/src/RTC/PipeTransport.cpp +4 -1
- package/worker/src/RTC/PlainTransport.cpp +4 -1
- package/worker/src/RTC/SCTP/association/Association.cpp +142 -3
- package/worker/src/RTC/SCTP/association/StateCookie.cpp +96 -31
- package/worker/src/RTC/SCTP/association/StreamResetHandler.cpp +4 -0
- package/worker/src/RTC/SCTP/packet/Packet.cpp +1 -1
- package/worker/src/RTC/SCTP/packet/parameters/StateCookieParameter.cpp +12 -3
- package/worker/src/RTC/SCTP/public/SctpOptions.cpp +4 -0
- package/worker/src/RTC/SCTP/rx/DataTracker.cpp +4 -1
- package/worker/src/RTC/SeqManager.cpp +42 -29
- package/worker/src/RTC/SimpleProducerStreamManager.cpp +343 -0
- package/worker/src/RTC/SimulcastProducerStreamManager.cpp +1068 -0
- package/worker/src/RTC/SvcProducerStreamManager.cpp +664 -0
- package/worker/src/RTC/Transport.cpp +7 -44
- package/worker/src/RTC/WebRtcTransport.cpp +8 -2
- package/worker/test/include/RTC/SCTP/sctpCommon.hpp +1 -1
- package/worker/test/src/RTC/SCTP/association/TestAssociation.cpp +115 -0
- package/worker/test/src/RTC/SCTP/association/TestStateCookie.cpp +123 -0
- package/worker/test/src/RTC/SCTP/packet/TestPacket.cpp +4 -4
- package/worker/test/src/RTC/{TestSimpleConsumer.cpp → TestConsumer.cpp} +6 -7
- package/worker/test/src/RTC/TestPipeProducerStreamManager.cpp +471 -0
- package/worker/test/src/RTC/TestSimpleProducerStreamManager.cpp +531 -0
- package/worker/test/src/RTC/TestSimulcastProducerStreamManager.cpp +1040 -0
- package/worker/test/src/RTC/TestSvcProducerStreamManager.cpp +1278 -0
- package/worker/include/RTC/PipeConsumer.hpp +0 -95
- package/worker/include/RTC/SimpleConsumer.hpp +0 -102
- package/worker/include/RTC/SimulcastConsumer.hpp +0 -141
- package/worker/include/RTC/SvcConsumer.hpp +0 -118
- package/worker/src/RTC/PipeConsumer.cpp +0 -874
- package/worker/src/RTC/SimpleConsumer.cpp +0 -882
- package/worker/src/RTC/SimulcastConsumer.cpp +0 -1887
- package/worker/src/RTC/SvcConsumer.cpp +0 -1384
|
@@ -15,7 +15,10 @@ namespace RTC
|
|
|
15
15
|
const std::string& id,
|
|
16
16
|
RTC::Transport::Listener* listener,
|
|
17
17
|
const FBS::DirectTransport::DirectTransportOptions* options)
|
|
18
|
-
|
|
18
|
+
// DirectTransport doesn't support SCTP, so State Cookie authentication is
|
|
19
|
+
// irrelevant here.
|
|
20
|
+
: RTC::Transport::Transport(
|
|
21
|
+
shared, id, listener, options->base(), /*requireSctpStateCookieAuthentication*/ false)
|
|
19
22
|
{
|
|
20
23
|
MS_TRACE();
|
|
21
24
|
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
#define MS_CLASS "RTC::PipeProducerStreamManager"
|
|
2
|
+
// #define MS_LOG_DEV_LEVEL 3
|
|
3
|
+
|
|
4
|
+
#include "RTC/PipeProducerStreamManager.hpp"
|
|
5
|
+
#include "Logger.hpp"
|
|
6
|
+
|
|
7
|
+
namespace RTC
|
|
8
|
+
{
|
|
9
|
+
/* Instance methods. */
|
|
10
|
+
|
|
11
|
+
PipeProducerStreamManager::PipeProducerStreamManager(
|
|
12
|
+
const std::vector<RTC::RtpEncodingParameters>& consumableRtpEncodings,
|
|
13
|
+
const RTC::ConsumerTypes::VideoLayers& preferredLayers,
|
|
14
|
+
std::unique_ptr<RTC::RTP::Codecs::EncodingContext> encodingContext,
|
|
15
|
+
RTC::Media::Kind kind,
|
|
16
|
+
bool keyFrameSupported,
|
|
17
|
+
Listener* listener,
|
|
18
|
+
SharedInterface* shared)
|
|
19
|
+
: ProducerStreamManager(
|
|
20
|
+
consumableRtpEncodings,
|
|
21
|
+
preferredLayers,
|
|
22
|
+
std::move(encodingContext),
|
|
23
|
+
kind,
|
|
24
|
+
keyFrameSupported,
|
|
25
|
+
listener,
|
|
26
|
+
shared)
|
|
27
|
+
{
|
|
28
|
+
MS_TRACE();
|
|
29
|
+
|
|
30
|
+
// Initialize per-stream sync state for each consumable encoding.
|
|
31
|
+
for (const auto& encoding : this->consumableRtpEncodings)
|
|
32
|
+
{
|
|
33
|
+
this->mapMappedSsrcSyncRequired[encoding.ssrc] = false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
bool PipeProducerStreamManager::IsActive() const
|
|
38
|
+
{
|
|
39
|
+
MS_TRACE();
|
|
40
|
+
|
|
41
|
+
return this->listener->IsActive();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
void PipeProducerStreamManager::ProducerRtpStream(
|
|
45
|
+
RTC::RTP::RtpStreamRecv* /*rtpStream*/, uint32_t /*mappedSsrc*/)
|
|
46
|
+
{
|
|
47
|
+
MS_TRACE();
|
|
48
|
+
|
|
49
|
+
// Do nothing.
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
void PipeProducerStreamManager::ProducerNewRtpStream(
|
|
53
|
+
RTC::RTP::RtpStreamRecv* /*rtpStream*/, uint32_t /*mappedSsrc*/)
|
|
54
|
+
{
|
|
55
|
+
MS_TRACE();
|
|
56
|
+
|
|
57
|
+
// Do nothing.
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
void PipeProducerStreamManager::ProducerRtpStreamScore(
|
|
61
|
+
RTC::RTP::RtpStreamRecv* /*rtpStream*/, uint8_t /*score*/, uint8_t /*previousScore*/)
|
|
62
|
+
{
|
|
63
|
+
MS_TRACE();
|
|
64
|
+
|
|
65
|
+
// Do nothing.
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
void PipeProducerStreamManager::ProducerRtcpSenderReport(
|
|
69
|
+
RTC::RTP::RtpStreamRecv* /*rtpStream*/, bool /*first*/)
|
|
70
|
+
{
|
|
71
|
+
MS_TRACE();
|
|
72
|
+
|
|
73
|
+
// Do nothing.
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
uint32_t PipeProducerStreamManager::IncreaseLayer(
|
|
77
|
+
uint32_t /*bitrate*/, bool /*considerLoss*/, float /*lossPercentage*/, uint64_t /*nowMs*/)
|
|
78
|
+
{
|
|
79
|
+
MS_TRACE();
|
|
80
|
+
|
|
81
|
+
// Pipe does not play the BWE game.
|
|
82
|
+
return 0u;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
void PipeProducerStreamManager::ApplyLayers(uint64_t /*rtpStreamActiveMs*/)
|
|
86
|
+
{
|
|
87
|
+
MS_TRACE();
|
|
88
|
+
|
|
89
|
+
// Pipe does not play the BWE game.
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
uint32_t PipeProducerStreamManager::GetDesiredBitrate(uint64_t /*nowMs*/) const
|
|
93
|
+
{
|
|
94
|
+
MS_TRACE();
|
|
95
|
+
|
|
96
|
+
// Pipe does not play the BWE game.
|
|
97
|
+
return 0u;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
ProducerStreamManager::RtpPacketProcessResult PipeProducerStreamManager::ProcessRtpPacket(
|
|
101
|
+
RTC::RTP::Packet* packet,
|
|
102
|
+
bool /*lastSentPacketHasMarker*/,
|
|
103
|
+
uint32_t /*clockRate*/,
|
|
104
|
+
uint32_t /*maxPacketTs*/)
|
|
105
|
+
{
|
|
106
|
+
MS_TRACE();
|
|
107
|
+
|
|
108
|
+
RtpPacketProcessResult result;
|
|
109
|
+
|
|
110
|
+
auto it = this->mapMappedSsrcSyncRequired.find(packet->GetSsrc());
|
|
111
|
+
|
|
112
|
+
MS_ASSERT(it != this->mapMappedSsrcSyncRequired.end(), "unknown mapped SSRC");
|
|
113
|
+
|
|
114
|
+
bool& syncRequired = it->second;
|
|
115
|
+
|
|
116
|
+
// If sync required and key frames supported, buffer non-key-frame packets.
|
|
117
|
+
if (syncRequired && this->keyFrameSupported && !packet->IsKeyFrame())
|
|
118
|
+
{
|
|
119
|
+
result.type = RtpPacketProcessResult::Type::BUFFER;
|
|
120
|
+
|
|
121
|
+
#ifdef MS_RTC_LOGGER_RTP
|
|
122
|
+
packet->logger.Discarded(RTC::RtcLogger::RtpPacket::DiscardReason::NOT_A_KEYFRAME);
|
|
123
|
+
#endif
|
|
124
|
+
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Packets with only padding are not forwarded.
|
|
129
|
+
if (packet->GetPayloadLength() == 0)
|
|
130
|
+
{
|
|
131
|
+
result.type = RtpPacketProcessResult::Type::DROP;
|
|
132
|
+
|
|
133
|
+
#ifdef MS_RTC_LOGGER_RTP
|
|
134
|
+
packet->logger.Discarded(RTC::RtcLogger::RtpPacket::DiscardReason::EMPTY_PAYLOAD);
|
|
135
|
+
#endif
|
|
136
|
+
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Whether this is the first packet after re-sync.
|
|
141
|
+
const bool isSyncPacket = syncRequired;
|
|
142
|
+
|
|
143
|
+
if (isSyncPacket)
|
|
144
|
+
{
|
|
145
|
+
if (packet->IsKeyFrame())
|
|
146
|
+
{
|
|
147
|
+
MS_DEBUG_TAG(
|
|
148
|
+
rtp,
|
|
149
|
+
"sync key frame received [ssrc:%" PRIu32 ", seq:%" PRIu16 ", ts:%" PRIu32 "]",
|
|
150
|
+
packet->GetSsrc(),
|
|
151
|
+
packet->GetSequenceNumber(),
|
|
152
|
+
packet->GetTimestamp());
|
|
153
|
+
|
|
154
|
+
result.sendBufferedPackets = true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
result.isSyncPacket = true;
|
|
158
|
+
result.syncSeqValue = packet->GetSequenceNumber() - 1;
|
|
159
|
+
|
|
160
|
+
syncRequired = false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Pipe passes timestamp and marker through unchanged.
|
|
164
|
+
result.type = RtpPacketProcessResult::Type::FORWARD;
|
|
165
|
+
result.tsOffset = 0u;
|
|
166
|
+
result.marker = packet->HasMarker();
|
|
167
|
+
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
void PipeProducerStreamManager::RequestKeyFrame()
|
|
172
|
+
{
|
|
173
|
+
MS_TRACE();
|
|
174
|
+
|
|
175
|
+
if (this->kind != RTC::Media::Kind::VIDEO)
|
|
176
|
+
{
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
for (const auto& encoding : this->consumableRtpEncodings)
|
|
181
|
+
{
|
|
182
|
+
this->listener->OnProducerStreamManagerKeyFrameRequested(encoding.ssrc);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
void PipeProducerStreamManager::RequestKeyFrameForTargetSpatialLayer()
|
|
187
|
+
{
|
|
188
|
+
MS_TRACE();
|
|
189
|
+
|
|
190
|
+
RequestKeyFrame();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
void PipeProducerStreamManager::RequestKeyFrameForCurrentSpatialLayer()
|
|
194
|
+
{
|
|
195
|
+
MS_TRACE();
|
|
196
|
+
|
|
197
|
+
RequestKeyFrame();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
void PipeProducerStreamManager::UpdateTargetLayers(int16_t /*spatial*/, int16_t /*temporal*/)
|
|
201
|
+
{
|
|
202
|
+
MS_TRACE();
|
|
203
|
+
|
|
204
|
+
// No layer management for pipe.
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
bool PipeProducerStreamManager::RecalculateTargetLayers(
|
|
208
|
+
RTC::ConsumerTypes::VideoLayers& /*newTargetLayers*/) const
|
|
209
|
+
{
|
|
210
|
+
MS_TRACE();
|
|
211
|
+
|
|
212
|
+
// No layer changes for pipe.
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
void PipeProducerStreamManager::OnTransportConnected()
|
|
217
|
+
{
|
|
218
|
+
MS_TRACE();
|
|
219
|
+
|
|
220
|
+
for (auto& kv : this->mapMappedSsrcSyncRequired)
|
|
221
|
+
{
|
|
222
|
+
kv.second = true;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (IsActive())
|
|
226
|
+
{
|
|
227
|
+
RequestKeyFrame();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
void PipeProducerStreamManager::OnTransportDisconnected()
|
|
232
|
+
{
|
|
233
|
+
MS_TRACE();
|
|
234
|
+
|
|
235
|
+
for (auto& kv : this->mapMappedSsrcSyncRequired)
|
|
236
|
+
{
|
|
237
|
+
kv.second = false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
void PipeProducerStreamManager::OnPaused()
|
|
242
|
+
{
|
|
243
|
+
MS_TRACE();
|
|
244
|
+
|
|
245
|
+
for (auto& kv : this->mapMappedSsrcSyncRequired)
|
|
246
|
+
{
|
|
247
|
+
kv.second = false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
void PipeProducerStreamManager::OnResumed()
|
|
252
|
+
{
|
|
253
|
+
MS_TRACE();
|
|
254
|
+
|
|
255
|
+
for (auto& kv : this->mapMappedSsrcSyncRequired)
|
|
256
|
+
{
|
|
257
|
+
kv.second = true;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (IsActive())
|
|
261
|
+
{
|
|
262
|
+
RequestKeyFrame();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
} // namespace RTC
|
|
@@ -29,7 +29,10 @@ namespace RTC
|
|
|
29
29
|
const std::string& id,
|
|
30
30
|
RTC::Transport::Listener* listener,
|
|
31
31
|
const FBS::PipeTransport::PipeTransportOptions* options)
|
|
32
|
-
|
|
32
|
+
// SCTP over PipeTransport is not protected by DTLS, so received State
|
|
33
|
+
// Cookies must be authenticated to prevent forgery (RFC 9260 section 5.1.3).
|
|
34
|
+
: RTC::Transport::Transport(
|
|
35
|
+
shared, id, listener, options->base(), /*requireSctpStateCookieAuthentication*/ true)
|
|
33
36
|
{
|
|
34
37
|
MS_TRACE();
|
|
35
38
|
|
|
@@ -37,7 +37,10 @@ namespace RTC
|
|
|
37
37
|
const std::string& id,
|
|
38
38
|
RTC::Transport::Listener* listener,
|
|
39
39
|
const FBS::PlainTransport::PlainTransportOptions* options)
|
|
40
|
-
|
|
40
|
+
// SCTP over PlainTransport is not protected by DTLS, so received State
|
|
41
|
+
// Cookies must be authenticated to prevent forgery (RFC 9260 section 5.1.3).
|
|
42
|
+
: RTC::Transport::Transport(
|
|
43
|
+
shared, id, listener, options->base(), /*requireSctpStateCookieAuthentication*/ true)
|
|
41
44
|
{
|
|
42
45
|
MS_TRACE();
|
|
43
46
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include "RTC/SCTP/packet/errorCauses/NoUserDataErrorCause.hpp"
|
|
8
8
|
#include "RTC/SCTP/packet/errorCauses/OutOfResourceErrorCause.hpp"
|
|
9
9
|
#include "RTC/SCTP/packet/errorCauses/ProtocolViolationErrorCause.hpp"
|
|
10
|
+
#include "RTC/SCTP/packet/errorCauses/StaleCookieErrorCause.hpp"
|
|
10
11
|
#include "RTC/SCTP/packet/errorCauses/UnrecognizedChunkTypeErrorCause.hpp"
|
|
11
12
|
#include "RTC/SCTP/packet/errorCauses/UserInitiatedAbortErrorCause.hpp"
|
|
12
13
|
#include "RTC/SCTP/packet/parameters/ForwardTsnSupportedParameter.hpp"
|
|
@@ -83,6 +84,15 @@ namespace RTC
|
|
|
83
84
|
mayConnectOnReceivedSctpData(mayConnectOnReceivedSctpData)
|
|
84
85
|
{
|
|
85
86
|
MS_TRACE();
|
|
87
|
+
|
|
88
|
+
// Generate the per-association secret used to authenticate the State
|
|
89
|
+
// Cookies we generate.
|
|
90
|
+
//
|
|
91
|
+
// @see RFC 9260 section 5.1.3.
|
|
92
|
+
if (this->sctpOptions.requireAuthenticatedCookie)
|
|
93
|
+
{
|
|
94
|
+
Utils::Crypto::WriteRandomBytes(this->stateCookieSecret, Association::StateCookieSecretLength);
|
|
95
|
+
}
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
Association::~Association()
|
|
@@ -1057,6 +1067,32 @@ namespace RTC
|
|
|
1057
1067
|
{
|
|
1058
1068
|
MS_TRACE();
|
|
1059
1069
|
|
|
1070
|
+
// https://datatracker.ietf.org/doc/html/rfc9653#section-5.3
|
|
1071
|
+
//
|
|
1072
|
+
// "If an endpoint has sent the Zero Checksum Acceptable Chunk Parameter
|
|
1073
|
+
// indicating the support of an alternate error detection method in an INIT
|
|
1074
|
+
// or INIT ACK chunk, in addition to SCTP packets containing the correct
|
|
1075
|
+
// CRC32c checksum value it MUST accept SCTP packets that have an incorrect
|
|
1076
|
+
// checksum value of zero [...]"
|
|
1077
|
+
const bool acceptZeroChecksum =
|
|
1078
|
+
this->sctpOptions.zeroChecksumAlternateErrorDetectionMethod !=
|
|
1079
|
+
ZeroChecksumAcceptableParameter::AlternateErrorDetectionMethod::NONE;
|
|
1080
|
+
|
|
1081
|
+
// Validate the CRC32c checksum unless this is a zero checksum packet that we
|
|
1082
|
+
// announced we are willing to accept.
|
|
1083
|
+
if (!acceptZeroChecksum || receivedPacket->GetChecksum() != 0)
|
|
1084
|
+
{
|
|
1085
|
+
if (!receivedPacket->ValidateCRC32cChecksum())
|
|
1086
|
+
{
|
|
1087
|
+
MS_WARN_TAG(sctp, "invalid CRC32c checksum, packet discarded");
|
|
1088
|
+
|
|
1089
|
+
this->associationListenerDeferrer.OnAssociationError(
|
|
1090
|
+
Types::ErrorKind::PARSE_FAILED, "invalid CRC32c checksum");
|
|
1091
|
+
|
|
1092
|
+
return false;
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1060
1096
|
const uint32_t localVerificationTag = this->tcb ? this->tcb->GetLocalVerificationTag() : 0;
|
|
1061
1097
|
|
|
1062
1098
|
// https://datatracker.ietf.org/doc/html/rfc9260#section-8.5.1
|
|
@@ -1462,7 +1498,9 @@ namespace RTC
|
|
|
1462
1498
|
|
|
1463
1499
|
case State::CLOSED:
|
|
1464
1500
|
{
|
|
1465
|
-
MS_WARN_TAG(sctp, "ignoring INIT chunk received in CLOSED state
|
|
1501
|
+
MS_WARN_TAG(sctp, "ignoring INIT chunk received in CLOSED state");
|
|
1502
|
+
|
|
1503
|
+
return;
|
|
1466
1504
|
}
|
|
1467
1505
|
|
|
1468
1506
|
// https://datatracker.ietf.org/doc/html/rfc9260#section-5.2.1
|
|
@@ -1535,6 +1573,12 @@ namespace RTC
|
|
|
1535
1573
|
const auto negotiatedCapabilities =
|
|
1536
1574
|
NegotiatedCapabilities::Factory(this->sctpOptions, receivedInitChunk);
|
|
1537
1575
|
|
|
1576
|
+
// When authentication is enabled, generate an authenticated cookie with a
|
|
1577
|
+
// creation timestamp and a MAC keyed with our per-association secret.
|
|
1578
|
+
//
|
|
1579
|
+
// @see RFC 9260 section 5.1.3.
|
|
1580
|
+
const bool authenticateCookie = this->sctpOptions.requireAuthenticatedCookie;
|
|
1581
|
+
|
|
1538
1582
|
// Write the StateCookie in place in the parameter.
|
|
1539
1583
|
stateCookieParameter->WriteStateCookieInPlace(
|
|
1540
1584
|
localVerificationTag,
|
|
@@ -1543,7 +1587,10 @@ namespace RTC
|
|
|
1543
1587
|
receivedInitChunk->GetInitialTsn(),
|
|
1544
1588
|
receivedInitChunk->GetAdvertisedReceiverWindowCredit(),
|
|
1545
1589
|
tieTag,
|
|
1546
|
-
negotiatedCapabilities
|
|
1590
|
+
negotiatedCapabilities,
|
|
1591
|
+
/*creationTimestampMs*/ authenticateCookie ? this->shared->GetTimeMs() : 0,
|
|
1592
|
+
/*macKey*/ authenticateCookie ? this->stateCookieSecret : nullptr,
|
|
1593
|
+
/*macKeyLength*/ authenticateCookie ? Association::StateCookieSecretLength : 0);
|
|
1547
1594
|
|
|
1548
1595
|
stateCookieParameter->Consolidate();
|
|
1549
1596
|
|
|
@@ -1676,6 +1723,18 @@ namespace RTC
|
|
|
1676
1723
|
return;
|
|
1677
1724
|
}
|
|
1678
1725
|
|
|
1726
|
+
// When authentication is enabled, verify that the cookie was generated by
|
|
1727
|
+
// us (valid MAC) and is not stale before trusting any of its contents.
|
|
1728
|
+
//
|
|
1729
|
+
// @see RFC 9260 section 5.1.4.
|
|
1730
|
+
if (this->sctpOptions.requireAuthenticatedCookie)
|
|
1731
|
+
{
|
|
1732
|
+
if (!VerifyReceivedStateCookie(cookie.get()))
|
|
1733
|
+
{
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1679
1738
|
if (this->tcb)
|
|
1680
1739
|
{
|
|
1681
1740
|
if (!HandleReceivedCookieEchoChunkWithTcb(receivedPacket, cookie.get()))
|
|
@@ -1812,7 +1871,7 @@ namespace RTC
|
|
|
1812
1871
|
else if (
|
|
1813
1872
|
receivedPacket->GetVerificationTag() != this->tcb->GetLocalVerificationTag() &&
|
|
1814
1873
|
cookie->GetRemoteVerificationTag() == this->tcb->GetRemoteVerificationTag() &&
|
|
1815
|
-
cookie->GetTieTag() ==
|
|
1874
|
+
cookie->GetTieTag() == 0)
|
|
1816
1875
|
{
|
|
1817
1876
|
MS_DEBUG_DEV("received COOKIE-ECHO indicating a late COOKIE-ECHO, discarding");
|
|
1818
1877
|
|
|
@@ -2430,6 +2489,86 @@ namespace RTC
|
|
|
2430
2489
|
return !skipProcessing;
|
|
2431
2490
|
}
|
|
2432
2491
|
|
|
2492
|
+
bool Association::VerifyReceivedStateCookie(const StateCookie* cookie)
|
|
2493
|
+
{
|
|
2494
|
+
MS_TRACE();
|
|
2495
|
+
|
|
2496
|
+
// https://datatracker.ietf.org/doc/html/rfc9260#section-5.1.4
|
|
2497
|
+
//
|
|
2498
|
+
// "Authenticate the State Cookie as one that it previously generated by
|
|
2499
|
+
// comparing the computed MAC against the one carried in the State Cookie.
|
|
2500
|
+
// If this comparison fails, the SCTP packet, including the COOKIE ECHO and
|
|
2501
|
+
// any DATA chunks, should be silently discarded."
|
|
2502
|
+
//
|
|
2503
|
+
// NOTE: The Verification Tag check (RFC 9260 section 5.1.4) is not done
|
|
2504
|
+
// here but in the COOKIE-ECHO handling logic (with and without TCB), which
|
|
2505
|
+
// is performed regardless of whether authentication is enabled.
|
|
2506
|
+
if (!StateCookie::VerifyMac(
|
|
2507
|
+
cookie->GetBuffer(),
|
|
2508
|
+
cookie->GetLength(),
|
|
2509
|
+
this->stateCookieSecret,
|
|
2510
|
+
Association::StateCookieSecretLength))
|
|
2511
|
+
{
|
|
2512
|
+
MS_WARN_TAG(
|
|
2513
|
+
sctp,
|
|
2514
|
+
"received COOKIE-ECHO chunk with State Cookie that failed MAC authentication, discarding");
|
|
2515
|
+
|
|
2516
|
+
this->associationListenerDeferrer.OnAssociationError(
|
|
2517
|
+
Types::ErrorKind::PARSE_FAILED,
|
|
2518
|
+
"received COOKIE-ECHO chunk with unauthenticated State Cookie");
|
|
2519
|
+
|
|
2520
|
+
return false;
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
// https://datatracker.ietf.org/doc/html/rfc9260#section-5.1.4
|
|
2524
|
+
//
|
|
2525
|
+
// "Compare the creation timestamp in the State Cookie to the current local
|
|
2526
|
+
// time. If the elapsed time is longer than the lifespan carried in the
|
|
2527
|
+
// State Cookie, then the packet, including the COOKIE ECHO and any attached
|
|
2528
|
+
// DATA chunks, SHOULD be discarded, and the endpoint MUST transmit an ERROR
|
|
2529
|
+
// chunk with a 'Stale Cookie' error cause to the peer endpoint."
|
|
2530
|
+
const uint64_t nowMs = this->shared->GetTimeMs();
|
|
2531
|
+
const uint64_t creationMs = cookie->GetCreationTimestampMs();
|
|
2532
|
+
|
|
2533
|
+
if (nowMs > creationMs && nowMs - creationMs > StateCookie::ValidCookieLifeMs)
|
|
2534
|
+
{
|
|
2535
|
+
const uint64_t stalenessMs = nowMs - creationMs - StateCookie::ValidCookieLifeMs;
|
|
2536
|
+
|
|
2537
|
+
MS_WARN_TAG(
|
|
2538
|
+
sctp,
|
|
2539
|
+
"received COOKIE-ECHO chunk with stale State Cookie, sending Stale Cookie error [staleness:%" PRIu64
|
|
2540
|
+
"ms]",
|
|
2541
|
+
stalenessMs);
|
|
2542
|
+
|
|
2543
|
+
// Respond with an ERROR chunk carrying a Stale Cookie error cause. The
|
|
2544
|
+
// packet must use the verification tag the peer expects (the remote
|
|
2545
|
+
// verification tag stored in the cookie we generated).
|
|
2546
|
+
auto packet = CreatePacketWithVerificationTag(cookie->GetRemoteVerificationTag());
|
|
2547
|
+
auto* operationErrorChunk = packet->BuildChunkInPlace<OperationErrorChunk>();
|
|
2548
|
+
auto* staleCookieErrorCause =
|
|
2549
|
+
operationErrorChunk->BuildErrorCauseInPlace<StaleCookieErrorCause>();
|
|
2550
|
+
|
|
2551
|
+
// The Measure of Staleness is expressed in microseconds.
|
|
2552
|
+
const uint64_t stalenessUs = stalenessMs * 1000;
|
|
2553
|
+
|
|
2554
|
+
staleCookieErrorCause->SetMeasureOfStaleness(
|
|
2555
|
+
stalenessUs > std::numeric_limits<uint32_t>::max() ? std::numeric_limits<uint32_t>::max()
|
|
2556
|
+
: static_cast<uint32_t>(stalenessUs));
|
|
2557
|
+
|
|
2558
|
+
staleCookieErrorCause->Consolidate();
|
|
2559
|
+
operationErrorChunk->Consolidate();
|
|
2560
|
+
|
|
2561
|
+
this->packetSender.SendPacket(packet.get());
|
|
2562
|
+
|
|
2563
|
+
this->associationListenerDeferrer.OnAssociationError(
|
|
2564
|
+
Types::ErrorKind::WRONG_SEQUENCE, "received COOKIE-ECHO chunk with stale State Cookie");
|
|
2565
|
+
|
|
2566
|
+
return false;
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
return true;
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2433
2572
|
void Association::OnT1InitTimer(uint64_t& /*baseTimeoutMs*/, bool& /*stop*/)
|
|
2434
2573
|
{
|
|
2435
2574
|
MS_TRACE();
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
#include "RTC/SCTP/association/StateCookie.hpp"
|
|
5
5
|
#include "Logger.hpp"
|
|
6
6
|
#include "MediaSoupErrors.hpp"
|
|
7
|
+
#include <cstring> // std::memcpy(), std::memcmp()
|
|
8
|
+
#include <string_view>
|
|
7
9
|
|
|
8
10
|
namespace RTC
|
|
9
11
|
{
|
|
@@ -11,31 +13,6 @@ namespace RTC
|
|
|
11
13
|
{
|
|
12
14
|
/* Class methods. */
|
|
13
15
|
|
|
14
|
-
bool StateCookie::IsMediasoupStateCookie(const uint8_t* buffer, size_t bufferLength)
|
|
15
|
-
{
|
|
16
|
-
MS_TRACE();
|
|
17
|
-
|
|
18
|
-
if (bufferLength != StateCookie::StateCookieLength)
|
|
19
|
-
{
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (Utils::Byte::Get8Bytes(buffer, 0) != StateCookie::Magic1)
|
|
24
|
-
{
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
auto* negotiatedCapabilitiesField = reinterpret_cast<NegotiatedCapabilitiesField*>(
|
|
29
|
-
const_cast<uint8_t*>(buffer) + StateCookie::NegotiatedCapabilitiesOffset);
|
|
30
|
-
|
|
31
|
-
if (ntohs(negotiatedCapabilitiesField->magic2) != StateCookie::Magic2)
|
|
32
|
-
{
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
16
|
StateCookie* StateCookie::Parse(const uint8_t* buffer, size_t bufferLength)
|
|
40
17
|
{
|
|
41
18
|
MS_TRACE();
|
|
@@ -61,7 +38,10 @@ namespace RTC
|
|
|
61
38
|
uint32_t remoteInitialTsn,
|
|
62
39
|
uint32_t remoteAdvertisedReceiverWindowCredit,
|
|
63
40
|
uint64_t tieTag,
|
|
64
|
-
const NegotiatedCapabilities& negotiatedCapabilities
|
|
41
|
+
const NegotiatedCapabilities& negotiatedCapabilities,
|
|
42
|
+
uint64_t creationTimestampMs,
|
|
43
|
+
const uint8_t* macKey,
|
|
44
|
+
size_t macKeyLength)
|
|
65
45
|
{
|
|
66
46
|
MS_TRACE();
|
|
67
47
|
|
|
@@ -75,9 +55,15 @@ namespace RTC
|
|
|
75
55
|
remoteInitialTsn,
|
|
76
56
|
remoteAdvertisedReceiverWindowCredit,
|
|
77
57
|
tieTag,
|
|
78
|
-
negotiatedCapabilities
|
|
58
|
+
negotiatedCapabilities,
|
|
59
|
+
creationTimestampMs,
|
|
60
|
+
macKey,
|
|
61
|
+
macKeyLength);
|
|
62
|
+
|
|
63
|
+
const size_t cookieLength = macKey != nullptr ? StateCookie::AuthenticatedStateCookieLength
|
|
64
|
+
: StateCookie::StateCookieLength;
|
|
79
65
|
|
|
80
|
-
return new StateCookie(buffer,
|
|
66
|
+
return new StateCookie(buffer, cookieLength);
|
|
81
67
|
}
|
|
82
68
|
|
|
83
69
|
void StateCookie::Write(
|
|
@@ -89,11 +75,18 @@ namespace RTC
|
|
|
89
75
|
uint32_t remoteInitialTsn,
|
|
90
76
|
uint32_t remoteAdvertisedReceiverWindowCredit,
|
|
91
77
|
uint64_t tieTag,
|
|
92
|
-
const NegotiatedCapabilities& negotiatedCapabilities
|
|
78
|
+
const NegotiatedCapabilities& negotiatedCapabilities,
|
|
79
|
+
uint64_t creationTimestampMs,
|
|
80
|
+
const uint8_t* macKey,
|
|
81
|
+
size_t macKeyLength)
|
|
93
82
|
{
|
|
94
83
|
MS_TRACE();
|
|
95
84
|
|
|
96
|
-
|
|
85
|
+
const bool authenticate = macKey != nullptr;
|
|
86
|
+
const size_t cookieLength =
|
|
87
|
+
authenticate ? StateCookie::AuthenticatedStateCookieLength : StateCookie::StateCookieLength;
|
|
88
|
+
|
|
89
|
+
if (bufferLength < cookieLength)
|
|
97
90
|
{
|
|
98
91
|
MS_THROW_TYPE_ERROR("buffer too small");
|
|
99
92
|
}
|
|
@@ -119,6 +112,65 @@ namespace RTC
|
|
|
119
112
|
htons(negotiatedCapabilities.negotiatedMaxOutboundStreams);
|
|
120
113
|
negotiatedCapabilitiesField->negotiatedMaxInboundStreams =
|
|
121
114
|
htons(negotiatedCapabilities.negotiatedMaxInboundStreams);
|
|
115
|
+
|
|
116
|
+
if (!authenticate)
|
|
117
|
+
{
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Append the creation timestamp and the MAC computed over all preceding
|
|
122
|
+
// bytes (including the timestamp).
|
|
123
|
+
//
|
|
124
|
+
// @see RFC 9260 section 5.1.3.
|
|
125
|
+
Utils::Byte::Set8Bytes(buffer, StateCookie::TimestampOffset, creationTimestampMs);
|
|
126
|
+
|
|
127
|
+
const uint8_t* mac = Utils::Crypto::GetHmacSha1(
|
|
128
|
+
reinterpret_cast<const char*>(macKey), macKeyLength, buffer, StateCookie::MacOffset);
|
|
129
|
+
|
|
130
|
+
std::memcpy(buffer + StateCookie::MacOffset, mac, StateCookie::MacLength);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
bool StateCookie::IsMediasoupStateCookie(const uint8_t* buffer, size_t bufferLength)
|
|
134
|
+
{
|
|
135
|
+
MS_TRACE();
|
|
136
|
+
|
|
137
|
+
if (bufferLength != StateCookie::StateCookieLength && bufferLength != StateCookie::AuthenticatedStateCookieLength)
|
|
138
|
+
{
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (Utils::Byte::Get8Bytes(buffer, 0) != StateCookie::Magic1)
|
|
143
|
+
{
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
auto* negotiatedCapabilitiesField = reinterpret_cast<NegotiatedCapabilitiesField*>(
|
|
148
|
+
const_cast<uint8_t*>(buffer) + StateCookie::NegotiatedCapabilitiesOffset);
|
|
149
|
+
|
|
150
|
+
if (ntohs(negotiatedCapabilitiesField->magic2) != StateCookie::Magic2)
|
|
151
|
+
{
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
bool StateCookie::VerifyMac(
|
|
159
|
+
const uint8_t* buffer, size_t bufferLength, const uint8_t* macKey, size_t macKeyLength)
|
|
160
|
+
{
|
|
161
|
+
MS_TRACE();
|
|
162
|
+
|
|
163
|
+
// An authenticated cookie has a fixed length.
|
|
164
|
+
if (bufferLength != StateCookie::AuthenticatedStateCookieLength)
|
|
165
|
+
{
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Recompute the MAC over all bytes preceding the MAC field.
|
|
170
|
+
const uint8_t* expectedMac = Utils::Crypto::GetHmacSha1(
|
|
171
|
+
reinterpret_cast<const char*>(macKey), macKeyLength, buffer, StateCookie::MacOffset);
|
|
172
|
+
|
|
173
|
+
return std::memcmp(buffer + StateCookie::MacOffset, expectedMac, StateCookie::MacLength) == 0;
|
|
122
174
|
}
|
|
123
175
|
|
|
124
176
|
Types::SctpImplementation StateCookie::DetermineSctpImplementation(
|
|
@@ -158,7 +210,13 @@ namespace RTC
|
|
|
158
210
|
{
|
|
159
211
|
MS_TRACE();
|
|
160
212
|
|
|
161
|
-
|
|
213
|
+
// The cookie length is determined by whether it carries a MAC. When the
|
|
214
|
+
// cookie is cloned into a larger buffer, `CloneInto()` will set the
|
|
215
|
+
// proper length afterwards.
|
|
216
|
+
SetLength(
|
|
217
|
+
bufferLength == StateCookie::AuthenticatedStateCookieLength
|
|
218
|
+
? StateCookie::AuthenticatedStateCookieLength
|
|
219
|
+
: StateCookie::StateCookieLength);
|
|
162
220
|
}
|
|
163
221
|
|
|
164
222
|
StateCookie::~StateCookie()
|
|
@@ -183,6 +241,13 @@ namespace RTC
|
|
|
183
241
|
" remote advertised receiver window credit: %" PRIu32,
|
|
184
242
|
GetRemoteAdvertisedReceiverWindowCredit());
|
|
185
243
|
MS_DUMP_CLEAN(indentation, " tie-tag: %" PRIu64, GetTieTag());
|
|
244
|
+
MS_DUMP_CLEAN(indentation, " authenticated: %s", IsAuthenticated() ? "yes" : "no");
|
|
245
|
+
|
|
246
|
+
if (IsAuthenticated())
|
|
247
|
+
{
|
|
248
|
+
MS_DUMP_CLEAN(indentation, " creation timestamp (ms): %" PRIu64, GetCreationTimestampMs());
|
|
249
|
+
}
|
|
250
|
+
|
|
186
251
|
negotiatedCapabilities.Dump(indentation + 1);
|
|
187
252
|
MS_DUMP_CLEAN(indentation, "</SCTP::StateCookie>");
|
|
188
253
|
}
|
|
@@ -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
|
{
|