289 lines
12 KiB
C++
289 lines
12 KiB
C++
/*
|
|
* 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_
|