Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make DTLS fragment stay within MTU size range #1156

Merged
merged 2 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* Add optional `rtcpListenInfo` in `PlainTransportOptions` ([PR #1099](https://github.com/versatica/mediasoup/pull/1099)).
* Add pause/resume API in `DataProducer` and `DataConsumer` ([PR #1104](https://github.com/versatica/mediasoup/pull/1104)).
* DataChannel subchannels feature ([PR #1152](https://github.com/versatica/mediasoup/pull/1152)).
* `Worker`: Make DTLS fragment stay within MTU size range ([PR #1156](https://github.com/versatica/mediasoup/pull/1156), based on [PR #1143](https://github.com/versatica/mediasoup/pull/1143) by @vpnts-se).


### 3.12.12
Expand Down
3 changes: 2 additions & 1 deletion worker/include/RTC/DtlsTransport.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ namespace RTC
return this->localRole;
}
void SendApplicationData(const uint8_t* data, size_t len);
// This method must be public since it's called within an OpenSSL callback.
void SendDtlsData(const uint8_t* data, size_t len);

private:
bool IsRunning() const
Expand All @@ -173,7 +175,6 @@ namespace RTC
}
void Reset();
bool CheckStatus(int returnCode);
void SendPendingOutgoingDtlsData();
bool SetTimeout();
bool ProcessHandshake();
bool CheckRemoteFingerprint();
Expand Down
75 changes: 36 additions & 39 deletions worker/src/RTC/DtlsTransport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,31 @@ inline static unsigned int onSslDtlsTimer(SSL* /*ssl*/, unsigned int timerUs)
return 2 * timerUs;
}

inline static long onSslBioOut(
BIO* bio,
int operationType,
const char* argp,
size_t len,
int /*argi*/,
long /*argl*/,
int ret,
size_t* /*processed*/)
{
long resultOfcallback = (operationType == BIO_CB_RETURN) ? static_cast<long>(ret) : 1;

if (operationType == BIO_CB_WRITE && argp && len > 0)
{
MS_DEBUG_DEV("%zu bytes of DTLS data ready to be sent", len);

RTC::DtlsTransport* dtlsTransport =
reinterpret_cast<RTC::DtlsTransport*>(BIO_get_callback_arg(bio));

dtlsTransport->SendDtlsData(reinterpret_cast<const uint8_t*>(argp), len);
}

return resultOfcallback;
}

namespace RTC
{
/* Static. */
Expand Down Expand Up @@ -633,6 +658,8 @@ namespace RTC
goto error;
}

BIO_set_callback_ex(this->sslBioToNetwork, onSslBioOut);
BIO_set_callback_arg(this->sslBioToNetwork, reinterpret_cast<char*>(this));
SSL_set_bio(this->ssl, this->sslBioFromNetwork, this->sslBioToNetwork);

// Set the MTU so that we don't send packets that are too large with no fragmentation.
Expand Down Expand Up @@ -673,7 +700,6 @@ namespace RTC
{
// Send close alert to the peer.
SSL_shutdown(this->ssl);
SendPendingOutgoingDtlsData();
}

if (this->ssl)
Expand Down Expand Up @@ -776,7 +802,6 @@ namespace RTC

SSL_set_connect_state(this->ssl);
SSL_do_handshake(this->ssl);
SendPendingOutgoingDtlsData();
SetTimeout();

break;
Expand Down Expand Up @@ -847,9 +872,6 @@ namespace RTC
// Must call SSL_read() to process received DTLS data.
read = SSL_read(this->ssl, static_cast<void*>(DtlsTransport::sslReadBuffer), SslReadBufferSize);

// Send data if it's ready.
SendPendingOutgoingDtlsData();

// Check SSL status and return if it is bad/closed.
if (!CheckStatus(read))
return;
Expand Down Expand Up @@ -910,9 +932,14 @@ namespace RTC
MS_WARN_TAG(
dtls, "OpenSSL SSL_write() wrote less (%d bytes) than given data (%zu bytes)", written, len);
}
}

// Send data.
SendPendingOutgoingDtlsData();
void DtlsTransport::SendDtlsData(const uint8_t* data, size_t len)
{
MS_TRACE();

// Notify the listener.
this->listener->OnDtlsTransportSendData(this, data, len);
}

void DtlsTransport::Reset()
Expand All @@ -929,9 +956,8 @@ namespace RTC
// Stop the DTLS timer.
this->timer->Stop();

// We need to reset the SSL instance so we need to "shutdown" it, but we
// don't want to send a Close Alert to the peer, so just don't call
// SendPendingOutgoingDTLSData().
// NOTE: We need to reset the SSL instance so we need to "shutdown" it, but we
// don't want to send a Close Alert to the peer. However this is gonna happen.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is shown to be a problem, perhaps I can write a failing test verifying the behavior. Then do some work to try to avoid sending the Close Alert and pass the test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, could you do it once this PR is merged and try that in flatbuffers branch?

NOTE: the problem I see is that we DO WANT to send DTLS Close Alert in most of the cases, and there is just one case in which we do not want to send it (this one). So no idea this can be done. Even if we set a this->allowOutgoingDtlsCloseAlert = true flag before that scenario and set it to false immediately after ignoring such a DTLS Close Alert, the problem is that this is driven by a OpenSSL callback that probably runs asynchronously.

SSL_shutdown(this->ssl);

this->localRole.reset();
Expand Down Expand Up @@ -1044,32 +1070,6 @@ namespace RTC
}
}

inline void DtlsTransport::SendPendingOutgoingDtlsData()
{
MS_TRACE();

if (BIO_eof(this->sslBioToNetwork))
return;

int64_t read;
char* data{ nullptr };

read = BIO_get_mem_data(this->sslBioToNetwork, &data); // NOLINT

if (read <= 0)
return;

MS_DEBUG_DEV("%" PRIu64 " bytes of DTLS data ready to sent to the peer", read);

// Notify the listener.
this->listener->OnDtlsTransportSendData(
this, reinterpret_cast<uint8_t*>(data), static_cast<size_t>(read));

// Clear the BIO buffer.
// NOTE: the (void) avoids the -Wunused-value warning.
(void)BIO_reset(this->sslBioToNetwork);
}

inline bool DtlsTransport::SetTimeout()
{
MS_TRACE();
Expand Down Expand Up @@ -1512,9 +1512,6 @@ namespace RTC

if (ret == 1)
{
// If required, send DTLS data.
SendPendingOutgoingDtlsData();

// Set the DTLS timer again.
SetTimeout();
}
Expand Down