sdp: add validation for the number of ssrcs in the ssrc group

for the known standard semantics FID (used by rtx) and
FEC-FR (used byFlexFEC) they should match the expected two SSRCs.
For the nonstandard SIM group this should be limited by the maximum
number of simulcast layers supported.

BUG=chromium:1459124

Change-Id: I7cc2417a3ab207658ec80e8d7e9984c1ae631f53
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/315323
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Philipp Hancke <phancke@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#40652}
This commit is contained in:
Philipp Hancke 2023-08-11 09:32:50 +02:00 committed by WebRTC LUCI CQ
parent 573ca2beb7
commit 1f1b0b31e7
3 changed files with 149 additions and 3 deletions

View File

@ -826,7 +826,7 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, TracksDoNotEndWhenSsrcChanges) {
for (size_t i = 0; i < contents.size(); ++i) {
auto& mutable_streams = contents[i].media_description()->mutable_streams();
ASSERT_EQ(mutable_streams.size(), 1u);
mutable_streams[0].ssrcs = {kFirstMungedSsrc + static_cast<uint32_t>(i)};
mutable_streams[0].ssrcs[0] = kFirstMungedSsrc + static_cast<uint32_t>(i);
}
ASSERT_TRUE(
callee->SetLocalDescription(CloneSessionDescription(answer.get())));

View File

@ -535,6 +535,33 @@ RTCError ValidateRtpHeaderExtensionsForSpecSimulcast(
return RTCError::OK();
}
RTCError ValidateSsrcGroups(const cricket::SessionDescription& description) {
for (const ContentInfo& content : description.contents()) {
if (content.type != MediaProtocolType::kRtp) {
continue;
}
for (const StreamParams& stream : content.media_description()->streams()) {
for (const cricket::SsrcGroup& group : stream.ssrc_groups) {
// Validate the number of SSRCs for standard SSRC group semantics such
// as FID and FEC-FR and the non-standard SIM group.
if ((group.semantics == cricket::kFidSsrcGroupSemantics &&
group.ssrcs.size() != 2) ||
(group.semantics == cricket::kFecFrSsrcGroupSemantics &&
group.ssrcs.size() != 2) ||
(group.semantics == cricket::kSimSsrcGroupSemantics &&
group.ssrcs.size() > kMaxSimulcastStreams)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
"The media section with MID='" + content.mid() +
"' has a ssrc-group with semantics " +
group.semantics +
" and an unexpected number of SSRCs.");
}
}
}
}
return RTCError::OK();
}
bool IsValidOfferToReceiveMedia(int value) {
typedef PeerConnectionInterface::RTCOfferAnswerOptions Options;
return (value >= Options::kUndefined) &&
@ -3547,6 +3574,13 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription(
return error;
}
// TODO(crbug.com/1459124): remove killswitch after rollout.
error = ValidateSsrcGroups(*sdesc->description());
if (!error.ok() &&
!pc_->trials().IsDisabled("WebRTC-PreventSsrcGroupsWithUnexpectedSize")) {
return error;
}
if (!pc_->ValidateBundleSettings(sdesc->description(),
bundle_groups_by_mid)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,

View File

@ -646,7 +646,10 @@ TEST_F(SdpOfferAnswerTest, ExpectAllSsrcsSpecifiedInSsrcGroupFid) {
"a=ssrc-group:FID 1 2\r\n"
"a=ssrc:1 cname:test\r\n";
auto offer = CreateSessionDescription(SdpType::kOffer, sdp);
EXPECT_FALSE(pc->SetRemoteDescription(std::move(offer)));
RTCError error;
pc->SetRemoteDescription(std::move(offer), &error);
EXPECT_FALSE(error.ok());
EXPECT_EQ(error.type(), RTCErrorType::INVALID_PARAMETER);
}
TEST_F(SdpOfferAnswerTest, ExpectAllSsrcsSpecifiedInSsrcGroupFecFr) {
@ -676,7 +679,116 @@ TEST_F(SdpOfferAnswerTest, ExpectAllSsrcsSpecifiedInSsrcGroupFecFr) {
"a=ssrc-group:FEC-FR 1 2\r\n"
"a=ssrc:1 cname:test\r\n";
auto offer = CreateSessionDescription(SdpType::kOffer, sdp);
EXPECT_FALSE(pc->SetRemoteDescription(std::move(offer)));
RTCError error;
pc->SetRemoteDescription(std::move(offer), &error);
EXPECT_FALSE(error.ok());
EXPECT_EQ(error.type(), RTCErrorType::INVALID_PARAMETER);
}
TEST_F(SdpOfferAnswerTest, ExpectTwoSsrcsInSsrcGroupFid) {
auto pc = CreatePeerConnection();
std::string sdp =
"v=0\r\n"
"o=- 0 3 IN IP4 127.0.0.1\r\n"
"s=-\r\n"
"t=0 0\r\n"
"a=group:BUNDLE 0\r\n"
"a=fingerprint:sha-1 "
"4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\r\n"
"a=setup:actpass\r\n"
"a=ice-ufrag:ETEn\r\n"
"a=ice-pwd:OtSK0WpNtpUjkY4+86js7Z/l\r\n"
"m=video 9 UDP/TLS/RTP/SAVPF 96 97\r\n"
"c=IN IP4 0.0.0.0\r\n"
"a=rtcp-mux\r\n"
"a=sendonly\r\n"
"a=mid:0\r\n"
"a=rtpmap:96 H264/90000\r\n"
"a=fmtp:96 "
"level-asymmetry-allowed=1;packetization-mode=1;profile-level-id="
"42e01f\r\n"
"a=rtpmap:97 rtx/90000\r\n"
"a=fmtp:97 apt=96\r\n"
"a=ssrc-group:FID 1 2 3\r\n"
"a=ssrc:1 cname:test\r\n"
"a=ssrc:2 cname:test\r\n"
"a=ssrc:3 cname:test\r\n";
auto offer = CreateSessionDescription(SdpType::kOffer, sdp);
RTCError error;
pc->SetRemoteDescription(std::move(offer), &error);
EXPECT_FALSE(error.ok());
EXPECT_EQ(error.type(), RTCErrorType::INVALID_PARAMETER);
}
TEST_F(SdpOfferAnswerTest, ExpectTwoSsrcsInSsrcGroupFecFr) {
auto pc = CreatePeerConnection();
std::string sdp =
"v=0\r\n"
"o=- 0 3 IN IP4 127.0.0.1\r\n"
"s=-\r\n"
"t=0 0\r\n"
"a=group:BUNDLE 0\r\n"
"a=fingerprint:sha-1 "
"4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\r\n"
"a=setup:actpass\r\n"
"a=ice-ufrag:ETEn\r\n"
"a=ice-pwd:OtSK0WpNtpUjkY4+86js7Z/l\r\n"
"m=video 9 UDP/TLS/RTP/SAVPF 96 98\r\n"
"c=IN IP4 0.0.0.0\r\n"
"a=rtcp-mux\r\n"
"a=sendonly\r\n"
"a=mid:0\r\n"
"a=rtpmap:96 H264/90000\r\n"
"a=fmtp:96 "
"level-asymmetry-allowed=1;packetization-mode=1;profile-level-id="
"42e01f\r\n"
"a=rtpmap:98 flexfec-03/90000\r\n"
"a=fmtp:98 repair-window=10000000\r\n"
"a=ssrc-group:FEC-FR 1 2 3\r\n"
"a=ssrc:1 cname:test\r\n"
"a=ssrc:2 cname:test\r\n"
"a=ssrc:3 cname:test\r\n";
auto offer = CreateSessionDescription(SdpType::kOffer, sdp);
RTCError error;
pc->SetRemoteDescription(std::move(offer), &error);
EXPECT_FALSE(error.ok());
EXPECT_EQ(error.type(), RTCErrorType::INVALID_PARAMETER);
}
TEST_F(SdpOfferAnswerTest, ExpectAtMostFourSsrcsInSsrcGroupSIM) {
auto pc = CreatePeerConnection();
std::string sdp =
"v=0\r\n"
"o=- 0 3 IN IP4 127.0.0.1\r\n"
"s=-\r\n"
"t=0 0\r\n"
"a=group:BUNDLE 0\r\n"
"a=fingerprint:sha-1 "
"4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\r\n"
"a=setup:actpass\r\n"
"a=ice-ufrag:ETEn\r\n"
"a=ice-pwd:OtSK0WpNtpUjkY4+86js7Z/l\r\n"
"m=video 9 UDP/TLS/RTP/SAVPF 96 97\r\n"
"c=IN IP4 0.0.0.0\r\n"
"a=rtcp-mux\r\n"
"a=sendonly\r\n"
"a=mid:0\r\n"
"a=rtpmap:96 H264/90000\r\n"
"a=fmtp:96 "
"level-asymmetry-allowed=1;packetization-mode=1;profile-level-id="
"42e01f\r\n"
"a=rtpmap:97 rtx/90000\r\n"
"a=fmtp:97 apt=96\r\n"
"a=ssrc-group:SIM 1 2 3 4\r\n"
"a=ssrc:1 cname:test\r\n"
"a=ssrc:2 cname:test\r\n"
"a=ssrc:3 cname:test\r\n"
"a=ssrc:4 cname:test\r\n";
auto offer = CreateSessionDescription(SdpType::kOffer, sdp);
RTCError error;
pc->SetRemoteDescription(std::move(offer), &error);
EXPECT_FALSE(error.ok());
EXPECT_EQ(error.type(), RTCErrorType::INVALID_PARAMETER);
}
TEST_F(SdpOfferAnswerTest, DuplicateSsrcsDisallowedInLocalDescription) {