/* * 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 #include #include #include #include #include #include #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_rcvinfo; struct sctp_stream_reset_event; struct sctp_sendv_spa; // Defined by 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) // 10. SctpTransport::OnDataFromSctpToTransport(data) // [sctp thread returns having async invoked on the network thread] // 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 max_outbound_streams() const override { return max_outbound_streams_; } absl::optional max_inbound_streams() const override { return max_inbound_streams_; } void set_debug_name_for_testing(const char* debug_name) override { debug_name_ = debug_name; } int InjectDataOrNotificationFromSctpForTesting(void* data, size_t length, struct sctp_rcvinfo rcv, int flags); // 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); void OnClosed(rtc::PacketTransportInternal* transport); // 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 on the SCTP thread. // Flags are standard socket API flags (RFC 6458). int OnDataOrNotificationFromSctp(void* data, size_t length, struct sctp_rcvinfo rcv, int flags); // Called using |invoker_| to decide what to do with the data. void OnDataFromSctpToTransport(const ReceiveDataParams& params, const rtc::CopyOnWriteBuffer& buffer); // Called using |invoker_| to decide what to do with the notification. 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 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 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 max_outbound_streams_; absl::optional max_inbound_streams_; // Used for associating this transport with the underlying sctp socket in // various callbacks. uintptr_t id_ = 0; RTC_DISALLOW_COPY_AND_ASSIGN(SctpTransport); }; class SctpTransportFactory : public SctpTransportInternalFactory { public: explicit SctpTransportFactory(rtc::Thread* network_thread) : network_thread_(network_thread) {} std::unique_ptr CreateSctpTransport( rtc::PacketTransportInternal* transport) override { return std::unique_ptr( new SctpTransport(network_thread_, transport)); } private: rtc::Thread* network_thread_; }; } // namespace cricket #endif // MEDIA_SCTP_SCTP_TRANSPORT_H_