Create no-op DTLS if media transport is used.

We'd like to disable RTP code path when media transport is used. In particular, we don't want occasional RTP/RTCP packets sent from the RTP code path when media transport is used.

Long term we will remove this new NoOp DTLS transport, when we stop creating rtp transport.

Bug: webrtc:9719
Change-Id: I27f121edef394465ddc8fe8003e6f4428b10c022
Reviewed-on: https://webrtc-review.googlesource.com/c/117700
Reviewed-by: Anton Sukhanov <sukhanov@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Commit-Queue: Peter Slatala <psla@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26286}
This commit is contained in:
Piotr (Peter) Slatala
2019-01-16 08:25:21 -08:00
committed by Commit Bot
parent 7c03bdc1d3
commit 2b5baeec17
6 changed files with 332 additions and 48 deletions

View File

@ -43,6 +43,8 @@ rtc_static_library("rtc_p2p") {
"base/icetransportinternal.h",
"base/mdns_message.cc",
"base/mdns_message.h",
"base/no_op_dtls_transport.cc",
"base/no_op_dtls_transport.h",
"base/p2p_constants.cc",
"base/p2p_constants.h",
"base/p2p_transport_channel.cc",

View File

@ -0,0 +1,143 @@
/*
* Copyright 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <algorithm>
#include <memory>
#include <utility>
#include "p2p/base/no_op_dtls_transport.h"
#include "absl/memory/memory.h"
#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h"
#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h"
#include "logging/rtc_event_log/rtc_event_log.h"
#include "p2p/base/packet_transport_internal.h"
#include "rtc_base/buffer.h"
#include "rtc_base/checks.h"
#include "rtc_base/dscp.h"
#include "rtc_base/logging.h"
#include "rtc_base/message_queue.h"
#include "rtc_base/rtc_certificate.h"
#include "rtc_base/ssl_stream_adapter.h"
#include "rtc_base/stream.h"
#include "rtc_base/thread.h"
namespace cricket {
NoOpDtlsTransport::NoOpDtlsTransport(
std::unique_ptr<IceTransportInternal> ice_transport,
const webrtc::CryptoOptions& crypto_options)
: crypto_options_(webrtc::CryptoOptions::NoGcm()),
ice_transport_(std::move(ice_transport)) {
RTC_DCHECK(ice_transport_);
ice_transport_->SignalWritableState.connect(
this, &NoOpDtlsTransport::OnWritableState);
ice_transport_->SignalReadyToSend.connect(this,
&NoOpDtlsTransport::OnReadyToSend);
}
NoOpDtlsTransport::~NoOpDtlsTransport() {}
const webrtc::CryptoOptions& NoOpDtlsTransport::crypto_options() const {
return crypto_options_;
}
DtlsTransportState NoOpDtlsTransport::dtls_state() const {
return DTLS_TRANSPORT_CONNECTED;
}
int NoOpDtlsTransport::component() const {
return kNoOpDtlsTransportComponent;
}
bool NoOpDtlsTransport::IsDtlsActive() const {
return true;
}
bool NoOpDtlsTransport::GetDtlsRole(rtc::SSLRole* role) const {
return false;
}
bool NoOpDtlsTransport::SetDtlsRole(rtc::SSLRole role) {
return false;
}
bool NoOpDtlsTransport::GetSrtpCryptoSuite(int* cipher) {
return false;
}
bool NoOpDtlsTransport::GetSslCipherSuite(int* cipher) {
return false;
}
rtc::scoped_refptr<rtc::RTCCertificate> NoOpDtlsTransport::GetLocalCertificate()
const {
return rtc::scoped_refptr<rtc::RTCCertificate>();
}
bool NoOpDtlsTransport::SetLocalCertificate(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
return false;
}
std::unique_ptr<rtc::SSLCertChain> NoOpDtlsTransport::GetRemoteSSLCertChain()
const {
return std::unique_ptr<rtc::SSLCertChain>();
}
bool NoOpDtlsTransport::ExportKeyingMaterial(const std::string& label,
const uint8_t* context,
size_t context_len,
bool use_context,
uint8_t* result,
size_t result_len) {
return false;
}
bool NoOpDtlsTransport::SetRemoteFingerprint(const std::string& digest_alg,
const uint8_t* digest,
size_t digest_len) {
return true;
}
bool NoOpDtlsTransport::SetSslMaxProtocolVersion(
rtc::SSLProtocolVersion version) {
return true;
}
IceTransportInternal* NoOpDtlsTransport::ice_transport() {
return ice_transport_.get();
}
void NoOpDtlsTransport::OnReadyToSend(rtc::PacketTransportInternal* transport) {
RTC_DCHECK_RUN_ON(&thread_checker_);
if (is_writable_) {
SignalReadyToSend(this);
}
}
void NoOpDtlsTransport::OnWritableState(
rtc::PacketTransportInternal* transport) {
RTC_DCHECK_RUN_ON(&thread_checker_);
is_writable_ = ice_transport_->writable();
if (is_writable_) {
SignalWritableState(this);
}
}
const std::string& NoOpDtlsTransport::transport_name() const {
return ice_transport_->transport_name();
}
bool NoOpDtlsTransport::writable() const {
return ice_transport_->writable();
}
bool NoOpDtlsTransport::receiving() const {
return ice_transport_->receiving();
}
int NoOpDtlsTransport::SendPacket(const char* data,
size_t len,
const rtc::PacketOptions& options,
int flags) {
return 0;
}
int NoOpDtlsTransport::SetOption(rtc::Socket::Option opt, int value) {
return ice_transport_->SetOption(opt, value);
}
int NoOpDtlsTransport::GetError() {
return ice_transport_->GetError();
}
} // namespace cricket

View File

@ -0,0 +1,109 @@
/*
* Copyright 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef P2P_BASE_NO_OP_DTLS_TRANSPORT_H_
#define P2P_BASE_NO_OP_DTLS_TRANSPORT_H_
#include <memory>
#include <string>
#include <vector>
#include "api/crypto/crypto_options.h"
#include "p2p/base/dtls_transport_internal.h"
#include "p2p/base/ice_transport_internal.h"
#include "p2p/base/packet_transport_internal.h"
#include "rtc_base/buffer.h"
#include "rtc_base/buffer_queue.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/ssl_stream_adapter.h"
#include "rtc_base/stream.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/thread_checker.h"
namespace cricket {
constexpr int kNoOpDtlsTransportComponent = -1;
// This implementation wraps a cricket::DtlsTransport, and takes
// ownership of it.
// The implementation does not perform any operations, except of being
// "connected". The purpose of this implementation is to disable RTP transport
// while MediaTransport is used.
//
// This implementation is only temporary. Long-term we will refactor and disable
// RTP transport entirely when MediaTransport is used. Always connected (after
// ICE), no-op, dtls transport. This is used when DTLS is disabled.
//
// MaybeCreateJsepTransport controller expects DTLS connection to send a
// 'connected' signal _after_ it is created (if it is created in a connected
// state, that would not be noticed by jsep transport controller). Therefore,
// the no-op dtls transport will wait for ICE event "writable", and then
// immediately report that it's connected (emulating 0-rtt connection).
//
// We could simply not set a dtls to active (not set a certificate on the DTLS),
// and it would use an underyling connection instead.
// However, when MediaTransport is used, we want to entirely disable
// dtls/srtp/rtp, in order to avoid multiplexing issues, such as "Failed to
// unprotect RTCP packet".
class NoOpDtlsTransport : public DtlsTransportInternal {
public:
NoOpDtlsTransport(std::unique_ptr<IceTransportInternal> ice_transport,
const webrtc::CryptoOptions& crypto_options);
~NoOpDtlsTransport() override;
const webrtc::CryptoOptions& crypto_options() const override;
DtlsTransportState dtls_state() const override;
int component() const override;
bool IsDtlsActive() const override;
bool GetDtlsRole(rtc::SSLRole* role) const override;
bool SetDtlsRole(rtc::SSLRole role) override;
bool GetSrtpCryptoSuite(int* cipher) override;
bool GetSslCipherSuite(int* cipher) override;
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override;
bool SetLocalCertificate(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override;
std::unique_ptr<rtc::SSLCertChain> GetRemoteSSLCertChain() const override;
bool ExportKeyingMaterial(const std::string& label,
const uint8_t* context,
size_t context_len,
bool use_context,
uint8_t* result,
size_t result_len) override;
bool SetRemoteFingerprint(const std::string& digest_alg,
const uint8_t* digest,
size_t digest_len) override;
bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override;
IceTransportInternal* ice_transport() override;
const std::string& transport_name() const override;
bool writable() const override;
bool receiving() const override;
private:
void OnReadyToSend(rtc::PacketTransportInternal* transport);
void OnWritableState(rtc::PacketTransportInternal* transport);
int SendPacket(const char* data,
size_t len,
const rtc::PacketOptions& options,
int flags) override;
int SetOption(rtc::Socket::Option opt, int value) override;
int GetError() override;
rtc::ThreadChecker thread_checker_;
webrtc::CryptoOptions crypto_options_;
std::unique_ptr<IceTransportInternal> ice_transport_;
bool is_writable_ = false;
};
} // namespace cricket
#endif // P2P_BASE_NO_OP_DTLS_TRANSPORT_H_

View File

@ -15,6 +15,8 @@
#include <utility>
#include "absl/memory/memory.h"
#include "p2p/base/ice_transport_internal.h"
#include "p2p/base/no_op_dtls_transport.h"
#include "p2p/base/port.h"
#include "pc/srtp_filter.h"
#include "rtc_base/bind.h"
@ -409,23 +411,35 @@ void JsepTransportController::SetMediaTransportFactory(
config_.media_transport_factory = media_transport_factory;
}
std::unique_ptr<cricket::DtlsTransportInternal>
JsepTransportController::CreateDtlsTransport(const std::string& transport_name,
bool rtcp) {
RTC_DCHECK(network_thread_->IsCurrent());
std::unique_ptr<cricket::IceTransportInternal>
JsepTransportController::CreateIceTransport(const std::string transport_name,
bool rtcp) {
int component = rtcp ? cricket::ICE_CANDIDATE_COMPONENT_RTCP
: cricket::ICE_CANDIDATE_COMPONENT_RTP;
std::unique_ptr<cricket::DtlsTransportInternal> dtls;
if (config_.external_transport_factory) {
auto ice = config_.external_transport_factory->CreateIceTransport(
return config_.external_transport_factory->CreateIceTransport(
transport_name, component);
} else {
return absl::make_unique<cricket::P2PTransportChannel>(
transport_name, component, port_allocator_, async_resolver_factory_,
config_.event_log);
}
}
std::unique_ptr<cricket::DtlsTransportInternal>
JsepTransportController::CreateDtlsTransport(
std::unique_ptr<cricket::IceTransportInternal> ice) {
RTC_DCHECK(network_thread_->IsCurrent());
std::unique_ptr<cricket::DtlsTransportInternal> dtls;
if (config_.media_transport_factory) {
dtls = absl::make_unique<cricket::NoOpDtlsTransport>(
std::move(ice), config_.crypto_options);
} else if (config_.external_transport_factory) {
dtls = config_.external_transport_factory->CreateDtlsTransport(
std::move(ice), config_.crypto_options);
} else {
auto ice = absl::make_unique<cricket::P2PTransportChannel>(
transport_name, component, port_allocator_, async_resolver_factory_,
config_.event_log);
dtls = absl::make_unique<cricket::DtlsTransport>(
std::move(ice), config_.crypto_options, config_.event_log);
}
@ -1032,26 +1046,30 @@ RTCError JsepTransportController::MaybeCreateJsepTransport(
"SDES and DTLS-SRTP cannot be enabled at the same time.");
}
std::unique_ptr<cricket::IceTransportInternal> ice =
CreateIceTransport(content_info.name, /*rtcp=*/false);
std::unique_ptr<MediaTransportInterface> media_transport =
MaybeCreateMediaTransport(content_info, local, ice.get());
std::unique_ptr<cricket::DtlsTransportInternal> rtp_dtls_transport =
CreateDtlsTransport(content_info.name, /*rtcp =*/false);
CreateDtlsTransport(std::move(ice));
std::unique_ptr<cricket::DtlsTransportInternal> rtcp_dtls_transport;
std::unique_ptr<RtpTransport> unencrypted_rtp_transport;
std::unique_ptr<SrtpTransport> sdes_transport;
std::unique_ptr<DtlsSrtpTransport> dtls_srtp_transport;
std::unique_ptr<MediaTransportInterface> media_transport;
if (config_.rtcp_mux_policy !=
PeerConnectionInterface::kRtcpMuxPolicyRequire &&
content_info.type == cricket::MediaProtocolType::kRtp) {
rtcp_dtls_transport =
CreateDtlsTransport(content_info.name, /*rtcp =*/true);
RTC_DCHECK(media_transport == nullptr);
rtcp_dtls_transport = CreateDtlsTransport(
CreateIceTransport(content_info.name, /*rtcp=*/true));
}
media_transport = MaybeCreateMediaTransport(
content_info, local, rtp_dtls_transport->ice_transport());
// TODO(sukhanov): Do not create RTP/RTCP transports if media transport is
// used.
// used, and remove the no-op dtls transport when that's done.
if (config_.disable_encryption) {
unencrypted_rtp_transport = CreateUnencryptedRtpTransport(
content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());

View File

@ -295,7 +295,9 @@ class JsepTransportController : public sigslot::has_slots<> {
bool local);
std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
const std::string& transport_name,
std::unique_ptr<cricket::IceTransportInternal> ice);
std::unique_ptr<cricket::IceTransportInternal> CreateIceTransport(
const std::string transport_name,
bool rtcp);
std::unique_ptr<webrtc::RtpTransport> CreateUnencryptedRtpTransport(

View File

@ -16,6 +16,7 @@
#include "api/test/fake_media_transport.h"
#include "p2p/base/fake_dtls_transport.h"
#include "p2p/base/fake_ice_transport.h"
#include "p2p/base/no_op_dtls_transport.h"
#include "p2p/base/transport_factory_interface.h"
#include "p2p/base/transport_info.h"
#include "pc/jsep_transport_controller.h"
@ -418,10 +419,10 @@ TEST_F(JsepTransportControllerTest, GetMediaTransportInCaller) {
FakeMediaTransportFactory fake_media_transport_factory;
JsepTransportController::Config config;
config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
config.media_transport_factory = &fake_media_transport_factory;
CreateJsepTransportController(config);
auto description = CreateSessionDescriptionWithoutBundle();
auto description = CreateSessionDescriptionWithBundleGroup();
AddCryptoSettings(description.get());
EXPECT_TRUE(transport_controller_
@ -439,16 +440,20 @@ TEST_F(JsepTransportControllerTest, GetMediaTransportInCaller) {
// Return nullptr for non-existing mids.
EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2));
EXPECT_EQ(cricket::kNoOpDtlsTransportComponent,
transport_controller_->GetDtlsTransport(kAudioMid1)->component())
<< "Because media transport is used, expected no-op DTLS transport.";
}
TEST_F(JsepTransportControllerTest, GetMediaTransportInCallee) {
FakeMediaTransportFactory fake_media_transport_factory;
JsepTransportController::Config config;
config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
config.media_transport_factory = &fake_media_transport_factory;
CreateJsepTransportController(config);
auto description = CreateSessionDescriptionWithoutBundle();
auto description = CreateSessionDescriptionWithBundleGroup();
AddCryptoSettings(description.get());
EXPECT_TRUE(transport_controller_
->SetRemoteDescription(SdpType::kOffer, description.get())
@ -465,6 +470,10 @@ TEST_F(JsepTransportControllerTest, GetMediaTransportInCallee) {
// Return nullptr for non-existing mids.
EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2));
EXPECT_EQ(cricket::kNoOpDtlsTransportComponent,
transport_controller_->GetDtlsTransport(kAudioMid1)->component())
<< "Because media transport is used, expected no-op DTLS transport.";
}
TEST_F(JsepTransportControllerTest, GetMediaTransportIsNotSetIfNoSdes) {
@ -490,6 +499,10 @@ TEST_F(JsepTransportControllerTest, GetMediaTransportIsNotSetIfNoSdes) {
.ok());
EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kAudioMid1));
EXPECT_EQ(cricket::ICE_CANDIDATE_COMPONENT_RTP,
transport_controller_->GetDtlsTransport(kAudioMid1)->component())
<< "Because media transport is NOT used (fallback to RTP), expected "
"actual DTLS transport for RTP";
}
TEST_F(JsepTransportControllerTest,
@ -497,7 +510,7 @@ TEST_F(JsepTransportControllerTest,
FakeMediaTransportFactory fake_media_transport_factory;
JsepTransportController::Config config;
config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
config.media_transport_factory = &fake_media_transport_factory;
CreateJsepTransportController(config);
auto description = CreateSessionDescriptionWithoutBundle();
@ -813,25 +826,24 @@ TEST_F(JsepTransportControllerTest,
JsepTransportController::Config config;
config.media_transport_factory = &fake_media_transport_factory;
CreateJsepTransportController(config);
auto description = CreateSessionDescriptionWithoutBundle();
// Media Transport is only used with bundle.
auto description = CreateSessionDescriptionWithBundleGroup();
AddCryptoSettings(description.get());
EXPECT_TRUE(transport_controller_
->SetLocalDescription(SdpType::kOffer, description.get())
.ok());
auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid1));
auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
transport_controller_->GetDtlsTransport(kVideoMid1));
fake_audio_dtls->SetWritable(true);
fake_video_dtls->SetWritable(true);
// Decreasing connection count from 2 to 1 triggers connection state event.
fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
auto fake_audio_ice = static_cast<cricket::FakeIceTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
auto fake_video_ice = static_cast<cricket::FakeIceTransport*>(
transport_controller_->GetDtlsTransport(kVideoMid1)->ice_transport());
fake_audio_ice->SetConnectionCount(2);
fake_audio_ice->SetConnectionCount(1);
fake_video_ice->SetConnectionCount(2);
fake_video_ice->SetConnectionCount(1);
fake_audio_ice->SetWritable(true);
fake_video_ice->SetWritable(true);
// Still not connected, because we are waiting for media transport.
EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_,
@ -864,19 +876,17 @@ TEST_F(JsepTransportControllerTest,
->SetLocalDescription(SdpType::kOffer, description.get())
.ok());
auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid1));
auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
transport_controller_->GetDtlsTransport(kVideoMid1));
fake_audio_dtls->SetWritable(true);
fake_video_dtls->SetWritable(true);
auto fake_audio_ice = static_cast<cricket::FakeIceTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
auto fake_video_ice = static_cast<cricket::FakeIceTransport*>(
transport_controller_->GetDtlsTransport(kVideoMid1)->ice_transport());
fake_audio_ice->SetWritable(true);
fake_video_ice->SetWritable(true);
// Decreasing connection count from 2 to 1 triggers connection state event.
fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
fake_audio_ice->SetConnectionCount(2);
fake_audio_ice->SetConnectionCount(1);
fake_video_ice->SetConnectionCount(2);
fake_video_ice->SetConnectionCount(1);
FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
transport_controller_->GetMediaTransport(kAudioMid1));