mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 05:40:42 +01:00

While this is a fairly big CL, it's fairly straightforward. It replaces uses of TimeMs with webrtc::Timestamp, and additionally does some cleanup of DurationMs to webrtc::TimeDelta that are now easier to do. Bug: webrtc:15593 Change-Id: Id0e3edcba0533e0e8df3358b1778b6995c344243 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/326560 Reviewed-by: Florent Castelli <orphis@webrtc.org> Commit-Queue: Victor Boivie <boivie@webrtc.org> Cr-Commit-Position: refs/heads/main@{#41138}
201 lines
7.2 KiB
C++
201 lines
7.2 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.
|
|
*/
|
|
#include "net/dcsctp/socket/heartbeat_handler.h"
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "absl/functional/bind_front.h"
|
|
#include "absl/strings/string_view.h"
|
|
#include "absl/types/optional.h"
|
|
#include "api/array_view.h"
|
|
#include "api/units/time_delta.h"
|
|
#include "net/dcsctp/packet/bounded_byte_reader.h"
|
|
#include "net/dcsctp/packet/bounded_byte_writer.h"
|
|
#include "net/dcsctp/packet/chunk/heartbeat_ack_chunk.h"
|
|
#include "net/dcsctp/packet/chunk/heartbeat_request_chunk.h"
|
|
#include "net/dcsctp/packet/parameter/heartbeat_info_parameter.h"
|
|
#include "net/dcsctp/packet/parameter/parameter.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/socket/context.h"
|
|
#include "net/dcsctp/timer/timer.h"
|
|
#include "rtc_base/logging.h"
|
|
|
|
namespace dcsctp {
|
|
using ::webrtc::TimeDelta;
|
|
using ::webrtc::Timestamp;
|
|
|
|
// This is stored (in serialized form) as HeartbeatInfoParameter sent in
|
|
// HeartbeatRequestChunk and received back in HeartbeatAckChunk. It should be
|
|
// well understood that this data may be modified by the peer, so it can't
|
|
// be trusted.
|
|
//
|
|
// It currently only stores a timestamp, in millisecond precision, to allow for
|
|
// RTT measurements. If that would be manipulated by the peer, it would just
|
|
// result in incorrect RTT measurements, which isn't an issue.
|
|
class HeartbeatInfo {
|
|
public:
|
|
static constexpr size_t kBufferSize = sizeof(uint64_t);
|
|
static_assert(kBufferSize == 8, "Unexpected buffer size");
|
|
|
|
explicit HeartbeatInfo(Timestamp created_at) : created_at_(created_at) {}
|
|
|
|
std::vector<uint8_t> Serialize() {
|
|
uint32_t high_bits = static_cast<uint32_t>(created_at_.ms() >> 32);
|
|
uint32_t low_bits = static_cast<uint32_t>(created_at_.ms());
|
|
|
|
std::vector<uint8_t> data(kBufferSize);
|
|
BoundedByteWriter<kBufferSize> writer(data);
|
|
writer.Store32<0>(high_bits);
|
|
writer.Store32<4>(low_bits);
|
|
return data;
|
|
}
|
|
|
|
static absl::optional<HeartbeatInfo> Deserialize(
|
|
rtc::ArrayView<const uint8_t> data) {
|
|
if (data.size() != kBufferSize) {
|
|
RTC_LOG(LS_WARNING) << "Invalid heartbeat info: " << data.size()
|
|
<< " bytes";
|
|
return absl::nullopt;
|
|
}
|
|
|
|
BoundedByteReader<kBufferSize> reader(data);
|
|
uint32_t high_bits = reader.Load32<0>();
|
|
uint32_t low_bits = reader.Load32<4>();
|
|
|
|
uint64_t created_at = static_cast<uint64_t>(high_bits) << 32 | low_bits;
|
|
return HeartbeatInfo(Timestamp::Millis(created_at));
|
|
}
|
|
|
|
Timestamp created_at() const { return created_at_; }
|
|
|
|
private:
|
|
const Timestamp created_at_;
|
|
};
|
|
|
|
HeartbeatHandler::HeartbeatHandler(absl::string_view log_prefix,
|
|
const DcSctpOptions& options,
|
|
Context* context,
|
|
TimerManager* timer_manager)
|
|
: log_prefix_(log_prefix),
|
|
ctx_(context),
|
|
timer_manager_(timer_manager),
|
|
interval_duration_(options.heartbeat_interval.ToTimeDelta()),
|
|
interval_duration_should_include_rtt_(
|
|
options.heartbeat_interval_include_rtt),
|
|
interval_timer_(timer_manager_->CreateTimer(
|
|
"heartbeat-interval",
|
|
absl::bind_front(&HeartbeatHandler::OnIntervalTimerExpiry, this),
|
|
TimerOptions(interval_duration_,
|
|
TimerBackoffAlgorithm::kFixed))),
|
|
timeout_timer_(timer_manager_->CreateTimer(
|
|
"heartbeat-timeout",
|
|
absl::bind_front(&HeartbeatHandler::OnTimeoutTimerExpiry, this),
|
|
TimerOptions(options.rto_initial.ToTimeDelta(),
|
|
TimerBackoffAlgorithm::kExponential,
|
|
/*max_restarts=*/0))) {
|
|
// The interval timer must always be running as long as the association is up.
|
|
RestartTimer();
|
|
}
|
|
|
|
void HeartbeatHandler::RestartTimer() {
|
|
if (interval_duration_.IsZero()) {
|
|
// Heartbeating has been disabled.
|
|
return;
|
|
}
|
|
|
|
if (interval_duration_should_include_rtt_) {
|
|
// The RTT should be used, but it's not easy accessible. The RTO will
|
|
// suffice.
|
|
interval_timer_->set_duration(
|
|
interval_duration_ + ctx_->current_rto());
|
|
} else {
|
|
interval_timer_->set_duration(interval_duration_);
|
|
}
|
|
|
|
interval_timer_->Start();
|
|
}
|
|
|
|
void HeartbeatHandler::HandleHeartbeatRequest(HeartbeatRequestChunk chunk) {
|
|
// https://tools.ietf.org/html/rfc4960#section-8.3
|
|
// "The receiver of the HEARTBEAT should immediately respond with a
|
|
// HEARTBEAT ACK that contains the Heartbeat Information TLV, together with
|
|
// any other received TLVs, copied unchanged from the received HEARTBEAT
|
|
// chunk."
|
|
ctx_->Send(ctx_->PacketBuilder().Add(
|
|
HeartbeatAckChunk(std::move(chunk).extract_parameters())));
|
|
}
|
|
|
|
void HeartbeatHandler::HandleHeartbeatAck(HeartbeatAckChunk chunk) {
|
|
timeout_timer_->Stop();
|
|
absl::optional<HeartbeatInfoParameter> info_param = chunk.info();
|
|
if (!info_param.has_value()) {
|
|
ctx_->callbacks().OnError(
|
|
ErrorKind::kParseFailed,
|
|
"Failed to parse HEARTBEAT-ACK; No Heartbeat Info parameter");
|
|
return;
|
|
}
|
|
absl::optional<HeartbeatInfo> info =
|
|
HeartbeatInfo::Deserialize(info_param->info());
|
|
if (!info.has_value()) {
|
|
ctx_->callbacks().OnError(ErrorKind::kParseFailed,
|
|
"Failed to parse HEARTBEAT-ACK; Failed to "
|
|
"deserialized Heartbeat info parameter");
|
|
return;
|
|
}
|
|
|
|
Timestamp now = ctx_->callbacks().Now();
|
|
if (info->created_at() > Timestamp::Zero() && info->created_at() <= now) {
|
|
ctx_->ObserveRTT(now - info->created_at());
|
|
}
|
|
|
|
// https://tools.ietf.org/html/rfc4960#section-8.1
|
|
// "The counter shall be reset each time ... a HEARTBEAT ACK is received from
|
|
// the peer endpoint."
|
|
ctx_->ClearTxErrorCounter();
|
|
}
|
|
|
|
TimeDelta HeartbeatHandler::OnIntervalTimerExpiry() {
|
|
if (ctx_->is_connection_established()) {
|
|
HeartbeatInfo info(ctx_->callbacks().Now());
|
|
timeout_timer_->set_duration(ctx_->current_rto());
|
|
timeout_timer_->Start();
|
|
RTC_DLOG(LS_INFO) << log_prefix_ << "Sending HEARTBEAT with timeout "
|
|
<< webrtc::ToString(timeout_timer_->duration());
|
|
|
|
Parameters parameters = Parameters::Builder()
|
|
.Add(HeartbeatInfoParameter(info.Serialize()))
|
|
.Build();
|
|
|
|
ctx_->Send(ctx_->PacketBuilder().Add(
|
|
HeartbeatRequestChunk(std::move(parameters))));
|
|
} else {
|
|
RTC_DLOG(LS_VERBOSE)
|
|
<< log_prefix_
|
|
<< "Will not send HEARTBEAT when connection not established";
|
|
}
|
|
return TimeDelta::Zero();
|
|
}
|
|
|
|
TimeDelta HeartbeatHandler::OnTimeoutTimerExpiry() {
|
|
// Note that the timeout timer is not restarted. It will be started again when
|
|
// the interval timer expires.
|
|
RTC_DCHECK(!timeout_timer_->is_running());
|
|
ctx_->IncrementTxErrorCounter("HEARTBEAT timeout");
|
|
return TimeDelta::Zero();
|
|
}
|
|
} // namespace dcsctp
|