webrtc_m130/media/sctp/sctp_transport.h
Seth Hampson 66d6c3b70b Buffers non atomic message send with usrsctp lib.
Currently we set the EOR bit when sending a message through the sctp
library. This makes the send non atomic, meaning that message can be
partially accepted by the sctp socket. Currently we ignore the sent
amount result, but this change now checks that result and buffers the
remaining message to be sent later in the case that it was only
partially accepted by usrsctp.

Bug: webrtc:10922
Change-Id: I9ff563c40e2b7dbdeb19b40d07c43a15ff7c9b49
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/150562
Commit-Queue: Seth Hampson <shampson@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Amit Hilbuch <amithi@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29051}
2019-09-03 16:30:21 +00:00

290 lines
11 KiB
C++

/*
* Copyright (c) 2012 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 MEDIA_SCTP_SCTP_TRANSPORT_H_
#define MEDIA_SCTP_SCTP_TRANSPORT_H_
#include <errno.h>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "absl/types/optional.h"
#include "rtc_base/async_invoker.h"
#include "rtc_base/buffer.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/copy_on_write_buffer.h"
#include "rtc_base/third_party/sigslot/sigslot.h"
#include "rtc_base/thread.h"
// For SendDataParams/ReceiveDataParams.
#include "media/base/media_channel.h"
#include "media/sctp/sctp_transport_internal.h"
// Defined by "usrsctplib/usrsctp.h"
struct sockaddr_conn;
struct sctp_assoc_change;
struct sctp_stream_reset_event;
struct sctp_sendv_spa;
// Defined by <sys/socket.h>
struct socket;
namespace cricket {
// Holds data to be passed on to a transport.
struct SctpInboundPacket;
// From transport calls, data flows like this:
// [network thread (although it can in princple be another thread)]
// 1. SctpTransport::SendData(data)
// 2. usrsctp_sendv(data)
// [network thread returns; sctp thread then calls the following]
// 3. OnSctpOutboundPacket(wrapped_data)
// [sctp thread returns having async invoked on the network thread]
// 4. SctpTransport::OnPacketFromSctpToNetwork(wrapped_data)
// 5. DtlsTransport::SendPacket(wrapped_data)
// 6. ... across network ... a packet is sent back ...
// 7. SctpTransport::OnPacketReceived(wrapped_data)
// 8. usrsctp_conninput(wrapped_data)
// [network thread returns; sctp thread then calls the following]
// 9. OnSctpInboundData(data)
// [sctp thread returns having async invoked on the network thread]
// 10. SctpTransport::OnInboundPacketFromSctpToTransport(inboundpacket)
// 11. SctpTransport::OnDataFromSctpToTransport(data)
// 12. SctpTransport::SignalDataReceived(data)
// [from the same thread, methods registered/connected to
// SctpTransport are called with the recieved data]
class SctpTransport : public SctpTransportInternal,
public sigslot::has_slots<> {
public:
// |network_thread| is where packets will be processed and callbacks from
// this transport will be posted, and is the only thread on which public
// methods can be called.
// |transport| is not required (can be null).
SctpTransport(rtc::Thread* network_thread,
rtc::PacketTransportInternal* transport);
~SctpTransport() override;
// SctpTransportInternal overrides (see sctptransportinternal.h for comments).
void SetDtlsTransport(rtc::PacketTransportInternal* transport) override;
bool Start(int local_port, int remote_port, int max_message_size) override;
bool OpenStream(int sid) override;
bool ResetStream(int sid) override;
bool SendData(const SendDataParams& params,
const rtc::CopyOnWriteBuffer& payload,
SendDataResult* result = nullptr) override;
bool ReadyToSendData() override;
int max_message_size() const override { return max_message_size_; }
absl::optional<int> max_outbound_streams() const override {
return max_outbound_streams_;
}
absl::optional<int> max_inbound_streams() const override {
return max_inbound_streams_;
}
void set_debug_name_for_testing(const char* debug_name) override {
debug_name_ = debug_name;
}
// Exposed to allow Post call from c-callbacks.
// TODO(deadbeef): Remove this or at least make it return a const pointer.
rtc::Thread* network_thread() const { return network_thread_; }
private:
// A message to be sent by the sctp library. This class is used to track the
// progress of writing a single message to the sctp library in the presence of
// partial writes. In this case, the Advance() function is provided in order
// to advance over what has already been accepted by the sctp library and
// avoid copying the remaining partial message buffer.
class OutgoingMessage {
public:
OutgoingMessage(const rtc::CopyOnWriteBuffer& buffer,
const SendDataParams& send_params)
: buffer_(buffer), send_params_(send_params) {}
// Advances the buffer by the incremented amount. Must not advance further
// than the current data size.
void Advance(size_t increment) {
RTC_DCHECK_LE(increment + offset_, buffer_.size());
offset_ += increment;
}
size_t size() const { return buffer_.size() - offset_; }
const void* data() const { return buffer_.data() + offset_; }
SendDataParams send_params() const { return send_params_; }
private:
const rtc::CopyOnWriteBuffer buffer_;
const SendDataParams send_params_;
size_t offset_ = 0;
};
void ConnectTransportSignals();
void DisconnectTransportSignals();
// Creates the socket and connects.
bool Connect();
// Returns false when opening the socket failed.
bool OpenSctpSocket();
// Helpet method to set socket options.
bool ConfigureSctpSocket();
// Sets |sock_ |to nullptr.
void CloseSctpSocket();
// Sends a SCTP_RESET_STREAM for all streams in closing_ssids_.
bool SendQueuedStreamResets();
// Sets the "ready to send" flag and fires signal if needed.
void SetReadyToSendData();
// Sends the outgoing buffered message that was only partially accepted by the
// sctp lib because it did not have enough space. Returns true if the entire
// buffered message was accepted by the sctp lib.
bool SendBufferedMessage();
// Tries to send the |payload| on the usrsctp lib. The message will be
// advanced by the amount that was sent.
SendDataResult SendMessageInternal(OutgoingMessage* message);
// Callbacks from DTLS transport.
void OnWritableState(rtc::PacketTransportInternal* transport);
virtual void OnPacketRead(rtc::PacketTransportInternal* transport,
const char* data,
size_t len,
const int64_t& packet_time_us,
int flags);
// Methods related to usrsctp callbacks.
void OnSendThresholdCallback();
sockaddr_conn GetSctpSockAddr(int port);
// Called using |invoker_| to send packet on the network.
void OnPacketFromSctpToNetwork(const rtc::CopyOnWriteBuffer& buffer);
// Called using |invoker_| to decide what to do with the packet.
// The |flags| parameter is used by SCTP to distinguish notification packets
// from other types of packets.
void OnInboundPacketFromSctpToTransport(const rtc::CopyOnWriteBuffer& buffer,
ReceiveDataParams params,
int flags);
void OnDataFromSctpToTransport(const ReceiveDataParams& params,
const rtc::CopyOnWriteBuffer& buffer);
void OnNotificationFromSctp(const rtc::CopyOnWriteBuffer& buffer);
void OnNotificationAssocChange(const sctp_assoc_change& change);
void OnStreamResetEvent(const struct sctp_stream_reset_event* evt);
// Responsible for marshalling incoming data to the transports listeners, and
// outgoing data to the network interface.
rtc::Thread* network_thread_;
// Helps pass inbound/outbound packets asynchronously to the network thread.
rtc::AsyncInvoker invoker_;
// Underlying DTLS transport.
rtc::PacketTransportInternal* transport_ = nullptr;
// Track the data received from usrsctp between callbacks until the EOR bit
// arrives.
rtc::CopyOnWriteBuffer partial_incoming_message_;
ReceiveDataParams partial_params_;
int partial_flags_;
// A message that was attempted to be sent, but was only partially accepted by
// usrsctp lib with usrsctp_sendv() because it cannot buffer the full message.
// This occurs because we explicitly set the EOR bit when sending, so
// usrsctp_sendv() is not atomic.
absl::optional<OutgoingMessage> partial_outgoing_message_;
bool was_ever_writable_ = false;
int local_port_ = kSctpDefaultPort;
int remote_port_ = kSctpDefaultPort;
int max_message_size_ = kSctpSendBufferSize;
struct socket* sock_ = nullptr; // The socket created by usrsctp_socket(...).
// Has Start been called? Don't create SCTP socket until it has.
bool started_ = false;
// Are we ready to queue data (SCTP socket created, and not blocked due to
// congestion control)? Different than |transport_|'s "ready to send".
bool ready_to_send_data_ = false;
// Used to keep track of the status of each stream (or rather, each pair of
// incoming/outgoing streams with matching IDs). It's specifically used to
// keep track of the status of resets, but more information could be put here
// later.
//
// See datachannel.h for a summary of the closing procedure.
struct StreamStatus {
// Closure initiated by application via ResetStream? Note that
// this may be true while outgoing_reset_initiated is false if the outgoing
// reset needed to be queued.
bool closure_initiated = false;
// Whether we've initiated the outgoing stream reset via
// SCTP_RESET_STREAMS.
bool outgoing_reset_initiated = false;
// Whether usrsctp has indicated that the incoming/outgoing streams have
// been reset. It's expected that the peer will reset its outgoing stream
// (our incoming stream) after receiving the reset for our outgoing stream,
// though older versions of chromium won't do this. See crbug.com/559394
// for context.
bool outgoing_reset_complete = false;
bool incoming_reset_complete = false;
// Some helper methods to improve code readability.
bool is_open() const {
return !closure_initiated && !incoming_reset_complete &&
!outgoing_reset_complete;
}
// We need to send an outgoing reset if the application has closed the data
// channel, or if we received a reset of the incoming stream from the
// remote endpoint, indicating the data channel was closed remotely.
bool need_outgoing_reset() const {
return (incoming_reset_complete || closure_initiated) &&
!outgoing_reset_initiated;
}
bool reset_complete() const {
return outgoing_reset_complete && incoming_reset_complete;
}
};
// Entries should only be removed from this map if |reset_complete| is
// true.
std::map<uint32_t, StreamStatus> stream_status_by_sid_;
// A static human-readable name for debugging messages.
const char* debug_name_ = "SctpTransport";
// Hides usrsctp interactions from this header file.
class UsrSctpWrapper;
// Number of channels negotiated. Not set before negotiation completes.
absl::optional<int> max_outbound_streams_;
absl::optional<int> max_inbound_streams_;
RTC_DISALLOW_COPY_AND_ASSIGN(SctpTransport);
};
class SctpTransportFactory : public SctpTransportInternalFactory {
public:
explicit SctpTransportFactory(rtc::Thread* network_thread)
: network_thread_(network_thread) {}
std::unique_ptr<SctpTransportInternal> CreateSctpTransport(
rtc::PacketTransportInternal* transport) override {
return std::unique_ptr<SctpTransportInternal>(
new SctpTransport(network_thread_, transport));
}
private:
rtc::Thread* network_thread_;
};
} // namespace cricket
#endif // MEDIA_SCTP_SCTP_TRANSPORT_H_