blob: 038ad3683f6977711be3ea31fd1944f29bed71c5 [file] [log] [blame]
/*
* 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_TRANSMISSION_CONTROL_BLOCK_H_
#define NET_DCSCTP_SOCKET_TRANSMISSION_CONTROL_BLOCK_H_
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/functional/bind_front.h"
#include "absl/strings/string_view.h"
#include "api/task_queue/task_queue_base.h"
#include "net/dcsctp/common/sequence_numbers.h"
#include "net/dcsctp/packet/chunk/cookie_echo_chunk.h"
#include "net/dcsctp/packet/sctp_packet.h"
#include "net/dcsctp/public/dcsctp_options.h"
#include "net/dcsctp/public/dcsctp_socket.h"
#include "net/dcsctp/rx/data_tracker.h"
#include "net/dcsctp/rx/reassembly_queue.h"
#include "net/dcsctp/socket/capabilities.h"
#include "net/dcsctp/socket/context.h"
#include "net/dcsctp/socket/heartbeat_handler.h"
#include "net/dcsctp/socket/packet_sender.h"
#include "net/dcsctp/socket/stream_reset_handler.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/send_queue.h"
namespace dcsctp {
// The TransmissionControlBlock (TCB) represents an open connection to a peer,
// and holds all the resources for that. If the connection is e.g. shutdown,
// closed or restarted, this object will be deleted and/or replaced.
class TransmissionControlBlock : public Context {
public:
TransmissionControlBlock(
TimerManager& timer_manager,
absl::string_view log_prefix,
const DcSctpOptions& options,
const Capabilities& capabilities,
DcSctpSocketCallbacks& callbacks,
SendQueue& send_queue,
VerificationTag my_verification_tag,
TSN my_initial_tsn,
VerificationTag peer_verification_tag,
TSN peer_initial_tsn,
size_t a_rwnd,
TieTag tie_tag,
PacketSender& packet_sender,
std::function<bool()> is_connection_established,
const DcSctpSocketHandoverState* handover_state = nullptr)
: log_prefix_(log_prefix),
options_(options),
timer_manager_(timer_manager),
capabilities_(capabilities),
callbacks_(callbacks),
t3_rtx_(timer_manager_.CreateTimer(
"t3-rtx",
absl::bind_front(&TransmissionControlBlock::OnRtxTimerExpiry, this),
TimerOptions(options.rto_initial,
TimerBackoffAlgorithm::kExponential,
/*max_restarts=*/absl::nullopt,
options.max_timer_backoff_duration))),
delayed_ack_timer_(timer_manager_.CreateTimer(
"delayed-ack",
absl::bind_front(&TransmissionControlBlock::OnDelayedAckTimerExpiry,
this),
TimerOptions(options.delayed_ack_max_timeout,
TimerBackoffAlgorithm::kExponential,
/*max_restarts=*/0,
/*max_backoff_duration=*/absl::nullopt,
webrtc::TaskQueueBase::DelayPrecision::kHigh))),
my_verification_tag_(my_verification_tag),
my_initial_tsn_(my_initial_tsn),
peer_verification_tag_(peer_verification_tag),
peer_initial_tsn_(peer_initial_tsn),
tie_tag_(tie_tag),
is_connection_established_(std::move(is_connection_established)),
packet_sender_(packet_sender),
rto_(options),
tx_error_counter_(log_prefix, options),
data_tracker_(log_prefix,
delayed_ack_timer_.get(),
peer_initial_tsn,
handover_state),
reassembly_queue_(log_prefix,
peer_initial_tsn,
options.max_receiver_window_buffer_size,
capabilities.message_interleaving,
handover_state),
retransmission_queue_(
log_prefix,
my_initial_tsn,
a_rwnd,
send_queue,
absl::bind_front(&TransmissionControlBlock::ObserveRTT, this),
[this]() { tx_error_counter_.Clear(); },
*t3_rtx_,
options,
capabilities.partial_reliability,
capabilities.message_interleaving,
handover_state),
stream_reset_handler_(log_prefix,
this,
&timer_manager,
&data_tracker_,
&reassembly_queue_,
&retransmission_queue_,
handover_state),
heartbeat_handler_(log_prefix, options, this, &timer_manager_) {
// If the connection is re-established (peer restarted, but re-used old
// connection), make sure that all message identifiers are reset and any
// partly sent message is re-sent in full. The same is true when the socket
// is closed and later re-opened, which never happens in WebRTC, but is a
// valid operation on the SCTP level. Note that in case of handover, the
// send queue is already re-configured, and shouldn't be reset.
if (handover_state == nullptr) {
send_queue.Reset();
}
}
// Implementation of `Context`.
bool is_connection_established() const override {
return is_connection_established_();
}
TSN my_initial_tsn() const override { return my_initial_tsn_; }
TSN peer_initial_tsn() const override { return peer_initial_tsn_; }
DcSctpSocketCallbacks& callbacks() const override { return callbacks_; }
void ObserveRTT(DurationMs rtt) override;
DurationMs current_rto() const override { return rto_.rto(); }
bool IncrementTxErrorCounter(absl::string_view reason) override {
return tx_error_counter_.Increment(reason);
}
void ClearTxErrorCounter() override { tx_error_counter_.Clear(); }
SctpPacket::Builder PacketBuilder() const override {
return SctpPacket::Builder(peer_verification_tag_, options_);
}
bool HasTooManyTxErrors() const override {
return tx_error_counter_.IsExhausted();
}
void Send(SctpPacket::Builder& builder) override {
packet_sender_.Send(builder);
}
// Other accessors
DataTracker& data_tracker() { return data_tracker_; }
ReassemblyQueue& reassembly_queue() { return reassembly_queue_; }
RetransmissionQueue& retransmission_queue() { return retransmission_queue_; }
StreamResetHandler& stream_reset_handler() { return stream_reset_handler_; }
HeartbeatHandler& heartbeat_handler() { return heartbeat_handler_; }
size_t cwnd() const { return retransmission_queue_.cwnd(); }
DurationMs current_srtt() const { return rto_.srtt(); }
// Returns this socket's verification tag, set in all packet headers.
VerificationTag my_verification_tag() const { return my_verification_tag_; }
// Returns the peer's verification tag, which should be in received packets.
VerificationTag peer_verification_tag() const {
return peer_verification_tag_;
}
// All negotiated supported capabilities.
const Capabilities& capabilities() const { return capabilities_; }
// A 64-bit tie-tag, used to e.g. detect reconnections.
TieTag tie_tag() const { return tie_tag_; }
// Sends a SACK, if there is a need to.
void MaybeSendSack();
// Sends a FORWARD-TSN, if it is needed and allowed (rate-limited).
void MaybeSendForwardTsn(SctpPacket::Builder& builder, TimeMs now);
// Will be set while the socket is in kCookieEcho state. In this state, there
// can only be a single packet outstanding, and it must contain the COOKIE
// ECHO chunk as the first chunk in that packet, until the COOKIE ACK has been
// received, which will make the socket call `ClearCookieEchoChunk`.
void SetCookieEchoChunk(CookieEchoChunk chunk) {
cookie_echo_chunk_ = std::move(chunk);
}
// Called when the COOKIE ACK chunk has been received, to allow further
// packets to be sent.
void ClearCookieEchoChunk() { cookie_echo_chunk_ = absl::nullopt; }
bool has_cookie_echo_chunk() const { return cookie_echo_chunk_.has_value(); }
void MaybeSendFastRetransmit();
// Fills `builder` (which may already be filled with control chunks) with
// other control and data chunks, and sends packets as much as can be
// allowed by the congestion control algorithm.
void SendBufferedPackets(SctpPacket::Builder& builder, TimeMs now);
// As above, but without passing in a builder. If `cookie_echo_chunk_` is
// present, then only one packet will be sent, with this chunk as the first
// chunk.
void SendBufferedPackets(TimeMs now) {
SctpPacket::Builder builder(peer_verification_tag_, options_);
SendBufferedPackets(builder, now);
}
// Returns a textual representation of this object, for logging.
std::string ToString() const;
HandoverReadinessStatus GetHandoverReadiness() const;
void AddHandoverState(DcSctpSocketHandoverState& state);
private:
// Will be called when the retransmission timer (t3-rtx) expires.
absl::optional<DurationMs> OnRtxTimerExpiry();
// Will be called when the delayed ack timer expires.
absl::optional<DurationMs> OnDelayedAckTimerExpiry();
const std::string log_prefix_;
const DcSctpOptions options_;
TimerManager& timer_manager_;
// Negotiated capabilities that both peers support.
const Capabilities capabilities_;
DcSctpSocketCallbacks& callbacks_;
// The data retransmission timer, called t3-rtx in SCTP.
const std::unique_ptr<Timer> t3_rtx_;
// Delayed ack timer, which triggers when acks should be sent (when delayed).
const std::unique_ptr<Timer> delayed_ack_timer_;
const VerificationTag my_verification_tag_;
const TSN my_initial_tsn_;
const VerificationTag peer_verification_tag_;
const TSN peer_initial_tsn_;
// Nonce, used to detect reconnections.
const TieTag tie_tag_;
const std::function<bool()> is_connection_established_;
PacketSender& packet_sender_;
// Rate limiting of FORWARD-TSN. Next can be sent at or after this timestamp.
TimeMs limit_forward_tsn_until_ = TimeMs(0);
RetransmissionTimeout rto_;
RetransmissionErrorCounter tx_error_counter_;
DataTracker data_tracker_;
ReassemblyQueue reassembly_queue_;
RetransmissionQueue retransmission_queue_;
StreamResetHandler stream_reset_handler_;
HeartbeatHandler heartbeat_handler_;
// Only valid when the socket state == State::kCookieEchoed. In this state,
// the socket must wait for COOKIE ACK to continue sending any packets (not
// including a COOKIE ECHO). So if `cookie_echo_chunk_` is present, the
// SendBufferedChunks will always only just send one packet, with this chunk
// as the first chunk in the packet.
absl::optional<CookieEchoChunk> cookie_echo_chunk_ = absl::nullopt;
};
} // namespace dcsctp
#endif // NET_DCSCTP_SOCKET_TRANSMISSION_CONTROL_BLOCK_H_