| /* |
| * Copyright (c) 2021 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 NET_DCSCTP_SOCKET_DCSCTP_SOCKET_H_ |
| #define NET_DCSCTP_SOCKET_DCSCTP_SOCKET_H_ |
| |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/strings/string_view.h" |
| #include "api/array_view.h" |
| #include "api/sequence_checker.h" |
| #include "net/dcsctp/packet/chunk/abort_chunk.h" |
| #include "net/dcsctp/packet/chunk/chunk.h" |
| #include "net/dcsctp/packet/chunk/cookie_ack_chunk.h" |
| #include "net/dcsctp/packet/chunk/cookie_echo_chunk.h" |
| #include "net/dcsctp/packet/chunk/data_chunk.h" |
| #include "net/dcsctp/packet/chunk/data_common.h" |
| #include "net/dcsctp/packet/chunk/error_chunk.h" |
| #include "net/dcsctp/packet/chunk/forward_tsn_chunk.h" |
| #include "net/dcsctp/packet/chunk/forward_tsn_common.h" |
| #include "net/dcsctp/packet/chunk/heartbeat_ack_chunk.h" |
| #include "net/dcsctp/packet/chunk/heartbeat_request_chunk.h" |
| #include "net/dcsctp/packet/chunk/idata_chunk.h" |
| #include "net/dcsctp/packet/chunk/iforward_tsn_chunk.h" |
| #include "net/dcsctp/packet/chunk/init_ack_chunk.h" |
| #include "net/dcsctp/packet/chunk/init_chunk.h" |
| #include "net/dcsctp/packet/chunk/reconfig_chunk.h" |
| #include "net/dcsctp/packet/chunk/sack_chunk.h" |
| #include "net/dcsctp/packet/chunk/shutdown_ack_chunk.h" |
| #include "net/dcsctp/packet/chunk/shutdown_chunk.h" |
| #include "net/dcsctp/packet/chunk/shutdown_complete_chunk.h" |
| #include "net/dcsctp/packet/data.h" |
| #include "net/dcsctp/packet/sctp_packet.h" |
| #include "net/dcsctp/public/dcsctp_message.h" |
| #include "net/dcsctp/public/dcsctp_options.h" |
| #include "net/dcsctp/public/dcsctp_socket.h" |
| #include "net/dcsctp/public/packet_observer.h" |
| #include "net/dcsctp/rx/data_tracker.h" |
| #include "net/dcsctp/rx/reassembly_queue.h" |
| #include "net/dcsctp/socket/callback_deferrer.h" |
| #include "net/dcsctp/socket/packet_sender.h" |
| #include "net/dcsctp/socket/state_cookie.h" |
| #include "net/dcsctp/socket/transmission_control_block.h" |
| #include "net/dcsctp/timer/timer.h" |
| #include "net/dcsctp/tx/retransmission_error_counter.h" |
| #include "net/dcsctp/tx/retransmission_queue.h" |
| #include "net/dcsctp/tx/retransmission_timeout.h" |
| #include "net/dcsctp/tx/rr_send_queue.h" |
| |
| namespace dcsctp { |
| |
| // DcSctpSocket represents a single SCTP socket, to be used over DTLS. |
| // |
| // Every dcSCTP is completely isolated from any other socket. |
| // |
| // This class manages all packet and chunk dispatching and mainly handles the |
| // connection sequences (connect, close, shutdown, etc) as well as managing |
| // the Transmission Control Block (tcb). |
| // |
| // This class is thread-compatible. |
| class DcSctpSocket : public DcSctpSocketInterface { |
| public: |
| // Instantiates a DcSctpSocket, which interacts with the world through the |
| // `callbacks` interface and is configured using `options`. |
| // |
| // For debugging, `log_prefix` will prefix all debug logs, and a |
| // `packet_observer` can be attached to e.g. dump sent and received packets. |
| DcSctpSocket(absl::string_view log_prefix, |
| DcSctpSocketCallbacks& callbacks, |
| std::unique_ptr<PacketObserver> packet_observer, |
| const DcSctpOptions& options); |
| |
| DcSctpSocket(const DcSctpSocket&) = delete; |
| DcSctpSocket& operator=(const DcSctpSocket&) = delete; |
| |
| // Implementation of `DcSctpSocketInterface`. |
| void ReceivePacket(rtc::ArrayView<const uint8_t> data) override; |
| void HandleTimeout(TimeoutID timeout_id) override; |
| void Connect() override; |
| void RestoreFromState(const DcSctpSocketHandoverState& state) override; |
| void Shutdown() override; |
| void Close() override; |
| SendStatus Send(DcSctpMessage message, |
| const SendOptions& send_options) override; |
| ResetStreamsStatus ResetStreams( |
| rtc::ArrayView<const StreamID> outgoing_streams) override; |
| SocketState state() const override; |
| const DcSctpOptions& options() const override { return options_; } |
| void SetMaxMessageSize(size_t max_message_size) override; |
| size_t buffered_amount(StreamID stream_id) const override; |
| size_t buffered_amount_low_threshold(StreamID stream_id) const override; |
| void SetBufferedAmountLowThreshold(StreamID stream_id, size_t bytes) override; |
| Metrics GetMetrics() const override; |
| HandoverReadinessStatus GetHandoverReadiness() const override; |
| absl::optional<DcSctpSocketHandoverState> GetHandoverStateAndClose() override; |
| SctpImplementation peer_implementation() const override { |
| return peer_implementation_; |
| } |
| // Returns this socket's verification tag, or zero if not yet connected. |
| VerificationTag verification_tag() const { |
| return tcb_ != nullptr ? tcb_->my_verification_tag() : VerificationTag(0); |
| } |
| |
| private: |
| // Parameter proposals valid during the connect phase. |
| struct ConnectParameters { |
| TSN initial_tsn = TSN(0); |
| VerificationTag verification_tag = VerificationTag(0); |
| }; |
| |
| // Detailed state (separate from SocketState, which is the public state). |
| enum class State { |
| kClosed, |
| kCookieWait, |
| // TCB valid in these: |
| kCookieEchoed, |
| kEstablished, |
| kShutdownPending, |
| kShutdownSent, |
| kShutdownReceived, |
| kShutdownAckSent, |
| }; |
| |
| // Returns the log prefix used for debug logging. |
| std::string log_prefix() const; |
| |
| bool IsConsistent() const; |
| static constexpr absl::string_view ToString(DcSctpSocket::State state); |
| |
| // Changes the socket state, given a `reason` (for debugging/logging). |
| void SetState(State state, absl::string_view reason); |
| // Fills in `connect_params` with random verification tag and initial TSN. |
| void MakeConnectionParameters(); |
| // Closes the association. Note that the TCB will not be valid past this call. |
| void InternalClose(ErrorKind error, absl::string_view message); |
| // Closes the association, because of too many retransmission errors. |
| void CloseConnectionBecauseOfTooManyTransmissionErrors(); |
| // Timer expiration handlers |
| absl::optional<DurationMs> OnInitTimerExpiry(); |
| absl::optional<DurationMs> OnCookieTimerExpiry(); |
| absl::optional<DurationMs> OnShutdownTimerExpiry(); |
| void OnSentPacket(rtc::ArrayView<const uint8_t> packet, |
| SendPacketStatus status); |
| // Sends SHUTDOWN or SHUTDOWN-ACK if the socket is shutting down and if all |
| // outstanding data has been acknowledged. |
| void MaybeSendShutdownOrAck(); |
| // If the socket is shutting down, responds SHUTDOWN to any incoming DATA. |
| void MaybeSendShutdownOnPacketReceived(const SctpPacket& packet); |
| // Sends a INIT chunk. |
| void SendInit(); |
| // Sends a SHUTDOWN chunk. |
| void SendShutdown(); |
| // Sends a SHUTDOWN-ACK chunk. |
| void SendShutdownAck(); |
| // Validates the SCTP packet, as a whole - not the validity of individual |
| // chunks within it, as that's done in the different chunk handlers. |
| bool ValidatePacket(const SctpPacket& packet); |
| // Parses `payload`, which is a serialized packet that is just going to be |
| // sent and prints all chunks. |
| void DebugPrintOutgoing(rtc::ArrayView<const uint8_t> payload); |
| // Called whenever there may be reassembled messages, and delivers those. |
| void DeliverReassembledMessages(); |
| // Returns true if there is a TCB, and false otherwise (and reports an error). |
| bool ValidateHasTCB(); |
| |
| // Returns true if the parsing of a chunk of type `T` succeeded. If it didn't, |
| // it reports an error and returns false. |
| template <class T> |
| bool ValidateParseSuccess(const absl::optional<T>& c) { |
| if (c.has_value()) { |
| return true; |
| } |
| |
| ReportFailedToParseChunk(T::kType); |
| return false; |
| } |
| |
| // Reports failing to have parsed a chunk with the provided `chunk_type`. |
| void ReportFailedToParseChunk(int chunk_type); |
| // Called when unknown chunks are received. May report an error. |
| bool HandleUnrecognizedChunk(const SctpPacket::ChunkDescriptor& descriptor); |
| |
| // Will dispatch more specific chunk handlers. |
| bool Dispatch(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles incoming DATA chunks. |
| void HandleData(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles incoming I-DATA chunks. |
| void HandleIData(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Common handler for DATA and I-DATA chunks. |
| void HandleDataCommon(AnyDataChunk& chunk); |
| // Handles incoming INIT chunks. |
| void HandleInit(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles incoming INIT-ACK chunks. |
| void HandleInitAck(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles incoming SACK chunks. |
| void HandleSack(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles incoming HEARTBEAT chunks. |
| void HandleHeartbeatRequest(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles incoming HEARTBEAT-ACK chunks. |
| void HandleHeartbeatAck(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles incoming ABORT chunks. |
| void HandleAbort(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles incoming ERROR chunks. |
| void HandleError(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles incoming COOKIE-ECHO chunks. |
| void HandleCookieEcho(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles receiving COOKIE-ECHO when there already is a TCB. The return value |
| // indicates if the processing should continue. |
| bool HandleCookieEchoWithTCB(const CommonHeader& header, |
| const StateCookie& cookie); |
| // Handles incoming COOKIE-ACK chunks. |
| void HandleCookieAck(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles incoming SHUTDOWN chunks. |
| void HandleShutdown(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles incoming SHUTDOWN-ACK chunks. |
| void HandleShutdownAck(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles incoming FORWARD-TSN chunks. |
| void HandleForwardTsn(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles incoming I-FORWARD-TSN chunks. |
| void HandleIForwardTsn(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Handles incoming RE-CONFIG chunks. |
| void HandleReconfig(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| // Common handled for FORWARD-TSN/I-FORWARD-TSN. |
| void HandleForwardTsnCommon(const AnyForwardTsnChunk& chunk); |
| // Handles incoming SHUTDOWN-COMPLETE chunks |
| void HandleShutdownComplete(const CommonHeader& header, |
| const SctpPacket::ChunkDescriptor& descriptor); |
| |
| const std::string log_prefix_; |
| const std::unique_ptr<PacketObserver> packet_observer_; |
| RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker thread_checker_; |
| Metrics metrics_; |
| DcSctpOptions options_; |
| |
| // Enqueues callbacks and dispatches them just before returning to the caller. |
| CallbackDeferrer callbacks_; |
| |
| TimerManager timer_manager_; |
| const std::unique_ptr<Timer> t1_init_; |
| const std::unique_ptr<Timer> t1_cookie_; |
| const std::unique_ptr<Timer> t2_shutdown_; |
| |
| // Packets that failed to be sent, but should be retried. |
| PacketSender packet_sender_; |
| |
| // The actual SendQueue implementation. As data can be sent on a socket before |
| // the connection is established, this component is not in the TCB. |
| RRSendQueue send_queue_; |
| |
| // Contains verification tag and initial TSN between having sent the INIT |
| // until the connection is established (there is no TCB at this point). |
| ConnectParameters connect_params_; |
| // The socket state. |
| State state_ = State::kClosed; |
| // If the connection is established, contains a transmission control block. |
| std::unique_ptr<TransmissionControlBlock> tcb_; |
| |
| SctpImplementation peer_implementation_ = SctpImplementation::kUnknown; |
| }; |
| } // namespace dcsctp |
| |
| #endif // NET_DCSCTP_SOCKET_DCSCTP_SOCKET_H_ |