Skip to content

Commit

Permalink
Merge branch 'paullouisageneau:master' into gjw-mbedtls
Browse files Browse the repository at this point in the history
  • Loading branch information
realprocrastinator authored Nov 17, 2023
2 parents 5c2983b + 3681569 commit aed02d0
Show file tree
Hide file tree
Showing 14 changed files with 280 additions and 80 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ set(TESTS_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/test/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/connectivity.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/negotiated.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/reliability.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/turn_connectivity.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/track.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/capi_connectivity.cpp
Expand Down
4 changes: 2 additions & 2 deletions DOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -644,8 +644,8 @@ Arguments:
- `reliability`: a structure of reliability settings containing:
- `unordered`: if `true`, the Data Channel will not enforce message ordering, else it will be ordered
- `unreliable`: if `true`, the Data Channel will not enforce strict reliability, else it will be reliable
- `maxPacketLifeTime`: if unreliable, maximum packet life time in milliseconds
- `maxRetransmits`: if unreliable and maxPacketLifeTime is 0, maximum number of retransmissions (0 means no retransmission)
- `maxPacketLifeTime`: if unreliable, time window in milliseconds during which transmissions and retransmissions may occur
- `maxRetransmits`: if unreliable and maxPacketLifeTime is 0, maximum number of attempted retransmissions (0 means no retransmission)
- `protocol` (optional): a user-defined UTF-8 string representing the Data Channel protocol, empty if NULL
- `negotiated`: if `true`, the Data Channel is assumed to be negotiated by the user and won't be negotiated by the WebRTC layer
- `manualStream`: if `true`, the Data Channel will use `stream` as stream ID, else an available id is automatically selected
Expand Down
21 changes: 18 additions & 3 deletions include/rtc/reliability.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,25 @@
namespace rtc {

struct Reliability {
enum class Type { Reliable = 0, Rexmit, Timed };

Type type = Type::Reliable;
// It true, the channel does not enforce message ordering and out-of-order delivery is allowed
bool unordered = false;

// If both maxPacketLifeTime or maxRetransmits are unset, the channel is reliable.
// If either maxPacketLifeTime or maxRetransmits is set, the channel is unreliable.
// (The settings are exclusive so both maxPacketLifetime and maxRetransmits must not be set.)

// Time window during which transmissions and retransmissions may occur
optional<std::chrono::milliseconds> maxPacketLifeTime;

// Maximum number of retransmissions that are attempted
optional<unsigned int> maxRetransmits;

// For backward compatibility, do not use
enum class Type { Reliable = 0, Rexmit, Timed };
union {
Type typeDeprecated = Type::Reliable;
[[deprecated("Use maxPacketLifeTime or maxRetransmits")]] Type type;
};
variant<int, std::chrono::milliseconds> rexmit = 0;
};

Expand Down
4 changes: 2 additions & 2 deletions include/rtc/rtc.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,8 @@ RTC_C_EXPORT int rtcReceiveMessage(int id, char *buffer, int *size);
typedef struct {
bool unordered;
bool unreliable;
int maxPacketLifeTime; // ignored if reliable
int maxRetransmits; // ignored if reliable
unsigned int maxPacketLifeTime; // ignored if reliable
unsigned int maxRetransmits; // ignored if reliable
} rtcReliability;

typedef struct {
Expand Down
4 changes: 2 additions & 2 deletions pages/content/pages/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -647,8 +647,8 @@ Arguments:
- `reliability`: a structure of reliability settings containing:
- `unordered`: if `true`, the Data Channel will not enforce message ordering, else it will be ordered
- `unreliable`: if `true`, the Data Channel will not enforce strict reliability, else it will be reliable
- `maxPacketLifeTime`: if unreliable, maximum packet life time in milliseconds
- `maxRetransmits`: if unreliable and maxPacketLifeTime is 0, maximum number of retransmissions (0 means no retransmission)
- `maxPacketLifeTime`: if unreliable, time window in milliseconds during which transmissions and retransmissions may occur
- `maxRetransmits`: if unreliable and maxPacketLifeTime is 0, maximum number of attempted retransmissions (0 means no retransmission)
- `protocol` (optional): a user-defined UTF-8 string representing the Data Channel protocol, empty if NULL
- `negotiated`: if `true`, the Data Channel is assumed to be negotiated by the user and won't be negotiated by the WebRTC layer
- `manualStream`: if `true`, the Data Channel will use `stream` as stream ID, else an available id is automatically selected
Expand Down
21 changes: 8 additions & 13 deletions src/capi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -901,15 +901,10 @@ int rtcCreateDataChannelEx(int pc, const char *label, const rtcDataChannelInit *
auto *reliability = &init->reliability;
dci.reliability.unordered = reliability->unordered;
if (reliability->unreliable) {
if (reliability->maxPacketLifeTime > 0) {
dci.reliability.type = Reliability::Type::Timed;
dci.reliability.rexmit = milliseconds(reliability->maxPacketLifeTime);
} else {
dci.reliability.type = Reliability::Type::Rexmit;
dci.reliability.rexmit = reliability->maxRetransmits;
}
} else {
dci.reliability.type = Reliability::Type::Reliable;
if (reliability->maxPacketLifeTime > 0)
dci.reliability.maxPacketLifeTime.emplace(milliseconds(reliability->maxPacketLifeTime));
else
dci.reliability.maxRetransmits.emplace(reliability->maxRetransmits);
}

dci.negotiated = init->negotiated;
Expand Down Expand Up @@ -971,12 +966,12 @@ int rtcGetDataChannelReliability(int dc, rtcReliability *reliability) {
Reliability dcr = dataChannel->reliability();
std::memset(reliability, 0, sizeof(*reliability));
reliability->unordered = dcr.unordered;
if (dcr.type == Reliability::Type::Timed) {
if(dcr.maxPacketLifeTime) {
reliability->unreliable = true;
reliability->maxPacketLifeTime = int(std::get<milliseconds>(dcr.rexmit).count());
} else if (dcr.type == Reliability::Type::Rexmit) {
reliability->maxPacketLifeTime = static_cast<unsigned int>(dcr.maxPacketLifeTime->count());
} else if (dcr.maxRetransmits) {
reliability->unreliable = true;
reliability->maxRetransmits = std::get<int>(dcr.rexmit);
reliability->maxRetransmits = *dcr.maxRetransmits;
} else {
reliability->unreliable = false;
}
Expand Down
77 changes: 56 additions & 21 deletions src/impl/datachannel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#include "logcounter.hpp"
#include "peerconnection.hpp"
#include "sctptransport.hpp"

#include "utils.hpp"
#include "rtc/datachannel.hpp"
#include "rtc/track.hpp"

Expand All @@ -28,6 +28,9 @@ using std::chrono::milliseconds;

namespace rtc::impl {

using utils::to_uint16;
using utils::to_uint32;

// Messages for the DataChannel establishment protocol (RFC 8832)
// See https://www.rfc-editor.org/rfc/rfc8832.html

Expand Down Expand Up @@ -74,8 +77,13 @@ bool DataChannel::IsOpenMessage(message_ptr message) {
DataChannel::DataChannel(weak_ptr<PeerConnection> pc, string label, string protocol,
Reliability reliability)
: mPeerConnection(pc), mLabel(std::move(label)), mProtocol(std::move(protocol)),
mReliability(std::make_shared<Reliability>(std::move(reliability))),
mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {}
mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {

if(reliability.maxPacketLifeTime && reliability.maxRetransmits)
throw std::invalid_argument("Both maxPacketLifeTime and maxRetransmits are set");

mReliability = std::make_shared<Reliability>(std::move(reliability));
}

DataChannel::~DataChannel() {
PLOG_VERBOSE << "Destroying DataChannel";
Expand Down Expand Up @@ -247,22 +255,35 @@ void OutgoingDataChannel::open(shared_ptr<SctpTransport> transport) {

uint8_t channelType;
uint32_t reliabilityParameter;
switch (mReliability->type) {
case Reliability::Type::Rexmit:
if (mReliability->maxPacketLifeTime) {
channelType = CHANNEL_PARTIAL_RELIABLE_TIMED;
reliabilityParameter = to_uint32(mReliability->maxPacketLifeTime->count());
} else if (mReliability->maxRetransmits) {
channelType = CHANNEL_PARTIAL_RELIABLE_REXMIT;
reliabilityParameter = uint32_t(std::max(std::get<int>(mReliability->rexmit), 0));
break;
reliabilityParameter = to_uint32(*mReliability->maxRetransmits);
}
// else {
// channelType = CHANNEL_RELIABLE;
// reliabilityParameter = 0;
// }
// Deprecated
else
switch (mReliability->typeDeprecated) {
case Reliability::Type::Rexmit:
channelType = CHANNEL_PARTIAL_RELIABLE_REXMIT;
reliabilityParameter = to_uint32(std::max(std::get<int>(mReliability->rexmit), 0));
break;

case Reliability::Type::Timed:
channelType = CHANNEL_PARTIAL_RELIABLE_TIMED;
reliabilityParameter = uint32_t(std::get<milliseconds>(mReliability->rexmit).count());
break;
case Reliability::Type::Timed:
channelType = CHANNEL_PARTIAL_RELIABLE_TIMED;
reliabilityParameter = to_uint32(std::get<milliseconds>(mReliability->rexmit).count());
break;

default:
channelType = CHANNEL_RELIABLE;
reliabilityParameter = 0;
break;
}
default:
channelType = CHANNEL_RELIABLE;
reliabilityParameter = 0;
break;
}

if (mReliability->unordered)
channelType |= 0x80;
Expand All @@ -274,8 +295,8 @@ void OutgoingDataChannel::open(shared_ptr<SctpTransport> transport) {
open.channelType = channelType;
open.priority = htons(0);
open.reliabilityParameter = htonl(reliabilityParameter);
open.labelLength = htons(uint16_t(mLabel.size()));
open.protocolLength = htons(uint16_t(mProtocol.size()));
open.labelLength = htons(to_uint16(mLabel.size()));
open.protocolLength = htons(to_uint16(mProtocol.size()));

auto end = reinterpret_cast<char *>(buffer.data() + sizeof(OpenMessage));
std::copy(mLabel.begin(), mLabel.end(), end);
Expand Down Expand Up @@ -329,17 +350,31 @@ void IncomingDataChannel::processOpenMessage(message_ptr message) {
mProtocol.assign(end + open.labelLength, open.protocolLength);

mReliability->unordered = (open.channelType & 0x80) != 0;
mReliability->maxPacketLifeTime.reset();
mReliability->maxRetransmits.reset();
switch (open.channelType & 0x7F) {
case CHANNEL_PARTIAL_RELIABLE_REXMIT:
mReliability->maxRetransmits.emplace(open.reliabilityParameter);
break;
case CHANNEL_PARTIAL_RELIABLE_TIMED:
mReliability->maxPacketLifeTime.emplace(milliseconds(open.reliabilityParameter));
break;
default:
break;
}

// Deprecated
switch (open.channelType & 0x7F) {
case CHANNEL_PARTIAL_RELIABLE_REXMIT:
mReliability->type = Reliability::Type::Rexmit;
mReliability->typeDeprecated = Reliability::Type::Rexmit;
mReliability->rexmit = int(open.reliabilityParameter);
break;
case CHANNEL_PARTIAL_RELIABLE_TIMED:
mReliability->type = Reliability::Type::Timed;
mReliability->typeDeprecated = Reliability::Type::Timed;
mReliability->rexmit = milliseconds(open.reliabilityParameter);
break;
default:
mReliability->type = Reliability::Type::Reliable;
mReliability->typeDeprecated = Reliability::Type::Reliable;
mReliability->rexmit = int(0);
}

Expand Down
8 changes: 6 additions & 2 deletions src/impl/peerconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,12 @@ void PeerConnection::forwardMedia([[maybe_unused]] message_ptr message) {

void PeerConnection::dispatchMedia([[maybe_unused]] message_ptr message) {
#if RTC_ENABLE_MEDIA
std::shared_lock lock(mTracksMutex); // read-only
if (mTrackLines.size()==1) {
if (auto track = mTrackLines.front().lock())
track->incoming(message);
return;
}
// Browsers like to compound their packets with a random SSRC.
// we have to do this monstrosity to distribute the report blocks
if (message->type == Message::Control) {
Expand Down Expand Up @@ -568,7 +574,6 @@ void PeerConnection::dispatchMedia([[maybe_unused]] message_ptr message) {
}

if (!ssrcs.empty()) {
std::shared_lock lock(mTracksMutex); // read-only
for (uint32_t ssrc : ssrcs) {
if (auto it = mTracksBySsrc.find(ssrc); it != mTracksBySsrc.end()) {
if (auto track = it->second.lock())
Expand All @@ -581,7 +586,6 @@ void PeerConnection::dispatchMedia([[maybe_unused]] message_ptr message) {

uint32_t ssrc = uint32_t(message->stream);

std::shared_lock lock(mTracksMutex); // read-only
if (auto it = mTracksBySsrc.find(ssrc); it != mTracksBySsrc.end()) {
if (auto track = it->second.lock())
track->incoming(message);
Expand Down
59 changes: 28 additions & 31 deletions src/impl/sctptransport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "dtlstransport.hpp"
#include "internals.hpp"
#include "logcounter.hpp"
#include "utils.hpp"

#include <algorithm>
#include <chrono>
Expand Down Expand Up @@ -50,28 +51,11 @@
using namespace std::chrono_literals;
using namespace std::chrono;

namespace {

template <typename T> uint16_t to_uint16(T i) {
if (i >= 0 && static_cast<typename std::make_unsigned<T>::type>(i) <=
std::numeric_limits<uint16_t>::max())
return static_cast<uint16_t>(i);
else
throw std::invalid_argument("Integer out of range");
}

template <typename T> uint32_t to_uint32(T i) {
if (i >= 0 && static_cast<typename std::make_unsigned<T>::type>(i) <=
std::numeric_limits<uint32_t>::max())
return static_cast<uint32_t>(i);
else
throw std::invalid_argument("Integer out of range");
}

} // namespace

namespace rtc::impl {

using utils::to_uint16;
using utils::to_uint32;

static LogCounter COUNTER_UNKNOWN_PPID(plog::warning,
"Number of SCTP packets received with an unknown PPID");

Expand Down Expand Up @@ -102,7 +86,6 @@ SctpTransport::InstancesSet *SctpTransport::Instances = new InstancesSet;

void SctpTransport::Init() {
usrsctp_init(0, SctpTransport::WriteCallback, SctpTransport::DebugCallback);
usrsctp_enable_crc32c_offload(); // We'll compute CRC32 only for outgoing packets
usrsctp_sysctl_set_sctp_pr_enable(1); // Enable Partial Reliability Extension (RFC 3758)
usrsctp_sysctl_set_sctp_ecn_enable(0); // Disable Explicit Congestion Notification
#ifdef SCTP_DEBUG
Expand Down Expand Up @@ -284,6 +267,14 @@ SctpTransport::SctpTransport(shared_ptr<Transport> lower, const Configuration &c
throw std::runtime_error("Could not disable SCTP fragmented interleave, errno=" +
std::to_string(errno));

// When using SCTP over DTLS, the data integrity is ensured by DTLS. Therefore, there's no need
// to check CRC32c additionally when receiving.
// See https://datatracker.ietf.org/doc/html/draft-ietf-tsvwg-sctp-zero-checksum
int edmid = SCTP_EDMID_LOWER_LAYER_DTLS;
if (usrsctp_setsockopt(mSock, IPPROTO_SCTP, SCTP_ACCEPT_ZERO_CHECKSUM, &edmid, sizeof(edmid)))
throw std::runtime_error("Could set socket option SCTP_ACCEPT_ZERO_CHECKSUM, errno=" +
std::to_string(errno));

int rcvBuf = 0;
socklen_t rcvBufLen = sizeof(rcvBuf);
if (usrsctp_getsockopt(mSock, SOL_SOCKET, SO_RCVBUF, &rcvBuf, &rcvBufLen))
Expand Down Expand Up @@ -380,7 +371,7 @@ bool SctpTransport::send(message_ptr message) {

PLOG_VERBOSE << "Send size=" << message->size();

if(message->size() > mMaxMessageSize)
if (message->size() > mMaxMessageSize)
throw std::invalid_argument("Message is too large");

// Flush the queue, and if nothing is pending, try to send directly
Expand Down Expand Up @@ -515,7 +506,7 @@ void SctpTransport::doRecv() {
} else {
// SCTP message
mPartialMessage.insert(mPartialMessage.end(), buffer, buffer + len);
if(mPartialMessage.size() > mMaxMessageSize) {
if (mPartialMessage.size() > mMaxMessageSize) {
PLOG_WARNING << "SCTP message is too large, truncating it";
mPartialMessage.resize(mMaxMessageSize);
}
Expand Down Expand Up @@ -639,7 +630,20 @@ bool SctpTransport::trySendMessage(message_ptr message) {
if (reliability.unordered)
spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED;

switch (reliability.type) {
if (reliability.maxPacketLifeTime) {
spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
spa.sendv_prinfo.pr_value = to_uint32(reliability.maxPacketLifeTime->count());
} else if (reliability.maxRetransmits) {
spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
spa.sendv_prinfo.pr_value = to_uint32(*reliability.maxRetransmits);
}
// else {
// spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_NONE;
// }
// Deprecated
else switch (reliability.typeDeprecated) {
case Reliability::Type::Rexmit:
spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
Expand Down Expand Up @@ -960,13 +964,6 @@ void SctpTransport::UpcallCallback(struct socket *, void *arg, int /* flags */)
int SctpTransport::WriteCallback(void *ptr, void *data, size_t len, uint8_t tos, uint8_t set_df) {
auto *transport = static_cast<SctpTransport *>(ptr);

// Set the CRC32 ourselves as we have enabled CRC32 offloading
if (len >= 12) {
uint32_t *checksum = reinterpret_cast<uint32_t *>(data) + 2;
*checksum = 0;
*checksum = usrsctp_crc32c(data, len);
}

// Workaround for sctplab/usrsctp#405: Send callback is invoked on already closed socket
// https://github.com/sctplab/usrsctp/issues/405
if (auto locked = Instances->lock(transport))
Expand Down
Loading

0 comments on commit aed02d0

Please sign in to comment.