diff options
Diffstat (limited to 'media/sctp/usrsctp_transport.cc')
-rw-r--r-- | media/sctp/usrsctp_transport.cc | 139 |
1 files changed, 79 insertions, 60 deletions
diff --git a/media/sctp/usrsctp_transport.cc b/media/sctp/usrsctp_transport.cc index fc226bf7ff..d43c017207 100644 --- a/media/sctp/usrsctp_transport.cc +++ b/media/sctp/usrsctp_transport.cc @@ -74,24 +74,25 @@ static constexpr size_t kSctpMtu = 1191; ABSL_CONST_INIT int g_usrsctp_usage_count = 0; ABSL_CONST_INIT bool g_usrsctp_initialized_ = false; ABSL_CONST_INIT webrtc::GlobalMutex g_usrsctp_lock_(absl::kConstInit); +ABSL_CONST_INIT char kZero[] = {'\0'}; // DataMessageType is used for the SCTP "Payload Protocol Identifier", as // defined in http://tools.ietf.org/html/rfc4960#section-14.4 // // For the list of IANA approved values see: +// https://tools.ietf.org/html/rfc8831 Sec. 8 // http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xml // The value is not used by SCTP itself. It indicates the protocol running // on top of SCTP. enum { PPID_NONE = 0, // No protocol is specified. - // Matches the PPIDs in mozilla source and - // https://datatracker.ietf.org/doc/draft-ietf-rtcweb-data-protocol Sec. 9 - // They're not yet assigned by IANA. PPID_CONTROL = 50, - PPID_BINARY_PARTIAL = 52, + PPID_TEXT_LAST = 51, + PPID_BINARY_PARTIAL = 52, // Deprecated PPID_BINARY_LAST = 53, - PPID_TEXT_PARTIAL = 54, - PPID_TEXT_LAST = 51 + PPID_TEXT_PARTIAL = 54, // Deprecated + PPID_TEXT_EMPTY = 56, + PPID_BINARY_EMPTY = 57, }; // Should only be modified by UsrSctpWrapper. @@ -128,44 +129,41 @@ void DebugSctpPrintf(const char* format, ...) { } // Get the PPID to use for the terminating fragment of this type. -uint32_t GetPpid(cricket::DataMessageType type) { +uint32_t GetPpid(webrtc::DataMessageType type, size_t size) { switch (type) { - default: - case cricket::DMT_NONE: - return PPID_NONE; - case cricket::DMT_CONTROL: + case webrtc::DataMessageType::kControl: return PPID_CONTROL; - case cricket::DMT_BINARY: - return PPID_BINARY_LAST; - case cricket::DMT_TEXT: - return PPID_TEXT_LAST; + case webrtc::DataMessageType::kBinary: + return size > 0 ? PPID_BINARY_LAST : PPID_BINARY_EMPTY; + case webrtc::DataMessageType::kText: + return size > 0 ? PPID_TEXT_LAST : PPID_TEXT_EMPTY; } } -bool GetDataMediaType(uint32_t ppid, cricket::DataMessageType* dest) { +bool GetDataMediaType(uint32_t ppid, webrtc::DataMessageType* dest) { RTC_DCHECK(dest != NULL); switch (ppid) { case PPID_BINARY_PARTIAL: case PPID_BINARY_LAST: - *dest = cricket::DMT_BINARY; + case PPID_BINARY_EMPTY: + *dest = webrtc::DataMessageType::kBinary; return true; case PPID_TEXT_PARTIAL: case PPID_TEXT_LAST: - *dest = cricket::DMT_TEXT; + case PPID_TEXT_EMPTY: + *dest = webrtc::DataMessageType::kText; return true; case PPID_CONTROL: - *dest = cricket::DMT_CONTROL; - return true; - - case PPID_NONE: - *dest = cricket::DMT_NONE; + *dest = webrtc::DataMessageType::kControl; return true; - - default: - return false; } + return false; +} + +bool IsEmptyPPID(uint32_t ppid) { + return ppid == PPID_BINARY_EMPTY || ppid == PPID_TEXT_EMPTY; } // Log the packet in text2pcap format, if log level is at LS_VERBOSE. @@ -205,11 +203,13 @@ void VerboseLogPacket(const void* data, size_t length, int direction) { // Creates the sctp_sendv_spa struct used for setting flags in the // sctp_sendv() call. -sctp_sendv_spa CreateSctpSendParams(const cricket::SendDataParams& params) { +sctp_sendv_spa CreateSctpSendParams(int sid, + const webrtc::SendDataParams& params, + size_t size) { struct sctp_sendv_spa spa = {0}; spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID; - spa.sendv_sndinfo.snd_sid = params.sid; - spa.sendv_sndinfo.snd_ppid = rtc::HostToNetwork32(GetPpid(params.type)); + spa.sendv_sndinfo.snd_sid = sid; + spa.sendv_sndinfo.snd_ppid = rtc::HostToNetwork32(GetPpid(params.type, size)); // Explicitly marking the EOR flag turns the usrsctp_sendv call below into a // non atomic operation. This means that the sctp lib might only accept the // message partially. This is done in order to improve throughput, so that we @@ -217,18 +217,22 @@ sctp_sendv_spa CreateSctpSendParams(const cricket::SendDataParams& params) { // example. spa.sendv_sndinfo.snd_flags |= SCTP_EOR; - // Ordered implies reliable. if (!params.ordered) { spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED; - if (params.max_rtx_count >= 0 || params.max_rtx_ms == 0) { - spa.sendv_flags |= SCTP_SEND_PRINFO_VALID; - spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX; - spa.sendv_prinfo.pr_value = params.max_rtx_count; - } else { - spa.sendv_flags |= SCTP_SEND_PRINFO_VALID; - spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL; - spa.sendv_prinfo.pr_value = params.max_rtx_ms; - } + } + if (params.max_rtx_count.has_value()) { + RTC_DCHECK(*params.max_rtx_count >= 0 && + *params.max_rtx_count <= std::numeric_limits<uint16_t>::max()); + spa.sendv_flags |= SCTP_SEND_PRINFO_VALID; + spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX; + spa.sendv_prinfo.pr_value = *params.max_rtx_count; + } + if (params.max_rtx_ms.has_value()) { + RTC_DCHECK(*params.max_rtx_ms >= 0 && + *params.max_rtx_ms <= std::numeric_limits<uint16_t>::max()); + spa.sendv_flags |= SCTP_SEND_PRINFO_VALID; + spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL; + spa.sendv_prinfo.pr_value = *params.max_rtx_ms; } return spa; } @@ -712,7 +716,8 @@ bool UsrsctpTransport::ResetStream(int sid) { return true; } -bool UsrsctpTransport::SendData(const SendDataParams& params, +bool UsrsctpTransport::SendData(int sid, + const webrtc::SendDataParams& params, const rtc::CopyOnWriteBuffer& payload, SendDataResult* result) { RTC_DCHECK_RUN_ON(network_thread_); @@ -727,13 +732,13 @@ bool UsrsctpTransport::SendData(const SendDataParams& params, } // Do not queue data to send on a closing stream. - auto it = stream_status_by_sid_.find(params.sid); + auto it = stream_status_by_sid_.find(sid); if (it == stream_status_by_sid_.end() || !it->second.is_open()) { RTC_LOG(LS_WARNING) << debug_name_ << "->SendData(...): " "Not sending data because sid is unknown or closing: " - << params.sid; + << sid; if (result) { *result = SDR_ERROR; } @@ -741,7 +746,7 @@ bool UsrsctpTransport::SendData(const SendDataParams& params, } size_t payload_size = payload.size(); - OutgoingMessage message(payload, params); + OutgoingMessage message(payload, sid, params); SendDataResult send_message_result = SendMessageInternal(&message); if (result) { *result = send_message_result; @@ -770,17 +775,17 @@ SendDataResult UsrsctpTransport::SendMessageInternal(OutgoingMessage* message) { RTC_LOG(LS_WARNING) << debug_name_ << "->SendMessageInternal(...): " "Not sending packet with sid=" - << message->send_params().sid - << " len=" << message->size() << " before Start()."; + << message->sid() << " len=" << message->size() + << " before Start()."; return SDR_ERROR; } - if (message->send_params().type != DMT_CONTROL) { - auto it = stream_status_by_sid_.find(message->send_params().sid); + if (message->send_params().type != webrtc::DataMessageType::kControl) { + auto it = stream_status_by_sid_.find(message->sid()); if (it == stream_status_by_sid_.end()) { RTC_LOG(LS_WARNING) << debug_name_ << "->SendMessageInternal(...): " "Not sending data because sid is unknown: " - << message->send_params().sid; + << message->sid(); return SDR_ERROR; } } @@ -792,13 +797,23 @@ SendDataResult UsrsctpTransport::SendMessageInternal(OutgoingMessage* message) { } // Send data using SCTP. - sctp_sendv_spa spa = CreateSctpSendParams(message->send_params()); + sctp_sendv_spa spa = CreateSctpSendParams( + message->sid(), message->send_params(), message->size()); + const void* data = message->data(); + size_t data_length = message->size(); + if (message->size() == 0) { + // Empty messages are replaced by a single NUL byte on the wire as SCTP + // doesn't support empty messages. + // The PPID carries the information that the payload needs to be ignored. + data = kZero; + data_length = 1; + } // Note: this send call is not atomic because the EOR bit is set. This means // that usrsctp can partially accept this message and it is our duty to buffer // the rest. - ssize_t send_res = usrsctp_sendv( - sock_, message->data(), message->size(), NULL, 0, &spa, - rtc::checked_cast<socklen_t>(sizeof(spa)), SCTP_SENDV_SPA, 0); + ssize_t send_res = usrsctp_sendv(sock_, data, data_length, NULL, 0, &spa, + rtc::checked_cast<socklen_t>(sizeof(spa)), + SCTP_SENDV_SPA, 0); if (send_res < 0) { if (errno == SCTP_EWOULDBLOCK) { ready_to_send_data_ = false; @@ -814,8 +829,9 @@ SendDataResult UsrsctpTransport::SendMessageInternal(OutgoingMessage* message) { } size_t amount_sent = static_cast<size_t>(send_res); - RTC_DCHECK_LE(amount_sent, message->size()); - message->Advance(amount_sent); + RTC_DCHECK_LE(amount_sent, data_length); + if (message->size() != 0) + message->Advance(amount_sent); // Only way out now is success. return SDR_SUCCESS; } @@ -1058,7 +1074,7 @@ bool UsrsctpTransport::SendQueuedStreamResets() { // https://w3c.github.io/webrtc-pc/#closing-procedure return stream.second.need_outgoing_reset() && (!partial_outgoing_message_.has_value() || - partial_outgoing_message_.value().send_params().sid != + partial_outgoing_message_.value().sid() != static_cast<int>(stream.first)); }; // Figure out how many streams need to be reset. We need to do this so we can @@ -1135,7 +1151,7 @@ bool UsrsctpTransport::SendBufferedMessage() { } RTC_DCHECK_EQ(0u, partial_outgoing_message_->size()); - int sid = partial_outgoing_message_->send_params().sid; + int sid = partial_outgoing_message_->sid(); partial_outgoing_message_.reset(); // Send the queued stream reset if it was pending for this stream. @@ -1291,7 +1307,7 @@ void UsrsctpTransport::OnDataOrNotificationFromSctp(const void* data, << ", eor=" << ((flags & MSG_EOR) ? "y" : "n"); // Validate payload protocol identifier - DataMessageType type = DMT_NONE; + webrtc::DataMessageType type; if (!GetDataMediaType(ppid, &type)) { // Unexpected PPID, dropping RTC_LOG(LS_ERROR) << "Received an unknown PPID " << ppid @@ -1319,9 +1335,12 @@ void UsrsctpTransport::OnDataOrNotificationFromSctp(const void* data, // association. params.seq_num = rcv.rcv_ssn; - // Append the chunk's data to the message buffer - partial_incoming_message_.AppendData(reinterpret_cast<const uint8_t*>(data), - length); + // Append the chunk's data to the message buffer unless we have a chunk with a + // PPID marking an empty message. + // See: https://tools.ietf.org/html/rfc8831#section-6.6 + if (!IsEmptyPPID(ppid)) + partial_incoming_message_.AppendData(reinterpret_cast<const uint8_t*>(data), + length); partial_params_ = params; partial_flags_ = flags; |