Move Connection into it's own .h/.cc file.
This patch is a NOP and moves
- class Connection
- class ConnectionInfo
- class ProxyConnection
from port.{h/cc} to a new file called connection.{h/cc}
BUG=webrtc:10647
Change-Id: I89322d3421d272657e24a46b28ab6679fcdc9450
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/137509
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Reviewed-by: Qingsi Wang <qingsi@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28101}
diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn
index c389e81..a07afef 100644
--- a/p2p/BUILD.gn
+++ b/p2p/BUILD.gn
@@ -25,6 +25,10 @@
"base/basic_packet_socket_factory.cc",
"base/basic_packet_socket_factory.h",
"base/candidate_pair_interface.h",
+ "base/connection.cc",
+ "base/connection.h",
+ "base/connection_info.cc",
+ "base/connection_info.h",
"base/datagram_dtls_adaptor.cc",
"base/datagram_dtls_adaptor.h",
"base/dtls_transport.cc",
diff --git a/p2p/base/connection.cc b/p2p/base/connection.cc
new file mode 100644
index 0000000..6fc93ed
--- /dev/null
+++ b/p2p/base/connection.cc
@@ -0,0 +1,1133 @@
+/*
+ * Copyright 2019 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 "p2p/base/connection.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "absl/algorithm/container.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/match.h"
+#include "p2p/base/port_allocator.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/crc32.h"
+#include "rtc_base/helpers.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/mdns_responder_interface.h"
+#include "rtc_base/message_digest.h"
+#include "rtc_base/network.h"
+#include "rtc_base/numerics/safe_minmax.h"
+#include "rtc_base/string_encode.h"
+#include "rtc_base/string_utils.h"
+#include "rtc_base/third_party/base64/base64.h"
+#include "system_wrappers/include/field_trial.h"
+
+namespace {
+
+// Determines whether we have seen at least the given maximum number of
+// pings fail to have a response.
+inline bool TooManyFailures(
+ const std::vector<cricket::Connection::SentPing>& pings_since_last_response,
+ uint32_t maximum_failures,
+ int rtt_estimate,
+ int64_t now) {
+ // If we haven't sent that many pings, then we can't have failed that many.
+ if (pings_since_last_response.size() < maximum_failures)
+ return false;
+
+ // Check if the window in which we would expect a response to the ping has
+ // already elapsed.
+ int64_t expected_response_time =
+ pings_since_last_response[maximum_failures - 1].sent_time + rtt_estimate;
+ return now > expected_response_time;
+}
+
+// Determines whether we have gone too long without seeing any response.
+inline bool TooLongWithoutResponse(
+ const std::vector<cricket::Connection::SentPing>& pings_since_last_response,
+ int64_t maximum_time,
+ int64_t now) {
+ if (pings_since_last_response.size() == 0)
+ return false;
+
+ auto first = pings_since_last_response[0];
+ return now > (first.sent_time + maximum_time);
+}
+
+// Helper methods for converting string values of log description fields to
+// enum.
+webrtc::IceCandidateType GetCandidateTypeByString(const std::string& type) {
+ if (type == cricket::LOCAL_PORT_TYPE) {
+ return webrtc::IceCandidateType::kLocal;
+ } else if (type == cricket::STUN_PORT_TYPE) {
+ return webrtc::IceCandidateType::kStun;
+ } else if (type == cricket::PRFLX_PORT_TYPE) {
+ return webrtc::IceCandidateType::kPrflx;
+ } else if (type == cricket::RELAY_PORT_TYPE) {
+ return webrtc::IceCandidateType::kRelay;
+ }
+ return webrtc::IceCandidateType::kUnknown;
+}
+
+webrtc::IceCandidatePairProtocol GetProtocolByString(
+ const std::string& protocol) {
+ if (protocol == cricket::UDP_PROTOCOL_NAME) {
+ return webrtc::IceCandidatePairProtocol::kUdp;
+ } else if (protocol == cricket::TCP_PROTOCOL_NAME) {
+ return webrtc::IceCandidatePairProtocol::kTcp;
+ } else if (protocol == cricket::SSLTCP_PROTOCOL_NAME) {
+ return webrtc::IceCandidatePairProtocol::kSsltcp;
+ } else if (protocol == cricket::TLS_PROTOCOL_NAME) {
+ return webrtc::IceCandidatePairProtocol::kTls;
+ }
+ return webrtc::IceCandidatePairProtocol::kUnknown;
+}
+
+webrtc::IceCandidatePairAddressFamily GetAddressFamilyByInt(
+ int address_family) {
+ if (address_family == AF_INET) {
+ return webrtc::IceCandidatePairAddressFamily::kIpv4;
+ } else if (address_family == AF_INET6) {
+ return webrtc::IceCandidatePairAddressFamily::kIpv6;
+ }
+ return webrtc::IceCandidatePairAddressFamily::kUnknown;
+}
+
+webrtc::IceCandidateNetworkType ConvertNetworkType(rtc::AdapterType type) {
+ if (type == rtc::ADAPTER_TYPE_ETHERNET) {
+ return webrtc::IceCandidateNetworkType::kEthernet;
+ } else if (type == rtc::ADAPTER_TYPE_LOOPBACK) {
+ return webrtc::IceCandidateNetworkType::kLoopback;
+ } else if (type == rtc::ADAPTER_TYPE_WIFI) {
+ return webrtc::IceCandidateNetworkType::kWifi;
+ } else if (type == rtc::ADAPTER_TYPE_VPN) {
+ return webrtc::IceCandidateNetworkType::kVpn;
+ } else if (type == rtc::ADAPTER_TYPE_CELLULAR) {
+ return webrtc::IceCandidateNetworkType::kCellular;
+ }
+ return webrtc::IceCandidateNetworkType::kUnknown;
+}
+
+// When we don't have any RTT data, we have to pick something reasonable. We
+// use a large value just in case the connection is really slow.
+const int DEFAULT_RTT = 3000; // 3 seconds
+
+// We will restrict RTT estimates (when used for determining state) to be
+// within a reasonable range.
+const int MINIMUM_RTT = 100; // 0.1 seconds
+const int MAXIMUM_RTT = 60000; // 60 seconds
+
+// Computes our estimate of the RTT given the current estimate.
+inline int ConservativeRTTEstimate(int rtt) {
+ return rtc::SafeClamp(2 * rtt, MINIMUM_RTT, MAXIMUM_RTT);
+}
+
+// Weighting of the old rtt value to new data.
+const int RTT_RATIO = 3; // 3 : 1
+
+constexpr int64_t kMinExtraPingDelayMs = 100;
+
+} // namespace
+
+namespace cricket {
+
+// A ConnectionRequest is a simple STUN ping used to determine writability.
+ConnectionRequest::ConnectionRequest(Connection* connection)
+ : StunRequest(new IceMessage()), connection_(connection) {}
+
+void ConnectionRequest::Prepare(StunMessage* request) {
+ request->SetType(STUN_BINDING_REQUEST);
+ std::string username;
+ connection_->port()->CreateStunUsername(
+ connection_->remote_candidate().username(), &username);
+ request->AddAttribute(
+ absl::make_unique<StunByteStringAttribute>(STUN_ATTR_USERNAME, username));
+
+ // connection_ already holds this ping, so subtract one from count.
+ if (connection_->port()->send_retransmit_count_attribute()) {
+ request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
+ STUN_ATTR_RETRANSMIT_COUNT,
+ static_cast<uint32_t>(connection_->pings_since_last_response_.size() -
+ 1)));
+ }
+ uint32_t network_info = connection_->port()->Network()->id();
+ network_info = (network_info << 16) | connection_->port()->network_cost();
+ request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
+ STUN_ATTR_NETWORK_INFO, network_info));
+
+ // Adding ICE_CONTROLLED or ICE_CONTROLLING attribute based on the role.
+ if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLING) {
+ request->AddAttribute(absl::make_unique<StunUInt64Attribute>(
+ STUN_ATTR_ICE_CONTROLLING, connection_->port()->IceTiebreaker()));
+ // We should have either USE_CANDIDATE attribute or ICE_NOMINATION
+ // attribute but not both. That was enforced in p2ptransportchannel.
+ if (connection_->use_candidate_attr()) {
+ request->AddAttribute(
+ absl::make_unique<StunByteStringAttribute>(STUN_ATTR_USE_CANDIDATE));
+ }
+ if (connection_->nomination() &&
+ connection_->nomination() != connection_->acked_nomination()) {
+ request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
+ STUN_ATTR_NOMINATION, connection_->nomination()));
+ }
+ } else if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLED) {
+ request->AddAttribute(absl::make_unique<StunUInt64Attribute>(
+ STUN_ATTR_ICE_CONTROLLED, connection_->port()->IceTiebreaker()));
+ } else {
+ RTC_NOTREACHED();
+ }
+
+ // Adding PRIORITY Attribute.
+ // Changing the type preference to Peer Reflexive and local preference
+ // and component id information is unchanged from the original priority.
+ // priority = (2^24)*(type preference) +
+ // (2^8)*(local preference) +
+ // (2^0)*(256 - component ID)
+ uint32_t type_preference =
+ (connection_->local_candidate().protocol() == TCP_PROTOCOL_NAME)
+ ? ICE_TYPE_PREFERENCE_PRFLX_TCP
+ : ICE_TYPE_PREFERENCE_PRFLX;
+ uint32_t prflx_priority =
+ type_preference << 24 |
+ (connection_->local_candidate().priority() & 0x00FFFFFF);
+ request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
+ STUN_ATTR_PRIORITY, prflx_priority));
+
+ // Adding Message Integrity attribute.
+ request->AddMessageIntegrity(connection_->remote_candidate().password());
+ // Adding Fingerprint.
+ request->AddFingerprint();
+}
+
+void ConnectionRequest::OnResponse(StunMessage* response) {
+ connection_->OnConnectionRequestResponse(this, response);
+}
+
+void ConnectionRequest::OnErrorResponse(StunMessage* response) {
+ connection_->OnConnectionRequestErrorResponse(this, response);
+}
+
+void ConnectionRequest::OnTimeout() {
+ connection_->OnConnectionRequestTimeout(this);
+}
+
+void ConnectionRequest::OnSent() {
+ connection_->OnConnectionRequestSent(this);
+ // Each request is sent only once. After a single delay , the request will
+ // time out.
+ timeout_ = true;
+}
+
+int ConnectionRequest::resend_delay() {
+ return CONNECTION_RESPONSE_TIMEOUT;
+}
+
+Connection::Connection(Port* port,
+ size_t index,
+ const Candidate& remote_candidate)
+ : id_(rtc::CreateRandomId()),
+ port_(port),
+ local_candidate_index_(index),
+ remote_candidate_(remote_candidate),
+ recv_rate_tracker_(100, 10u),
+ send_rate_tracker_(100, 10u),
+ write_state_(STATE_WRITE_INIT),
+ receiving_(false),
+ connected_(true),
+ pruned_(false),
+ use_candidate_attr_(false),
+ remote_ice_mode_(ICEMODE_FULL),
+ requests_(port->thread()),
+ rtt_(DEFAULT_RTT),
+ last_ping_sent_(0),
+ last_ping_received_(0),
+ last_data_received_(0),
+ last_ping_response_received_(0),
+ reported_(false),
+ state_(IceCandidatePairState::WAITING),
+ time_created_ms_(rtc::TimeMillis()) {
+ // All of our connections start in WAITING state.
+ // TODO(mallinath) - Start connections from STATE_FROZEN.
+ // Wire up to send stun packets
+ requests_.SignalSendPacket.connect(this, &Connection::OnSendStunPacket);
+ RTC_LOG(LS_INFO) << ToString() << ": Connection created";
+}
+
+Connection::~Connection() {}
+
+const Candidate& Connection::local_candidate() const {
+ RTC_DCHECK(local_candidate_index_ < port_->Candidates().size());
+ return port_->Candidates()[local_candidate_index_];
+}
+
+const Candidate& Connection::remote_candidate() const {
+ return remote_candidate_;
+}
+
+uint64_t Connection::priority() const {
+ uint64_t priority = 0;
+ // RFC 5245 - 5.7.2. Computing Pair Priority and Ordering Pairs
+ // Let G be the priority for the candidate provided by the controlling
+ // agent. Let D be the priority for the candidate provided by the
+ // controlled agent.
+ // pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)
+ IceRole role = port_->GetIceRole();
+ if (role != ICEROLE_UNKNOWN) {
+ uint32_t g = 0;
+ uint32_t d = 0;
+ if (role == ICEROLE_CONTROLLING) {
+ g = local_candidate().priority();
+ d = remote_candidate_.priority();
+ } else {
+ g = remote_candidate_.priority();
+ d = local_candidate().priority();
+ }
+ priority = std::min(g, d);
+ priority = priority << 32;
+ priority += 2 * std::max(g, d) + (g > d ? 1 : 0);
+ }
+ return priority;
+}
+
+void Connection::set_write_state(WriteState value) {
+ WriteState old_value = write_state_;
+ write_state_ = value;
+ if (value != old_value) {
+ RTC_LOG(LS_VERBOSE) << ToString() << ": set_write_state from: " << old_value
+ << " to " << value;
+ SignalStateChange(this);
+ }
+}
+
+void Connection::UpdateReceiving(int64_t now) {
+ bool receiving;
+ if (last_ping_sent() < last_ping_response_received()) {
+ // We consider any candidate pair that has its last connectivity check
+ // acknowledged by a response as receiving, particularly for backup
+ // candidate pairs that send checks at a much slower pace than the selected
+ // one. Otherwise, a backup candidate pair constantly becomes not receiving
+ // as a side effect of a long ping interval, since we do not have a separate
+ // receiving timeout for backup candidate pairs. See
+ // IceConfig.ice_backup_candidate_pair_ping_interval,
+ // IceConfig.ice_connection_receiving_timeout and their default value.
+ receiving = true;
+ } else {
+ receiving =
+ last_received() > 0 && now <= last_received() + receiving_timeout();
+ }
+ if (receiving_ == receiving) {
+ return;
+ }
+ RTC_LOG(LS_VERBOSE) << ToString() << ": set_receiving to " << receiving;
+ receiving_ = receiving;
+ receiving_unchanged_since_ = now;
+ SignalStateChange(this);
+}
+
+void Connection::set_state(IceCandidatePairState state) {
+ IceCandidatePairState old_state = state_;
+ state_ = state;
+ if (state != old_state) {
+ RTC_LOG(LS_VERBOSE) << ToString() << ": set_state";
+ }
+}
+
+void Connection::set_connected(bool value) {
+ bool old_value = connected_;
+ connected_ = value;
+ if (value != old_value) {
+ RTC_LOG(LS_VERBOSE) << ToString() << ": Change connected_ to " << value;
+ SignalStateChange(this);
+ }
+}
+
+void Connection::set_use_candidate_attr(bool enable) {
+ use_candidate_attr_ = enable;
+}
+
+int Connection::unwritable_timeout() const {
+ return unwritable_timeout_.value_or(CONNECTION_WRITE_CONNECT_TIMEOUT);
+}
+
+int Connection::unwritable_min_checks() const {
+ return unwritable_min_checks_.value_or(CONNECTION_WRITE_CONNECT_FAILURES);
+}
+
+int Connection::inactive_timeout() const {
+ return inactive_timeout_.value_or(CONNECTION_WRITE_TIMEOUT);
+}
+
+int Connection::receiving_timeout() const {
+ return receiving_timeout_.value_or(WEAK_CONNECTION_RECEIVE_TIMEOUT);
+}
+
+void Connection::OnSendStunPacket(const void* data,
+ size_t size,
+ StunRequest* req) {
+ rtc::PacketOptions options(port_->StunDscpValue());
+ options.info_signaled_after_sent.packet_type =
+ rtc::PacketType::kIceConnectivityCheck;
+ auto err =
+ port_->SendTo(data, size, remote_candidate_.address(), options, false);
+ if (err < 0) {
+ RTC_LOG(LS_WARNING) << ToString()
+ << ": Failed to send STUN ping "
+ " err="
+ << err << " id=" << rtc::hex_encode(req->id());
+ }
+}
+
+void Connection::OnReadPacket(const char* data,
+ size_t size,
+ int64_t packet_time_us) {
+ std::unique_ptr<IceMessage> msg;
+ std::string remote_ufrag;
+ const rtc::SocketAddress& addr(remote_candidate_.address());
+ if (!port_->GetStunMessage(data, size, addr, &msg, &remote_ufrag)) {
+ // The packet did not parse as a valid STUN message
+ // This is a data packet, pass it along.
+ last_data_received_ = rtc::TimeMillis();
+ UpdateReceiving(last_data_received_);
+ recv_rate_tracker_.AddSamples(size);
+ SignalReadPacket(this, data, size, packet_time_us);
+
+ // If timed out sending writability checks, start up again
+ if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) {
+ RTC_LOG(LS_WARNING)
+ << "Received a data packet on a timed-out Connection. "
+ "Resetting state to STATE_WRITE_INIT.";
+ set_write_state(STATE_WRITE_INIT);
+ }
+ } else if (!msg) {
+ // The packet was STUN, but failed a check and was handled internally.
+ } else {
+ // The packet is STUN and passed the Port checks.
+ // Perform our own checks to ensure this packet is valid.
+ // If this is a STUN request, then update the receiving bit and respond.
+ // If this is a STUN response, then update the writable bit.
+ // Log at LS_INFO if we receive a ping on an unwritable connection.
+ rtc::LoggingSeverity sev = (!writable() ? rtc::LS_INFO : rtc::LS_VERBOSE);
+ switch (msg->type()) {
+ case STUN_BINDING_REQUEST:
+ RTC_LOG_V(sev) << ToString() << ": Received STUN ping, id="
+ << rtc::hex_encode(msg->transaction_id());
+
+ if (remote_ufrag == remote_candidate_.username()) {
+ HandleBindingRequest(msg.get());
+ } else {
+ // The packet had the right local username, but the remote username
+ // was not the right one for the remote address.
+ RTC_LOG(LS_ERROR)
+ << ToString()
+ << ": Received STUN request with bad remote username "
+ << remote_ufrag;
+ port_->SendBindingErrorResponse(msg.get(), addr,
+ STUN_ERROR_UNAUTHORIZED,
+ STUN_ERROR_REASON_UNAUTHORIZED);
+ }
+ break;
+
+ // Response from remote peer. Does it match request sent?
+ // This doesn't just check, it makes callbacks if transaction
+ // id's match.
+ case STUN_BINDING_RESPONSE:
+ case STUN_BINDING_ERROR_RESPONSE:
+ if (msg->ValidateMessageIntegrity(data, size,
+ remote_candidate().password())) {
+ requests_.CheckResponse(msg.get());
+ }
+ // Otherwise silently discard the response message.
+ break;
+
+ // Remote end point sent an STUN indication instead of regular binding
+ // request. In this case |last_ping_received_| will be updated but no
+ // response will be sent.
+ case STUN_BINDING_INDICATION:
+ ReceivedPing();
+ break;
+
+ default:
+ RTC_NOTREACHED();
+ break;
+ }
+ }
+}
+
+void Connection::HandleBindingRequest(IceMessage* msg) {
+ // This connection should now be receiving.
+ ReceivedPing();
+ if (webrtc::field_trial::IsEnabled("WebRTC-ExtraICEPing") &&
+ last_ping_response_received_ == 0) {
+ if (local_candidate().type() == RELAY_PORT_TYPE ||
+ local_candidate().type() == PRFLX_PORT_TYPE ||
+ remote_candidate().type() == RELAY_PORT_TYPE ||
+ remote_candidate().type() == PRFLX_PORT_TYPE) {
+ const int64_t now = rtc::TimeMillis();
+ if (last_ping_sent_ + kMinExtraPingDelayMs <= now) {
+ RTC_LOG(LS_INFO) << ToString()
+ << "WebRTC-ExtraICEPing/Sending extra ping"
+ << " last_ping_sent_: " << last_ping_sent_
+ << " now: " << now
+ << " (diff: " << (now - last_ping_sent_) << ")";
+ Ping(now);
+ } else {
+ RTC_LOG(LS_INFO) << ToString()
+ << "WebRTC-ExtraICEPing/Not sending extra ping"
+ << " last_ping_sent_: " << last_ping_sent_
+ << " now: " << now
+ << " (diff: " << (now - last_ping_sent_) << ")";
+ }
+ }
+ }
+
+ const rtc::SocketAddress& remote_addr = remote_candidate_.address();
+ const std::string& remote_ufrag = remote_candidate_.username();
+ // Check for role conflicts.
+ if (!port_->MaybeIceRoleConflict(remote_addr, msg, remote_ufrag)) {
+ // Received conflicting role from the peer.
+ RTC_LOG(LS_INFO) << "Received conflicting role from the peer.";
+ return;
+ }
+
+ stats_.recv_ping_requests++;
+ LogCandidatePairEvent(webrtc::IceCandidatePairEventType::kCheckReceived,
+ msg->reduced_transaction_id());
+
+ // This is a validated stun request from remote peer.
+ port_->SendBindingResponse(msg, remote_addr);
+
+ // If it timed out on writing check, start up again
+ if (!pruned_ && write_state_ == STATE_WRITE_TIMEOUT) {
+ set_write_state(STATE_WRITE_INIT);
+ }
+
+ if (port_->GetIceRole() == ICEROLE_CONTROLLED) {
+ const StunUInt32Attribute* nomination_attr =
+ msg->GetUInt32(STUN_ATTR_NOMINATION);
+ uint32_t nomination = 0;
+ if (nomination_attr) {
+ nomination = nomination_attr->value();
+ if (nomination == 0) {
+ RTC_LOG(LS_ERROR) << "Invalid nomination: " << nomination;
+ }
+ } else {
+ const StunByteStringAttribute* use_candidate_attr =
+ msg->GetByteString(STUN_ATTR_USE_CANDIDATE);
+ if (use_candidate_attr) {
+ nomination = 1;
+ }
+ }
+ // We don't un-nominate a connection, so we only keep a larger nomination.
+ if (nomination > remote_nomination_) {
+ set_remote_nomination(nomination);
+ SignalNominated(this);
+ }
+ }
+ // Set the remote cost if the network_info attribute is available.
+ // Note: If packets are re-ordered, we may get incorrect network cost
+ // temporarily, but it should get the correct value shortly after that.
+ const StunUInt32Attribute* network_attr =
+ msg->GetUInt32(STUN_ATTR_NETWORK_INFO);
+ if (network_attr) {
+ uint32_t network_info = network_attr->value();
+ uint16_t network_cost = static_cast<uint16_t>(network_info);
+ if (network_cost != remote_candidate_.network_cost()) {
+ remote_candidate_.set_network_cost(network_cost);
+ // Network cost change will affect the connection ranking, so signal
+ // state change to force a re-sort in P2PTransportChannel.
+ SignalStateChange(this);
+ }
+ }
+}
+
+void Connection::OnReadyToSend() {
+ SignalReadyToSend(this);
+}
+
+void Connection::Prune() {
+ if (!pruned_ || active()) {
+ RTC_LOG(LS_INFO) << ToString() << ": Connection pruned";
+ pruned_ = true;
+ requests_.Clear();
+ set_write_state(STATE_WRITE_TIMEOUT);
+ }
+}
+
+void Connection::Destroy() {
+ // TODO(deadbeef, nisse): This may leak if an application closes a
+ // PeerConnection and then quickly destroys the PeerConnectionFactory (along
+ // with the networking thread on which this message is posted). Also affects
+ // tests, with a workaround in
+ // AutoSocketServerThread::~AutoSocketServerThread.
+ RTC_LOG(LS_VERBOSE) << ToString() << ": Connection destroyed";
+ port_->thread()->Post(RTC_FROM_HERE, this, MSG_DELETE);
+ LogCandidatePairConfig(webrtc::IceCandidatePairConfigType::kDestroyed);
+}
+
+void Connection::FailAndDestroy() {
+ set_state(IceCandidatePairState::FAILED);
+ Destroy();
+}
+
+void Connection::FailAndPrune() {
+ set_state(IceCandidatePairState::FAILED);
+ Prune();
+}
+
+void Connection::PrintPingsSinceLastResponse(std::string* s, size_t max) {
+ rtc::StringBuilder oss;
+ if (pings_since_last_response_.size() > max) {
+ for (size_t i = 0; i < max; i++) {
+ const SentPing& ping = pings_since_last_response_[i];
+ oss << rtc::hex_encode(ping.id) << " ";
+ }
+ oss << "... " << (pings_since_last_response_.size() - max) << " more";
+ } else {
+ for (const SentPing& ping : pings_since_last_response_) {
+ oss << rtc::hex_encode(ping.id) << " ";
+ }
+ }
+ *s = oss.str();
+}
+
+void Connection::UpdateState(int64_t now) {
+ int rtt = ConservativeRTTEstimate(rtt_);
+
+ if (RTC_LOG_CHECK_LEVEL(LS_VERBOSE)) {
+ std::string pings;
+ PrintPingsSinceLastResponse(&pings, 5);
+ RTC_LOG(LS_VERBOSE) << ToString()
+ << ": UpdateState()"
+ ", ms since last received response="
+ << now - last_ping_response_received_
+ << ", ms since last received data="
+ << now - last_data_received_ << ", rtt=" << rtt
+ << ", pings_since_last_response=" << pings;
+ }
+
+ // Check the writable state. (The order of these checks is important.)
+ //
+ // Before becoming unwritable, we allow for a fixed number of pings to fail
+ // (i.e., receive no response). We also have to give the response time to
+ // get back, so we include a conservative estimate of this.
+ //
+ // Before timing out writability, we give a fixed amount of time. This is to
+ // allow for changes in network conditions.
+
+ if ((write_state_ == STATE_WRITABLE) &&
+ TooManyFailures(pings_since_last_response_, unwritable_min_checks(), rtt,
+ now) &&
+ TooLongWithoutResponse(pings_since_last_response_, unwritable_timeout(),
+ now)) {
+ uint32_t max_pings = unwritable_min_checks();
+ RTC_LOG(LS_INFO) << ToString() << ": Unwritable after " << max_pings
+ << " ping failures and "
+ << now - pings_since_last_response_[0].sent_time
+ << " ms without a response,"
+ " ms since last received ping="
+ << now - last_ping_received_
+ << " ms since last received data="
+ << now - last_data_received_ << " rtt=" << rtt;
+ set_write_state(STATE_WRITE_UNRELIABLE);
+ }
+ if ((write_state_ == STATE_WRITE_UNRELIABLE ||
+ write_state_ == STATE_WRITE_INIT) &&
+ TooLongWithoutResponse(pings_since_last_response_, inactive_timeout(),
+ now)) {
+ RTC_LOG(LS_INFO) << ToString() << ": Timed out after "
+ << now - pings_since_last_response_[0].sent_time
+ << " ms without a response, rtt=" << rtt;
+ set_write_state(STATE_WRITE_TIMEOUT);
+ }
+
+ // Update the receiving state.
+ UpdateReceiving(now);
+ if (dead(now)) {
+ Destroy();
+ }
+}
+
+void Connection::Ping(int64_t now) {
+ last_ping_sent_ = now;
+ ConnectionRequest* req = new ConnectionRequest(this);
+ // If not using renomination, we use "1" to mean "nominated" and "0" to mean
+ // "not nominated". If using renomination, values greater than 1 are used for
+ // re-nominated pairs.
+ int nomination = use_candidate_attr_ ? 1 : 0;
+ if (nomination_ > 0) {
+ nomination = nomination_;
+ }
+ pings_since_last_response_.push_back(SentPing(req->id(), now, nomination));
+ RTC_LOG(LS_VERBOSE) << ToString() << ": Sending STUN ping, id="
+ << rtc::hex_encode(req->id())
+ << ", nomination=" << nomination_;
+ requests_.Send(req);
+ state_ = IceCandidatePairState::IN_PROGRESS;
+ num_pings_sent_++;
+}
+
+void Connection::ReceivedPing() {
+ last_ping_received_ = rtc::TimeMillis();
+ UpdateReceiving(last_ping_received_);
+}
+
+void Connection::ReceivedPingResponse(int rtt, const std::string& request_id) {
+ RTC_DCHECK_GE(rtt, 0);
+ // We've already validated that this is a STUN binding response with
+ // the correct local and remote username for this connection.
+ // So if we're not already, become writable. We may be bringing a pruned
+ // connection back to life, but if we don't really want it, we can always
+ // prune it again.
+ auto iter = absl::c_find_if(
+ pings_since_last_response_,
+ [request_id](const SentPing& ping) { return ping.id == request_id; });
+ if (iter != pings_since_last_response_.end() &&
+ iter->nomination > acked_nomination_) {
+ acked_nomination_ = iter->nomination;
+ }
+
+ total_round_trip_time_ms_ += rtt;
+ current_round_trip_time_ms_ = static_cast<uint32_t>(rtt);
+
+ pings_since_last_response_.clear();
+ last_ping_response_received_ = rtc::TimeMillis();
+ UpdateReceiving(last_ping_response_received_);
+ set_write_state(STATE_WRITABLE);
+ set_state(IceCandidatePairState::SUCCEEDED);
+ if (rtt_samples_ > 0) {
+ rtt_ = rtc::GetNextMovingAverage(rtt_, rtt, RTT_RATIO);
+ } else {
+ rtt_ = rtt;
+ }
+ rtt_samples_++;
+}
+
+bool Connection::dead(int64_t now) const {
+ if (last_received() > 0) {
+ // If it has ever received anything, we keep it alive until it hasn't
+ // received anything for DEAD_CONNECTION_RECEIVE_TIMEOUT. This covers the
+ // normal case of a successfully used connection that stops working. This
+ // also allows a remote peer to continue pinging over a locally inactive
+ // (pruned) connection.
+ return (now > (last_received() + DEAD_CONNECTION_RECEIVE_TIMEOUT));
+ }
+
+ if (active()) {
+ // If it has never received anything, keep it alive as long as it is
+ // actively pinging and not pruned. Otherwise, the connection might be
+ // deleted before it has a chance to ping. This is the normal case for a
+ // new connection that is pinging but hasn't received anything yet.
+ return false;
+ }
+
+ // If it has never received anything and is not actively pinging (pruned), we
+ // keep it around for at least MIN_CONNECTION_LIFETIME to prevent connections
+ // from being pruned too quickly during a network change event when two
+ // networks would be up simultaneously but only for a brief period.
+ return now > (time_created_ms_ + MIN_CONNECTION_LIFETIME);
+}
+
+bool Connection::stable(int64_t now) const {
+ // A connection is stable if it's RTT has converged and it isn't missing any
+ // responses. We should send pings at a higher rate until the RTT converges
+ // and whenever a ping response is missing (so that we can detect
+ // unwritability faster)
+ return rtt_converged() && !missing_responses(now);
+}
+
+std::string Connection::ToDebugId() const {
+ return rtc::ToHex(reinterpret_cast<uintptr_t>(this));
+}
+
+uint32_t Connection::ComputeNetworkCost() const {
+ // TODO(honghaiz): Will add rtt as part of the network cost.
+ return port()->network_cost() + remote_candidate_.network_cost();
+}
+
+std::string Connection::ToString() const {
+ const absl::string_view CONNECT_STATE_ABBREV[2] = {
+ "-", // not connected (false)
+ "C", // connected (true)
+ };
+ const absl::string_view RECEIVE_STATE_ABBREV[2] = {
+ "-", // not receiving (false)
+ "R", // receiving (true)
+ };
+ const absl::string_view WRITE_STATE_ABBREV[4] = {
+ "W", // STATE_WRITABLE
+ "w", // STATE_WRITE_UNRELIABLE
+ "-", // STATE_WRITE_INIT
+ "x", // STATE_WRITE_TIMEOUT
+ };
+ const absl::string_view ICESTATE[4] = {
+ "W", // STATE_WAITING
+ "I", // STATE_INPROGRESS
+ "S", // STATE_SUCCEEDED
+ "F" // STATE_FAILED
+ };
+ const absl::string_view SELECTED_STATE_ABBREV[2] = {
+ "-", // candidate pair not selected (false)
+ "S", // selected (true)
+ };
+ const Candidate& local = local_candidate();
+ const Candidate& remote = remote_candidate();
+ rtc::StringBuilder ss;
+ ss << "Conn[" << ToDebugId() << ":" << port_->content_name() << ":"
+ << port_->Network()->ToString() << ":" << local.id() << ":"
+ << local.component() << ":" << local.generation() << ":" << local.type()
+ << ":" << local.protocol() << ":" << local.address().ToSensitiveString()
+ << "->" << remote.id() << ":" << remote.component() << ":"
+ << remote.priority() << ":" << remote.type() << ":" << remote.protocol()
+ << ":" << remote.address().ToSensitiveString() << "|"
+ << CONNECT_STATE_ABBREV[connected()] << RECEIVE_STATE_ABBREV[receiving()]
+ << WRITE_STATE_ABBREV[write_state()] << ICESTATE[static_cast<int>(state())]
+ << "|" << SELECTED_STATE_ABBREV[selected()] << "|" << remote_nomination()
+ << "|" << nomination() << "|" << priority() << "|";
+ if (rtt_ < DEFAULT_RTT) {
+ ss << rtt_ << "]";
+ } else {
+ ss << "-]";
+ }
+ return ss.Release();
+}
+
+std::string Connection::ToSensitiveString() const {
+ return ToString();
+}
+
+const webrtc::IceCandidatePairDescription& Connection::ToLogDescription() {
+ if (log_description_.has_value()) {
+ return log_description_.value();
+ }
+ const Candidate& local = local_candidate();
+ const Candidate& remote = remote_candidate();
+ const rtc::Network* network = port()->Network();
+ log_description_ = webrtc::IceCandidatePairDescription();
+ log_description_->local_candidate_type =
+ GetCandidateTypeByString(local.type());
+ log_description_->local_relay_protocol =
+ GetProtocolByString(local.relay_protocol());
+ log_description_->local_network_type = ConvertNetworkType(network->type());
+ log_description_->local_address_family =
+ GetAddressFamilyByInt(local.address().family());
+ log_description_->remote_candidate_type =
+ GetCandidateTypeByString(remote.type());
+ log_description_->remote_address_family =
+ GetAddressFamilyByInt(remote.address().family());
+ log_description_->candidate_pair_protocol =
+ GetProtocolByString(local.protocol());
+ return log_description_.value();
+}
+
+void Connection::LogCandidatePairConfig(
+ webrtc::IceCandidatePairConfigType type) {
+ if (ice_event_log_ == nullptr) {
+ return;
+ }
+ ice_event_log_->LogCandidatePairConfig(type, id(), ToLogDescription());
+}
+
+void Connection::LogCandidatePairEvent(webrtc::IceCandidatePairEventType type,
+ uint32_t transaction_id) {
+ if (ice_event_log_ == nullptr) {
+ return;
+ }
+ ice_event_log_->LogCandidatePairEvent(type, id(), transaction_id);
+}
+
+void Connection::OnConnectionRequestResponse(ConnectionRequest* request,
+ StunMessage* response) {
+ // Log at LS_INFO if we receive a ping response on an unwritable
+ // connection.
+ rtc::LoggingSeverity sev = !writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
+
+ int rtt = request->Elapsed();
+
+ if (RTC_LOG_CHECK_LEVEL_V(sev)) {
+ std::string pings;
+ PrintPingsSinceLastResponse(&pings, 5);
+ RTC_LOG_V(sev) << ToString() << ": Received STUN ping response, id="
+ << rtc::hex_encode(request->id())
+ << ", code=0" // Makes logging easier to parse.
+ ", rtt="
+ << rtt << ", pings_since_last_response=" << pings;
+ }
+ ReceivedPingResponse(rtt, request->id());
+
+ stats_.recv_ping_responses++;
+ LogCandidatePairEvent(
+ webrtc::IceCandidatePairEventType::kCheckResponseReceived,
+ response->reduced_transaction_id());
+
+ MaybeUpdateLocalCandidate(request, response);
+}
+
+void Connection::OnConnectionRequestErrorResponse(ConnectionRequest* request,
+ StunMessage* response) {
+ int error_code = response->GetErrorCodeValue();
+ RTC_LOG(LS_WARNING) << ToString() << ": Received STUN error response id="
+ << rtc::hex_encode(request->id())
+ << " code=" << error_code
+ << " rtt=" << request->Elapsed();
+
+ if (error_code == STUN_ERROR_UNKNOWN_ATTRIBUTE ||
+ error_code == STUN_ERROR_SERVER_ERROR ||
+ error_code == STUN_ERROR_UNAUTHORIZED) {
+ // Recoverable error, retry
+ } else if (error_code == STUN_ERROR_STALE_CREDENTIALS) {
+ // Race failure, retry
+ } else if (error_code == STUN_ERROR_ROLE_CONFLICT) {
+ HandleRoleConflictFromPeer();
+ } else {
+ // This is not a valid connection.
+ RTC_LOG(LS_ERROR) << ToString()
+ << ": Received STUN error response, code=" << error_code
+ << "; killing connection";
+ FailAndDestroy();
+ }
+}
+
+void Connection::OnConnectionRequestTimeout(ConnectionRequest* request) {
+ // Log at LS_INFO if we miss a ping on a writable connection.
+ rtc::LoggingSeverity sev = writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
+ RTC_LOG_V(sev) << ToString() << ": Timing-out STUN ping "
+ << rtc::hex_encode(request->id()) << " after "
+ << request->Elapsed() << " ms";
+}
+
+void Connection::OnConnectionRequestSent(ConnectionRequest* request) {
+ // Log at LS_INFO if we send a ping on an unwritable connection.
+ rtc::LoggingSeverity sev = !writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
+ RTC_LOG_V(sev) << ToString()
+ << ": Sent STUN ping, id=" << rtc::hex_encode(request->id())
+ << ", use_candidate=" << use_candidate_attr()
+ << ", nomination=" << nomination();
+ stats_.sent_ping_requests_total++;
+ LogCandidatePairEvent(webrtc::IceCandidatePairEventType::kCheckSent,
+ request->reduced_transaction_id());
+ if (stats_.recv_ping_responses == 0) {
+ stats_.sent_ping_requests_before_first_response++;
+ }
+}
+
+void Connection::HandleRoleConflictFromPeer() {
+ port_->SignalRoleConflict(port_);
+}
+
+void Connection::MaybeSetRemoteIceParametersAndGeneration(
+ const IceParameters& ice_params,
+ int generation) {
+ if (remote_candidate_.username() == ice_params.ufrag &&
+ remote_candidate_.password().empty()) {
+ remote_candidate_.set_password(ice_params.pwd);
+ }
+ // TODO(deadbeef): A value of '0' for the generation is used for both
+ // generation 0 and "generation unknown". It should be changed to an
+ // absl::optional to fix this.
+ if (remote_candidate_.username() == ice_params.ufrag &&
+ remote_candidate_.password() == ice_params.pwd &&
+ remote_candidate_.generation() == 0) {
+ remote_candidate_.set_generation(generation);
+ }
+}
+
+void Connection::MaybeUpdatePeerReflexiveCandidate(
+ const Candidate& new_candidate) {
+ if (remote_candidate_.type() == PRFLX_PORT_TYPE &&
+ new_candidate.type() != PRFLX_PORT_TYPE &&
+ remote_candidate_.protocol() == new_candidate.protocol() &&
+ remote_candidate_.address() == new_candidate.address() &&
+ remote_candidate_.username() == new_candidate.username() &&
+ remote_candidate_.password() == new_candidate.password() &&
+ remote_candidate_.generation() == new_candidate.generation()) {
+ remote_candidate_ = new_candidate;
+ }
+}
+
+void Connection::OnMessage(rtc::Message* pmsg) {
+ RTC_DCHECK(pmsg->message_id == MSG_DELETE);
+ RTC_LOG(LS_INFO) << "Connection deleted with number of pings sent: "
+ << num_pings_sent_;
+ SignalDestroyed(this);
+ delete this;
+}
+
+int64_t Connection::last_received() const {
+ return std::max(last_data_received_,
+ std::max(last_ping_received_, last_ping_response_received_));
+}
+
+ConnectionInfo Connection::stats() {
+ stats_.recv_bytes_second = round(recv_rate_tracker_.ComputeRate());
+ stats_.recv_total_bytes = recv_rate_tracker_.TotalSampleCount();
+ stats_.sent_bytes_second = round(send_rate_tracker_.ComputeRate());
+ stats_.sent_total_bytes = send_rate_tracker_.TotalSampleCount();
+ stats_.receiving = receiving_;
+ stats_.writable = write_state_ == STATE_WRITABLE;
+ stats_.timeout = write_state_ == STATE_WRITE_TIMEOUT;
+ stats_.new_connection = !reported_;
+ stats_.rtt = rtt_;
+ stats_.key = this;
+ stats_.state = state_;
+ stats_.priority = priority();
+ stats_.nominated = nominated();
+ stats_.total_round_trip_time_ms = total_round_trip_time_ms_;
+ stats_.current_round_trip_time_ms = current_round_trip_time_ms_;
+ CopyCandidatesToStatsAndSanitizeIfNecessary();
+ return stats_;
+}
+
+void Connection::MaybeUpdateLocalCandidate(ConnectionRequest* request,
+ StunMessage* response) {
+ // RFC 5245
+ // The agent checks the mapped address from the STUN response. If the
+ // transport address does not match any of the local candidates that the
+ // agent knows about, the mapped address represents a new candidate -- a
+ // peer reflexive candidate.
+ const StunAddressAttribute* addr =
+ response->GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
+ if (!addr) {
+ RTC_LOG(LS_WARNING)
+ << "Connection::OnConnectionRequestResponse - "
+ "No MAPPED-ADDRESS or XOR-MAPPED-ADDRESS found in the "
+ "stun response message";
+ return;
+ }
+
+ for (size_t i = 0; i < port_->Candidates().size(); ++i) {
+ if (port_->Candidates()[i].address() == addr->GetAddress()) {
+ if (local_candidate_index_ != i) {
+ RTC_LOG(LS_INFO) << ToString()
+ << ": Updating local candidate type to srflx.";
+ local_candidate_index_ = i;
+ // SignalStateChange to force a re-sort in P2PTransportChannel as this
+ // Connection's local candidate has changed.
+ SignalStateChange(this);
+ }
+ return;
+ }
+ }
+
+ // RFC 5245
+ // Its priority is set equal to the value of the PRIORITY attribute
+ // in the Binding request.
+ const StunUInt32Attribute* priority_attr =
+ request->msg()->GetUInt32(STUN_ATTR_PRIORITY);
+ if (!priority_attr) {
+ RTC_LOG(LS_WARNING) << "Connection::OnConnectionRequestResponse - "
+ "No STUN_ATTR_PRIORITY found in the "
+ "stun response message";
+ return;
+ }
+ const uint32_t priority = priority_attr->value();
+ std::string id = rtc::CreateRandomString(8);
+
+ Candidate new_local_candidate;
+ new_local_candidate.set_id(id);
+ new_local_candidate.set_component(local_candidate().component());
+ new_local_candidate.set_type(PRFLX_PORT_TYPE);
+ new_local_candidate.set_protocol(local_candidate().protocol());
+ new_local_candidate.set_address(addr->GetAddress());
+ new_local_candidate.set_priority(priority);
+ new_local_candidate.set_username(local_candidate().username());
+ new_local_candidate.set_password(local_candidate().password());
+ new_local_candidate.set_network_name(local_candidate().network_name());
+ new_local_candidate.set_network_type(local_candidate().network_type());
+ new_local_candidate.set_related_address(local_candidate().address());
+ new_local_candidate.set_generation(local_candidate().generation());
+ new_local_candidate.set_foundation(Port::ComputeFoundation(
+ PRFLX_PORT_TYPE, local_candidate().protocol(),
+ local_candidate().relay_protocol(), local_candidate().address()));
+ new_local_candidate.set_network_id(local_candidate().network_id());
+ new_local_candidate.set_network_cost(local_candidate().network_cost());
+
+ // Change the local candidate of this Connection to the new prflx candidate.
+ RTC_LOG(LS_INFO) << ToString() << ": Updating local candidate type to prflx.";
+ local_candidate_index_ = port_->AddPrflxCandidate(new_local_candidate);
+
+ // SignalStateChange to force a re-sort in P2PTransportChannel as this
+ // Connection's local candidate has changed.
+ SignalStateChange(this);
+}
+
+void Connection::CopyCandidatesToStatsAndSanitizeIfNecessary() {
+ auto get_sanitized_copy = [](const Candidate& c) {
+ bool use_hostname_address = c.type() == LOCAL_PORT_TYPE;
+ bool filter_related_address = c.type() == STUN_PORT_TYPE;
+ return c.ToSanitizedCopy(use_hostname_address, filter_related_address);
+ };
+
+ if (port_->Network()->GetMdnsResponder() != nullptr) {
+ // When the mDNS obfuscation of local IPs is enabled, we sanitize local
+ // candidates.
+ stats_.local_candidate = get_sanitized_copy(local_candidate());
+ } else {
+ stats_.local_candidate = local_candidate();
+ }
+
+ if (!remote_candidate().address().hostname().empty()) {
+ // If the remote endpoint signaled us a hostname candidate, we assume it is
+ // supposed to be sanitized in the stats.
+ //
+ // A prflx remote candidate should not have a hostname set.
+ RTC_DCHECK(remote_candidate().type() != PRFLX_PORT_TYPE);
+ // A remote hostname candidate should have a resolved IP before we can form
+ // a candidate pair.
+ RTC_DCHECK(!remote_candidate().address().IsUnresolvedIP());
+ stats_.remote_candidate = get_sanitized_copy(remote_candidate());
+ } else {
+ stats_.remote_candidate = remote_candidate();
+ }
+}
+
+bool Connection::rtt_converged() const {
+ return rtt_samples_ > (RTT_RATIO + 1);
+}
+
+bool Connection::missing_responses(int64_t now) const {
+ if (pings_since_last_response_.empty()) {
+ return false;
+ }
+
+ int64_t waiting = now - pings_since_last_response_[0].sent_time;
+ return waiting > 2 * rtt();
+}
+
+ProxyConnection::ProxyConnection(Port* port,
+ size_t index,
+ const Candidate& remote_candidate)
+ : Connection(port, index, remote_candidate) {}
+
+int ProxyConnection::Send(const void* data,
+ size_t size,
+ const rtc::PacketOptions& options) {
+ stats_.sent_total_packets++;
+ int sent =
+ port_->SendTo(data, size, remote_candidate_.address(), options, true);
+ if (sent <= 0) {
+ RTC_DCHECK(sent < 0);
+ error_ = port_->GetError();
+ stats_.sent_discarded_packets++;
+ } else {
+ send_rate_tracker_.AddSamples(sent);
+ }
+ return sent;
+}
+
+int ProxyConnection::GetError() {
+ return error_;
+}
+
+} // namespace cricket
diff --git a/p2p/base/connection.h b/p2p/base/connection.h
new file mode 100644
index 0000000..601d34c
--- /dev/null
+++ b/p2p/base/connection.h
@@ -0,0 +1,404 @@
+/*
+ * Copyright 2019 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 P2P_BASE_CONNECTION_H_
+#define P2P_BASE_CONNECTION_H_
+
+#include <string>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/candidate.h"
+#include "logging/rtc_event_log/ice_logger.h"
+#include "p2p/base/candidate_pair_interface.h"
+#include "p2p/base/connection_info.h"
+#include "p2p/base/stun.h"
+#include "p2p/base/stun_request.h"
+#include "p2p/base/transport_description.h"
+#include "rtc_base/async_packet_socket.h"
+#include "rtc_base/message_handler.h"
+#include "rtc_base/rate_tracker.h"
+#include "rtc_base/third_party/sigslot/sigslot.h"
+
+namespace cricket {
+
+// Connection and Port has circular dependencies.
+// So we use forward declaration rather than include.
+class Port;
+
+// Forward declaration so that a ConnectionRequest can contain a Connection.
+class Connection;
+
+// A ConnectionRequest is a simple STUN ping used to determine writability.
+class ConnectionRequest : public StunRequest {
+ public:
+ explicit ConnectionRequest(Connection* connection);
+ void Prepare(StunMessage* request) override;
+ void OnResponse(StunMessage* response) override;
+ void OnErrorResponse(StunMessage* response) override;
+ void OnTimeout() override;
+ void OnSent() override;
+ int resend_delay() override;
+
+ private:
+ Connection* connection_;
+};
+
+// Represents a communication link between a port on the local client and a
+// port on the remote client.
+class Connection : public CandidatePairInterface,
+ public rtc::MessageHandler,
+ public sigslot::has_slots<> {
+ public:
+ struct SentPing {
+ SentPing(const std::string id, int64_t sent_time, uint32_t nomination)
+ : id(id), sent_time(sent_time), nomination(nomination) {}
+
+ std::string id;
+ int64_t sent_time;
+ uint32_t nomination;
+ };
+
+ ~Connection() override;
+
+ // A unique ID assigned when the connection is created.
+ uint32_t id() { return id_; }
+
+ // The local port where this connection sends and receives packets.
+ Port* port() { return port_; }
+ const Port* port() const { return port_; }
+
+ // Implementation of virtual methods in CandidatePairInterface.
+ // Returns the description of the local port
+ const Candidate& local_candidate() const override;
+ // Returns the description of the remote port to which we communicate.
+ const Candidate& remote_candidate() const override;
+
+ // Returns the pair priority.
+ uint64_t priority() const;
+
+ enum WriteState {
+ STATE_WRITABLE = 0, // we have received ping responses recently
+ STATE_WRITE_UNRELIABLE = 1, // we have had a few ping failures
+ STATE_WRITE_INIT = 2, // we have yet to receive a ping response
+ STATE_WRITE_TIMEOUT = 3, // we have had a large number of ping failures
+ };
+
+ WriteState write_state() const { return write_state_; }
+ bool writable() const { return write_state_ == STATE_WRITABLE; }
+ bool receiving() const { return receiving_; }
+
+ // Determines whether the connection has finished connecting. This can only
+ // be false for TCP connections.
+ bool connected() const { return connected_; }
+ bool weak() const { return !(writable() && receiving() && connected()); }
+ bool active() const { return write_state_ != STATE_WRITE_TIMEOUT; }
+
+ // A connection is dead if it can be safely deleted.
+ bool dead(int64_t now) const;
+
+ // Estimate of the round-trip time over this connection.
+ int rtt() const { return rtt_; }
+
+ int unwritable_timeout() const;
+ void set_unwritable_timeout(const absl::optional<int>& value_ms) {
+ unwritable_timeout_ = value_ms;
+ }
+ int unwritable_min_checks() const;
+ void set_unwritable_min_checks(const absl::optional<int>& value) {
+ unwritable_min_checks_ = value;
+ }
+ int inactive_timeout() const;
+ void set_inactive_timeout(const absl::optional<int>& value) {
+ inactive_timeout_ = value;
+ }
+
+ // Gets the |ConnectionInfo| stats, where |best_connection| has not been
+ // populated (default value false).
+ ConnectionInfo stats();
+
+ sigslot::signal1<Connection*> SignalStateChange;
+
+ // Sent when the connection has decided that it is no longer of value. It
+ // will delete itself immediately after this call.
+ sigslot::signal1<Connection*> SignalDestroyed;
+
+ // The connection can send and receive packets asynchronously. This matches
+ // the interface of AsyncPacketSocket, which may use UDP or TCP under the
+ // covers.
+ virtual int Send(const void* data,
+ size_t size,
+ const rtc::PacketOptions& options) = 0;
+
+ // Error if Send() returns < 0
+ virtual int GetError() = 0;
+
+ sigslot::signal4<Connection*, const char*, size_t, int64_t> SignalReadPacket;
+
+ sigslot::signal1<Connection*> SignalReadyToSend;
+
+ // Called when a packet is received on this connection.
+ void OnReadPacket(const char* data, size_t size, int64_t packet_time_us);
+
+ // Called when the socket is currently able to send.
+ void OnReadyToSend();
+
+ // Called when a connection is determined to be no longer useful to us. We
+ // still keep it around in case the other side wants to use it. But we can
+ // safely stop pinging on it and we can allow it to time out if the other
+ // side stops using it as well.
+ bool pruned() const { return pruned_; }
+ void Prune();
+
+ bool use_candidate_attr() const { return use_candidate_attr_; }
+ void set_use_candidate_attr(bool enable);
+
+ void set_nomination(uint32_t value) { nomination_ = value; }
+
+ uint32_t remote_nomination() const { return remote_nomination_; }
+ // One or several pairs may be nominated based on if Regular or Aggressive
+ // Nomination is used. https://tools.ietf.org/html/rfc5245#section-8
+ // |nominated| is defined both for the controlling or controlled agent based
+ // on if a nomination has been pinged or acknowledged. The controlled agent
+ // gets its |remote_nomination_| set when pinged by the controlling agent with
+ // a nomination value. The controlling agent gets its |acked_nomination_| set
+ // when receiving a response to a nominating ping.
+ bool nominated() const { return acked_nomination_ || remote_nomination_; }
+ // Public for unit tests.
+ void set_remote_nomination(uint32_t remote_nomination) {
+ remote_nomination_ = remote_nomination;
+ }
+ // Public for unit tests.
+ uint32_t acked_nomination() const { return acked_nomination_; }
+
+ void set_remote_ice_mode(IceMode mode) { remote_ice_mode_ = mode; }
+
+ int receiving_timeout() const;
+ void set_receiving_timeout(absl::optional<int> receiving_timeout_ms) {
+ receiving_timeout_ = receiving_timeout_ms;
+ }
+
+ // Makes the connection go away.
+ void Destroy();
+
+ // Makes the connection go away, in a failed state.
+ void FailAndDestroy();
+
+ // Prunes the connection and sets its state to STATE_FAILED,
+ // It will not be used or send pings although it can still receive packets.
+ void FailAndPrune();
+
+ // Checks that the state of this connection is up-to-date. The argument is
+ // the current time, which is compared against various timeouts.
+ void UpdateState(int64_t now);
+
+ // Called when this connection should try checking writability again.
+ int64_t last_ping_sent() const { return last_ping_sent_; }
+ void Ping(int64_t now);
+ void ReceivedPingResponse(int rtt, const std::string& request_id);
+ int64_t last_ping_response_received() const {
+ return last_ping_response_received_;
+ }
+ // Used to check if any STUN ping response has been received.
+ int rtt_samples() const { return rtt_samples_; }
+
+ // Called whenever a valid ping is received on this connection. This is
+ // public because the connection intercepts the first ping for us.
+ int64_t last_ping_received() const { return last_ping_received_; }
+ void ReceivedPing();
+ // Handles the binding request; sends a response if this is a valid request.
+ void HandleBindingRequest(IceMessage* msg);
+
+ int64_t last_data_received() const { return last_data_received_; }
+
+ // Debugging description of this connection
+ std::string ToDebugId() const;
+ std::string ToString() const;
+ std::string ToSensitiveString() const;
+ // Structured description of this candidate pair.
+ const webrtc::IceCandidatePairDescription& ToLogDescription();
+ void set_ice_event_log(webrtc::IceEventLog* ice_event_log) {
+ ice_event_log_ = ice_event_log;
+ }
+ // Prints pings_since_last_response_ into a string.
+ void PrintPingsSinceLastResponse(std::string* pings, size_t max);
+
+ bool reported() const { return reported_; }
+ void set_reported(bool reported) { reported_ = reported; }
+ // The following two methods are only used for logging in ToString above, and
+ // this flag is set true by P2PTransportChannel for its selected candidate
+ // pair.
+ bool selected() const { return selected_; }
+ void set_selected(bool selected) { selected_ = selected; }
+
+ // This signal will be fired if this connection is nominated by the
+ // controlling side.
+ sigslot::signal1<Connection*> SignalNominated;
+
+ // Invoked when Connection receives STUN error response with 487 code.
+ void HandleRoleConflictFromPeer();
+
+ IceCandidatePairState state() const { return state_; }
+
+ int num_pings_sent() const { return num_pings_sent_; }
+
+ IceMode remote_ice_mode() const { return remote_ice_mode_; }
+
+ uint32_t ComputeNetworkCost() const;
+
+ // Update the ICE password and/or generation of the remote candidate if the
+ // ufrag in |params| matches the candidate's ufrag, and the
+ // candidate's password and/or ufrag has not been set.
+ void MaybeSetRemoteIceParametersAndGeneration(const IceParameters& params,
+ int generation);
+
+ // If |remote_candidate_| is peer reflexive and is equivalent to
+ // |new_candidate| except the type, update |remote_candidate_| to
+ // |new_candidate|.
+ void MaybeUpdatePeerReflexiveCandidate(const Candidate& new_candidate);
+
+ // Returns the last received time of any data, stun request, or stun
+ // response in milliseconds
+ int64_t last_received() const;
+ // Returns the last time when the connection changed its receiving state.
+ int64_t receiving_unchanged_since() const {
+ return receiving_unchanged_since_;
+ }
+
+ bool stable(int64_t now) const;
+
+ protected:
+ enum { MSG_DELETE = 0, MSG_FIRST_AVAILABLE };
+
+ // Constructs a new connection to the given remote port.
+ Connection(Port* port, size_t index, const Candidate& candidate);
+
+ // Called back when StunRequestManager has a stun packet to send
+ void OnSendStunPacket(const void* data, size_t size, StunRequest* req);
+
+ // Callbacks from ConnectionRequest
+ virtual void OnConnectionRequestResponse(ConnectionRequest* req,
+ StunMessage* response);
+ void OnConnectionRequestErrorResponse(ConnectionRequest* req,
+ StunMessage* response);
+ void OnConnectionRequestTimeout(ConnectionRequest* req);
+ void OnConnectionRequestSent(ConnectionRequest* req);
+
+ bool rtt_converged() const;
+
+ // If the response is not received within 2 * RTT, the response is assumed to
+ // be missing.
+ bool missing_responses(int64_t now) const;
+
+ // Changes the state and signals if necessary.
+ void set_write_state(WriteState value);
+ void UpdateReceiving(int64_t now);
+ void set_state(IceCandidatePairState state);
+ void set_connected(bool value);
+
+ uint32_t nomination() const { return nomination_; }
+
+ void OnMessage(rtc::Message* pmsg) override;
+
+ uint32_t id_;
+ Port* port_;
+ size_t local_candidate_index_;
+ Candidate remote_candidate_;
+
+ ConnectionInfo stats_;
+ rtc::RateTracker recv_rate_tracker_;
+ rtc::RateTracker send_rate_tracker_;
+
+ private:
+ // Update the local candidate based on the mapped address attribute.
+ // If the local candidate changed, fires SignalStateChange.
+ void MaybeUpdateLocalCandidate(ConnectionRequest* request,
+ StunMessage* response);
+
+ void CopyCandidatesToStatsAndSanitizeIfNecessary();
+
+ void LogCandidatePairConfig(webrtc::IceCandidatePairConfigType type);
+ void LogCandidatePairEvent(webrtc::IceCandidatePairEventType type,
+ uint32_t transaction_id);
+
+ WriteState write_state_;
+ bool receiving_;
+ bool connected_;
+ bool pruned_;
+ bool selected_ = false;
+ // By default |use_candidate_attr_| flag will be true,
+ // as we will be using aggressive nomination.
+ // But when peer is ice-lite, this flag "must" be initialized to false and
+ // turn on when connection becomes "best connection".
+ bool use_candidate_attr_;
+ // Used by the controlling side to indicate that this connection will be
+ // selected for transmission if the peer supports ICE-renomination when this
+ // value is positive. A larger-value indicates that a connection is nominated
+ // later and should be selected by the controlled side with higher precedence.
+ // A zero-value indicates not nominating this connection.
+ uint32_t nomination_ = 0;
+ // The last nomination that has been acknowledged.
+ uint32_t acked_nomination_ = 0;
+ // Used by the controlled side to remember the nomination value received from
+ // the controlling side. When the peer does not support ICE re-nomination,
+ // its value will be 1 if the connection has been nominated.
+ uint32_t remote_nomination_ = 0;
+
+ IceMode remote_ice_mode_;
+ StunRequestManager requests_;
+ int rtt_;
+ int rtt_samples_ = 0;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-totalroundtriptime
+ uint64_t total_round_trip_time_ms_ = 0;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime
+ absl::optional<uint32_t> current_round_trip_time_ms_;
+ int64_t last_ping_sent_; // last time we sent a ping to the other side
+ int64_t last_ping_received_; // last time we received a ping from the other
+ // side
+ int64_t last_data_received_;
+ int64_t last_ping_response_received_;
+ int64_t receiving_unchanged_since_ = 0;
+ std::vector<SentPing> pings_since_last_response_;
+
+ absl::optional<int> unwritable_timeout_;
+ absl::optional<int> unwritable_min_checks_;
+ absl::optional<int> inactive_timeout_;
+
+ bool reported_;
+ IceCandidatePairState state_;
+ // Time duration to switch from receiving to not receiving.
+ absl::optional<int> receiving_timeout_;
+ int64_t time_created_ms_;
+ int num_pings_sent_ = 0;
+
+ absl::optional<webrtc::IceCandidatePairDescription> log_description_;
+ webrtc::IceEventLog* ice_event_log_ = nullptr;
+
+ friend class Port;
+ friend class ConnectionRequest;
+};
+
+// ProxyConnection defers all the interesting work to the port.
+class ProxyConnection : public Connection {
+ public:
+ ProxyConnection(Port* port, size_t index, const Candidate& remote_candidate);
+
+ int Send(const void* data,
+ size_t size,
+ const rtc::PacketOptions& options) override;
+ int GetError() override;
+
+ private:
+ int error_ = 0;
+};
+
+} // namespace cricket
+
+#endif // P2P_BASE_CONNECTION_H_
diff --git a/p2p/base/connection_info.cc b/p2p/base/connection_info.cc
new file mode 100644
index 0000000..a4f8036
--- /dev/null
+++ b/p2p/base/connection_info.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 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 "p2p/base/connection_info.h"
+
+namespace cricket {
+
+ConnectionInfo::ConnectionInfo()
+ : best_connection(false),
+ writable(false),
+ receiving(false),
+ timeout(false),
+ new_connection(false),
+ rtt(0),
+ sent_total_bytes(0),
+ sent_bytes_second(0),
+ sent_discarded_packets(0),
+ sent_total_packets(0),
+ sent_ping_requests_total(0),
+ sent_ping_requests_before_first_response(0),
+ sent_ping_responses(0),
+ recv_total_bytes(0),
+ recv_bytes_second(0),
+ recv_ping_requests(0),
+ recv_ping_responses(0),
+ key(nullptr),
+ state(IceCandidatePairState::WAITING),
+ priority(0),
+ nominated(false),
+ total_round_trip_time_ms(0) {}
+
+ConnectionInfo::ConnectionInfo(const ConnectionInfo&) = default;
+
+ConnectionInfo::~ConnectionInfo() = default;
+
+} // namespace cricket
diff --git a/p2p/base/connection_info.h b/p2p/base/connection_info.h
new file mode 100644
index 0000000..a62e8ae
--- /dev/null
+++ b/p2p/base/connection_info.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2019 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 P2P_BASE_CONNECTION_INFO_H_
+#define P2P_BASE_CONNECTION_INFO_H_
+
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/candidate.h"
+
+namespace cricket {
+
+// States are from RFC 5245. http://tools.ietf.org/html/rfc5245#section-5.7.4
+enum class IceCandidatePairState {
+ WAITING = 0, // Check has not been performed, Waiting pair on CL.
+ IN_PROGRESS, // Check has been sent, transaction is in progress.
+ SUCCEEDED, // Check already done, produced a successful result.
+ FAILED, // Check for this connection failed.
+ // According to spec there should also be a frozen state, but nothing is ever
+ // frozen because we have not implemented ICE freezing logic.
+};
+
+// Stats that we can return about the connections for a transport channel.
+// TODO(hta): Rename to ConnectionStats
+struct ConnectionInfo {
+ ConnectionInfo();
+ ConnectionInfo(const ConnectionInfo&);
+ ~ConnectionInfo();
+
+ bool best_connection; // Is this the best connection we have?
+ bool writable; // Has this connection received a STUN response?
+ bool receiving; // Has this connection received anything?
+ bool timeout; // Has this connection timed out?
+ bool new_connection; // Is this a newly created connection?
+ size_t rtt; // The STUN RTT for this connection.
+ size_t sent_total_bytes; // Total bytes sent on this connection.
+ size_t sent_bytes_second; // Bps over the last measurement interval.
+ size_t sent_discarded_packets; // Number of outgoing packets discarded due to
+ // socket errors.
+ size_t sent_total_packets; // Number of total outgoing packets attempted for
+ // sending.
+ size_t sent_ping_requests_total; // Number of STUN ping request sent.
+ size_t sent_ping_requests_before_first_response; // Number of STUN ping
+ // sent before receiving the first response.
+ size_t sent_ping_responses; // Number of STUN ping response sent.
+
+ size_t recv_total_bytes; // Total bytes received on this connection.
+ size_t recv_bytes_second; // Bps over the last measurement interval.
+ size_t recv_ping_requests; // Number of STUN ping request received.
+ size_t recv_ping_responses; // Number of STUN ping response received.
+ Candidate local_candidate; // The local candidate for this connection.
+ Candidate remote_candidate; // The remote candidate for this connection.
+ void* key; // A static value that identifies this conn.
+ // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-state
+ IceCandidatePairState state;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-priority
+ uint64_t priority;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-nominated
+ bool nominated;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-totalroundtriptime
+ uint64_t total_round_trip_time_ms;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime
+ absl::optional<uint32_t> current_round_trip_time_ms;
+};
+
+// Information about all the candidate pairs of a channel.
+typedef std::vector<ConnectionInfo> ConnectionInfos;
+
+} // namespace cricket
+
+#endif // P2P_BASE_CONNECTION_INFO_H_
diff --git a/p2p/base/ice_transport_internal.h b/p2p/base/ice_transport_internal.h
index 8f21673..93eec14 100644
--- a/p2p/base/ice_transport_internal.h
+++ b/p2p/base/ice_transport_internal.h
@@ -18,6 +18,7 @@
#include "absl/types/optional.h"
#include "api/candidate.h"
#include "api/transport/enums.h"
+#include "p2p/base/connection.h"
#include "p2p/base/packet_transport_internal.h"
#include "p2p/base/port.h"
#include "p2p/base/transport_description.h"
diff --git a/p2p/base/p2p_transport_channel.cc b/p2p/base/p2p_transport_channel.cc
index 6b6727e..57e0e12 100644
--- a/p2p/base/p2p_transport_channel.cc
+++ b/p2p/base/p2p_transport_channel.cc
@@ -19,6 +19,7 @@
#include "api/candidate.h"
#include "logging/rtc_event_log/ice_logger.h"
#include "p2p/base/candidate_pair_interface.h"
+#include "p2p/base/connection.h"
#include "p2p/base/port.h"
#include "rtc_base/checks.h"
#include "rtc_base/crc32.h"
diff --git a/p2p/base/p2p_transport_channel_unittest.cc b/p2p/base/p2p_transport_channel_unittest.cc
index 623bb7c..e20f92d 100644
--- a/p2p/base/p2p_transport_channel_unittest.cc
+++ b/p2p/base/p2p_transport_channel_unittest.cc
@@ -13,6 +13,7 @@
#include <utility>
#include "absl/memory/memory.h"
+#include "p2p/base/connection.h"
#include "p2p/base/fake_port_allocator.h"
#include "p2p/base/ice_transport_internal.h"
#include "p2p/base/mock_async_resolver.h"
diff --git a/p2p/base/port.cc b/p2p/base/port.cc
index b6fca24..9574323 100644
--- a/p2p/base/port.cc
+++ b/p2p/base/port.cc
@@ -19,6 +19,7 @@
#include "absl/algorithm/container.h"
#include "absl/memory/memory.h"
#include "absl/strings/match.h"
+#include "p2p/base/connection.h"
#include "p2p/base/port_allocator.h"
#include "rtc_base/checks.h"
#include "rtc_base/crc32.h"
@@ -35,90 +36,6 @@
namespace {
-// Determines whether we have seen at least the given maximum number of
-// pings fail to have a response.
-inline bool TooManyFailures(
- const std::vector<cricket::Connection::SentPing>& pings_since_last_response,
- uint32_t maximum_failures,
- int rtt_estimate,
- int64_t now) {
- // If we haven't sent that many pings, then we can't have failed that many.
- if (pings_since_last_response.size() < maximum_failures)
- return false;
-
- // Check if the window in which we would expect a response to the ping has
- // already elapsed.
- int64_t expected_response_time =
- pings_since_last_response[maximum_failures - 1].sent_time + rtt_estimate;
- return now > expected_response_time;
-}
-
-// Determines whether we have gone too long without seeing any response.
-inline bool TooLongWithoutResponse(
- const std::vector<cricket::Connection::SentPing>& pings_since_last_response,
- int64_t maximum_time,
- int64_t now) {
- if (pings_since_last_response.size() == 0)
- return false;
-
- auto first = pings_since_last_response[0];
- return now > (first.sent_time + maximum_time);
-}
-
-// Helper methods for converting string values of log description fields to
-// enum.
-webrtc::IceCandidateType GetCandidateTypeByString(const std::string& type) {
- if (type == cricket::LOCAL_PORT_TYPE) {
- return webrtc::IceCandidateType::kLocal;
- } else if (type == cricket::STUN_PORT_TYPE) {
- return webrtc::IceCandidateType::kStun;
- } else if (type == cricket::PRFLX_PORT_TYPE) {
- return webrtc::IceCandidateType::kPrflx;
- } else if (type == cricket::RELAY_PORT_TYPE) {
- return webrtc::IceCandidateType::kRelay;
- }
- return webrtc::IceCandidateType::kUnknown;
-}
-
-webrtc::IceCandidatePairProtocol GetProtocolByString(
- const std::string& protocol) {
- if (protocol == cricket::UDP_PROTOCOL_NAME) {
- return webrtc::IceCandidatePairProtocol::kUdp;
- } else if (protocol == cricket::TCP_PROTOCOL_NAME) {
- return webrtc::IceCandidatePairProtocol::kTcp;
- } else if (protocol == cricket::SSLTCP_PROTOCOL_NAME) {
- return webrtc::IceCandidatePairProtocol::kSsltcp;
- } else if (protocol == cricket::TLS_PROTOCOL_NAME) {
- return webrtc::IceCandidatePairProtocol::kTls;
- }
- return webrtc::IceCandidatePairProtocol::kUnknown;
-}
-
-webrtc::IceCandidatePairAddressFamily GetAddressFamilyByInt(
- int address_family) {
- if (address_family == AF_INET) {
- return webrtc::IceCandidatePairAddressFamily::kIpv4;
- } else if (address_family == AF_INET6) {
- return webrtc::IceCandidatePairAddressFamily::kIpv6;
- }
- return webrtc::IceCandidatePairAddressFamily::kUnknown;
-}
-
-webrtc::IceCandidateNetworkType ConvertNetworkType(rtc::AdapterType type) {
- if (type == rtc::ADAPTER_TYPE_ETHERNET) {
- return webrtc::IceCandidateNetworkType::kEthernet;
- } else if (type == rtc::ADAPTER_TYPE_LOOPBACK) {
- return webrtc::IceCandidateNetworkType::kLoopback;
- } else if (type == rtc::ADAPTER_TYPE_WIFI) {
- return webrtc::IceCandidateNetworkType::kWifi;
- } else if (type == rtc::ADAPTER_TYPE_VPN) {
- return webrtc::IceCandidateNetworkType::kVpn;
- } else if (type == rtc::ADAPTER_TYPE_CELLULAR) {
- return webrtc::IceCandidateNetworkType::kCellular;
- }
- return webrtc::IceCandidateNetworkType::kUnknown;
-}
-
rtc::PacketInfoProtocolType ConvertProtocolTypeToPacketInfoProtocolType(
cricket::ProtocolType type) {
switch (type) {
@@ -135,29 +52,10 @@
}
}
-// We will restrict RTT estimates (when used for determining state) to be
-// within a reasonable range.
-const int MINIMUM_RTT = 100; // 0.1 seconds
-const int MAXIMUM_RTT = 60000; // 60 seconds
-
-// When we don't have any RTT data, we have to pick something reasonable. We
-// use a large value just in case the connection is really slow.
-const int DEFAULT_RTT = 3000; // 3 seconds
-
-// Computes our estimate of the RTT given the current estimate.
-inline int ConservativeRTTEstimate(int rtt) {
- return rtc::SafeClamp(2 * rtt, MINIMUM_RTT, MAXIMUM_RTT);
-}
-
-// Weighting of the old rtt value to new data.
-const int RTT_RATIO = 3; // 3 : 1
-
// The delay before we begin checking if this port is useless. We set
// it to a little higher than a total STUN timeout.
const int kPortTimeoutDelay = cricket::STUN_TOTAL_TIMEOUT + 5000;
-constexpr int64_t kMinExtraPingDelayMs = 100;
-
} // namespace
namespace cricket {
@@ -196,16 +94,10 @@
const char TCPTYPE_PASSIVE_STR[] = "passive";
const char TCPTYPE_SIMOPEN_STR[] = "so";
-// Foundation: An arbitrary string that is the same for two candidates
-// that have the same type, base IP address, protocol (UDP, TCP,
-// etc.), and STUN or TURN server. If any of these are different,
-// then the foundation will be different. Two candidate pairs with
-// the same foundation pairs are likely to have similar network
-// characteristics. Foundations are used in the frozen algorithm.
-static std::string ComputeFoundation(const std::string& type,
- const std::string& protocol,
- const std::string& relay_protocol,
- const rtc::SocketAddress& base_address) {
+std::string Port::ComputeFoundation(const std::string& type,
+ const std::string& protocol,
+ const std::string& relay_protocol,
+ const rtc::SocketAddress& base_address) {
rtc::StringBuilder sb;
sb << type << base_address.ipaddr().ToString() << protocol << relay_protocol;
return rtc::ToString(rtc::ComputeCrc32(sb.Release()));
@@ -221,34 +113,6 @@
CandidateStats::~CandidateStats() = default;
-ConnectionInfo::ConnectionInfo()
- : best_connection(false),
- writable(false),
- receiving(false),
- timeout(false),
- new_connection(false),
- rtt(0),
- sent_total_bytes(0),
- sent_bytes_second(0),
- sent_discarded_packets(0),
- sent_total_packets(0),
- sent_ping_requests_total(0),
- sent_ping_requests_before_first_response(0),
- sent_ping_responses(0),
- recv_total_bytes(0),
- recv_bytes_second(0),
- recv_ping_requests(0),
- recv_ping_responses(0),
- key(nullptr),
- state(IceCandidatePairState::WAITING),
- priority(0),
- nominated(false),
- total_round_trip_time_ms(0) {}
-
-ConnectionInfo::ConnectionInfo(const ConnectionInfo&) = default;
-
-ConnectionInfo::~ConnectionInfo() = default;
-
Port::Port(rtc::Thread* thread,
const std::string& type,
rtc::PacketSocketFactory* factory,
@@ -1001,997 +865,4 @@
info->network_id = Network()->id();
}
-// A ConnectionRequest is a simple STUN ping used to determine writability.
-class ConnectionRequest : public StunRequest {
- public:
- explicit ConnectionRequest(Connection* connection)
- : StunRequest(new IceMessage()), connection_(connection) {}
-
- void Prepare(StunMessage* request) override {
- request->SetType(STUN_BINDING_REQUEST);
- std::string username;
- connection_->port()->CreateStunUsername(
- connection_->remote_candidate().username(), &username);
- request->AddAttribute(absl::make_unique<StunByteStringAttribute>(
- STUN_ATTR_USERNAME, username));
-
- // connection_ already holds this ping, so subtract one from count.
- if (connection_->port()->send_retransmit_count_attribute()) {
- request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
- STUN_ATTR_RETRANSMIT_COUNT,
- static_cast<uint32_t>(connection_->pings_since_last_response_.size() -
- 1)));
- }
- uint32_t network_info = connection_->port()->Network()->id();
- network_info = (network_info << 16) | connection_->port()->network_cost();
- request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
- STUN_ATTR_NETWORK_INFO, network_info));
-
- // Adding ICE_CONTROLLED or ICE_CONTROLLING attribute based on the role.
- if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLING) {
- request->AddAttribute(absl::make_unique<StunUInt64Attribute>(
- STUN_ATTR_ICE_CONTROLLING, connection_->port()->IceTiebreaker()));
- // We should have either USE_CANDIDATE attribute or ICE_NOMINATION
- // attribute but not both. That was enforced in p2ptransportchannel.
- if (connection_->use_candidate_attr()) {
- request->AddAttribute(absl::make_unique<StunByteStringAttribute>(
- STUN_ATTR_USE_CANDIDATE));
- }
- if (connection_->nomination() &&
- connection_->nomination() != connection_->acked_nomination()) {
- request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
- STUN_ATTR_NOMINATION, connection_->nomination()));
- }
- } else if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLED) {
- request->AddAttribute(absl::make_unique<StunUInt64Attribute>(
- STUN_ATTR_ICE_CONTROLLED, connection_->port()->IceTiebreaker()));
- } else {
- RTC_NOTREACHED();
- }
-
- // Adding PRIORITY Attribute.
- // Changing the type preference to Peer Reflexive and local preference
- // and component id information is unchanged from the original priority.
- // priority = (2^24)*(type preference) +
- // (2^8)*(local preference) +
- // (2^0)*(256 - component ID)
- uint32_t type_preference =
- (connection_->local_candidate().protocol() == TCP_PROTOCOL_NAME)
- ? ICE_TYPE_PREFERENCE_PRFLX_TCP
- : ICE_TYPE_PREFERENCE_PRFLX;
- uint32_t prflx_priority =
- type_preference << 24 |
- (connection_->local_candidate().priority() & 0x00FFFFFF);
- request->AddAttribute(absl::make_unique<StunUInt32Attribute>(
- STUN_ATTR_PRIORITY, prflx_priority));
-
- // Adding Message Integrity attribute.
- request->AddMessageIntegrity(connection_->remote_candidate().password());
- // Adding Fingerprint.
- request->AddFingerprint();
- }
-
- void OnResponse(StunMessage* response) override {
- connection_->OnConnectionRequestResponse(this, response);
- }
-
- void OnErrorResponse(StunMessage* response) override {
- connection_->OnConnectionRequestErrorResponse(this, response);
- }
-
- void OnTimeout() override { connection_->OnConnectionRequestTimeout(this); }
-
- void OnSent() override {
- connection_->OnConnectionRequestSent(this);
- // Each request is sent only once. After a single delay , the request will
- // time out.
- timeout_ = true;
- }
-
- int resend_delay() override { return CONNECTION_RESPONSE_TIMEOUT; }
-
- private:
- Connection* connection_;
-};
-
-//
-// Connection
-//
-
-Connection::Connection(Port* port,
- size_t index,
- const Candidate& remote_candidate)
- : id_(rtc::CreateRandomId()),
- port_(port),
- local_candidate_index_(index),
- remote_candidate_(remote_candidate),
- recv_rate_tracker_(100, 10u),
- send_rate_tracker_(100, 10u),
- write_state_(STATE_WRITE_INIT),
- receiving_(false),
- connected_(true),
- pruned_(false),
- use_candidate_attr_(false),
- remote_ice_mode_(ICEMODE_FULL),
- requests_(port->thread()),
- rtt_(DEFAULT_RTT),
- last_ping_sent_(0),
- last_ping_received_(0),
- last_data_received_(0),
- last_ping_response_received_(0),
- reported_(false),
- state_(IceCandidatePairState::WAITING),
- time_created_ms_(rtc::TimeMillis()) {
- // All of our connections start in WAITING state.
- // TODO(mallinath) - Start connections from STATE_FROZEN.
- // Wire up to send stun packets
- requests_.SignalSendPacket.connect(this, &Connection::OnSendStunPacket);
- RTC_LOG(LS_INFO) << ToString() << ": Connection created";
-}
-
-Connection::~Connection() {}
-
-const Candidate& Connection::local_candidate() const {
- RTC_DCHECK(local_candidate_index_ < port_->Candidates().size());
- return port_->Candidates()[local_candidate_index_];
-}
-
-const Candidate& Connection::remote_candidate() const {
- return remote_candidate_;
-}
-
-uint64_t Connection::priority() const {
- uint64_t priority = 0;
- // RFC 5245 - 5.7.2. Computing Pair Priority and Ordering Pairs
- // Let G be the priority for the candidate provided by the controlling
- // agent. Let D be the priority for the candidate provided by the
- // controlled agent.
- // pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)
- IceRole role = port_->GetIceRole();
- if (role != ICEROLE_UNKNOWN) {
- uint32_t g = 0;
- uint32_t d = 0;
- if (role == ICEROLE_CONTROLLING) {
- g = local_candidate().priority();
- d = remote_candidate_.priority();
- } else {
- g = remote_candidate_.priority();
- d = local_candidate().priority();
- }
- priority = std::min(g, d);
- priority = priority << 32;
- priority += 2 * std::max(g, d) + (g > d ? 1 : 0);
- }
- return priority;
-}
-
-void Connection::set_write_state(WriteState value) {
- WriteState old_value = write_state_;
- write_state_ = value;
- if (value != old_value) {
- RTC_LOG(LS_VERBOSE) << ToString() << ": set_write_state from: " << old_value
- << " to " << value;
- SignalStateChange(this);
- }
-}
-
-void Connection::UpdateReceiving(int64_t now) {
- bool receiving;
- if (last_ping_sent() < last_ping_response_received()) {
- // We consider any candidate pair that has its last connectivity check
- // acknowledged by a response as receiving, particularly for backup
- // candidate pairs that send checks at a much slower pace than the selected
- // one. Otherwise, a backup candidate pair constantly becomes not receiving
- // as a side effect of a long ping interval, since we do not have a separate
- // receiving timeout for backup candidate pairs. See
- // IceConfig.ice_backup_candidate_pair_ping_interval,
- // IceConfig.ice_connection_receiving_timeout and their default value.
- receiving = true;
- } else {
- receiving =
- last_received() > 0 && now <= last_received() + receiving_timeout();
- }
- if (receiving_ == receiving) {
- return;
- }
- RTC_LOG(LS_VERBOSE) << ToString() << ": set_receiving to " << receiving;
- receiving_ = receiving;
- receiving_unchanged_since_ = now;
- SignalStateChange(this);
-}
-
-void Connection::set_state(IceCandidatePairState state) {
- IceCandidatePairState old_state = state_;
- state_ = state;
- if (state != old_state) {
- RTC_LOG(LS_VERBOSE) << ToString() << ": set_state";
- }
-}
-
-void Connection::set_connected(bool value) {
- bool old_value = connected_;
- connected_ = value;
- if (value != old_value) {
- RTC_LOG(LS_VERBOSE) << ToString() << ": Change connected_ to " << value;
- SignalStateChange(this);
- }
-}
-
-void Connection::set_use_candidate_attr(bool enable) {
- use_candidate_attr_ = enable;
-}
-
-int Connection::unwritable_timeout() const {
- return unwritable_timeout_.value_or(CONNECTION_WRITE_CONNECT_TIMEOUT);
-}
-
-int Connection::unwritable_min_checks() const {
- return unwritable_min_checks_.value_or(CONNECTION_WRITE_CONNECT_FAILURES);
-}
-
-int Connection::inactive_timeout() const {
- return inactive_timeout_.value_or(CONNECTION_WRITE_TIMEOUT);
-}
-
-int Connection::receiving_timeout() const {
- return receiving_timeout_.value_or(WEAK_CONNECTION_RECEIVE_TIMEOUT);
-}
-
-void Connection::OnSendStunPacket(const void* data,
- size_t size,
- StunRequest* req) {
- rtc::PacketOptions options(port_->StunDscpValue());
- options.info_signaled_after_sent.packet_type =
- rtc::PacketType::kIceConnectivityCheck;
- auto err =
- port_->SendTo(data, size, remote_candidate_.address(), options, false);
- if (err < 0) {
- RTC_LOG(LS_WARNING) << ToString()
- << ": Failed to send STUN ping "
- " err="
- << err << " id=" << rtc::hex_encode(req->id());
- }
-}
-
-void Connection::OnReadPacket(const char* data,
- size_t size,
- int64_t packet_time_us) {
- std::unique_ptr<IceMessage> msg;
- std::string remote_ufrag;
- const rtc::SocketAddress& addr(remote_candidate_.address());
- if (!port_->GetStunMessage(data, size, addr, &msg, &remote_ufrag)) {
- // The packet did not parse as a valid STUN message
- // This is a data packet, pass it along.
- last_data_received_ = rtc::TimeMillis();
- UpdateReceiving(last_data_received_);
- recv_rate_tracker_.AddSamples(size);
- SignalReadPacket(this, data, size, packet_time_us);
-
- // If timed out sending writability checks, start up again
- if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) {
- RTC_LOG(LS_WARNING)
- << "Received a data packet on a timed-out Connection. "
- "Resetting state to STATE_WRITE_INIT.";
- set_write_state(STATE_WRITE_INIT);
- }
- } else if (!msg) {
- // The packet was STUN, but failed a check and was handled internally.
- } else {
- // The packet is STUN and passed the Port checks.
- // Perform our own checks to ensure this packet is valid.
- // If this is a STUN request, then update the receiving bit and respond.
- // If this is a STUN response, then update the writable bit.
- // Log at LS_INFO if we receive a ping on an unwritable connection.
- rtc::LoggingSeverity sev = (!writable() ? rtc::LS_INFO : rtc::LS_VERBOSE);
- switch (msg->type()) {
- case STUN_BINDING_REQUEST:
- RTC_LOG_V(sev) << ToString() << ": Received STUN ping, id="
- << rtc::hex_encode(msg->transaction_id());
-
- if (remote_ufrag == remote_candidate_.username()) {
- HandleBindingRequest(msg.get());
- } else {
- // The packet had the right local username, but the remote username
- // was not the right one for the remote address.
- RTC_LOG(LS_ERROR)
- << ToString()
- << ": Received STUN request with bad remote username "
- << remote_ufrag;
- port_->SendBindingErrorResponse(msg.get(), addr,
- STUN_ERROR_UNAUTHORIZED,
- STUN_ERROR_REASON_UNAUTHORIZED);
- }
- break;
-
- // Response from remote peer. Does it match request sent?
- // This doesn't just check, it makes callbacks if transaction
- // id's match.
- case STUN_BINDING_RESPONSE:
- case STUN_BINDING_ERROR_RESPONSE:
- if (msg->ValidateMessageIntegrity(data, size,
- remote_candidate().password())) {
- requests_.CheckResponse(msg.get());
- }
- // Otherwise silently discard the response message.
- break;
-
- // Remote end point sent an STUN indication instead of regular binding
- // request. In this case |last_ping_received_| will be updated but no
- // response will be sent.
- case STUN_BINDING_INDICATION:
- ReceivedPing();
- break;
-
- default:
- RTC_NOTREACHED();
- break;
- }
- }
-}
-
-void Connection::HandleBindingRequest(IceMessage* msg) {
- // This connection should now be receiving.
- ReceivedPing();
- if (webrtc::field_trial::IsEnabled("WebRTC-ExtraICEPing") &&
- last_ping_response_received_ == 0) {
- if (local_candidate().type() == RELAY_PORT_TYPE ||
- local_candidate().type() == PRFLX_PORT_TYPE ||
- remote_candidate().type() == RELAY_PORT_TYPE ||
- remote_candidate().type() == PRFLX_PORT_TYPE) {
- const int64_t now = rtc::TimeMillis();
- if (last_ping_sent_ + kMinExtraPingDelayMs <= now) {
- RTC_LOG(LS_INFO) << ToString()
- << "WebRTC-ExtraICEPing/Sending extra ping"
- << " last_ping_sent_: " << last_ping_sent_
- << " now: " << now
- << " (diff: " << (now - last_ping_sent_) << ")";
- Ping(now);
- } else {
- RTC_LOG(LS_INFO) << ToString()
- << "WebRTC-ExtraICEPing/Not sending extra ping"
- << " last_ping_sent_: " << last_ping_sent_
- << " now: " << now
- << " (diff: " << (now - last_ping_sent_) << ")";
- }
- }
- }
-
- const rtc::SocketAddress& remote_addr = remote_candidate_.address();
- const std::string& remote_ufrag = remote_candidate_.username();
- // Check for role conflicts.
- if (!port_->MaybeIceRoleConflict(remote_addr, msg, remote_ufrag)) {
- // Received conflicting role from the peer.
- RTC_LOG(LS_INFO) << "Received conflicting role from the peer.";
- return;
- }
-
- stats_.recv_ping_requests++;
- LogCandidatePairEvent(webrtc::IceCandidatePairEventType::kCheckReceived,
- msg->reduced_transaction_id());
-
- // This is a validated stun request from remote peer.
- port_->SendBindingResponse(msg, remote_addr);
-
- // If it timed out on writing check, start up again
- if (!pruned_ && write_state_ == STATE_WRITE_TIMEOUT) {
- set_write_state(STATE_WRITE_INIT);
- }
-
- if (port_->GetIceRole() == ICEROLE_CONTROLLED) {
- const StunUInt32Attribute* nomination_attr =
- msg->GetUInt32(STUN_ATTR_NOMINATION);
- uint32_t nomination = 0;
- if (nomination_attr) {
- nomination = nomination_attr->value();
- if (nomination == 0) {
- RTC_LOG(LS_ERROR) << "Invalid nomination: " << nomination;
- }
- } else {
- const StunByteStringAttribute* use_candidate_attr =
- msg->GetByteString(STUN_ATTR_USE_CANDIDATE);
- if (use_candidate_attr) {
- nomination = 1;
- }
- }
- // We don't un-nominate a connection, so we only keep a larger nomination.
- if (nomination > remote_nomination_) {
- set_remote_nomination(nomination);
- SignalNominated(this);
- }
- }
- // Set the remote cost if the network_info attribute is available.
- // Note: If packets are re-ordered, we may get incorrect network cost
- // temporarily, but it should get the correct value shortly after that.
- const StunUInt32Attribute* network_attr =
- msg->GetUInt32(STUN_ATTR_NETWORK_INFO);
- if (network_attr) {
- uint32_t network_info = network_attr->value();
- uint16_t network_cost = static_cast<uint16_t>(network_info);
- if (network_cost != remote_candidate_.network_cost()) {
- remote_candidate_.set_network_cost(network_cost);
- // Network cost change will affect the connection ranking, so signal
- // state change to force a re-sort in P2PTransportChannel.
- SignalStateChange(this);
- }
- }
-}
-
-void Connection::OnReadyToSend() {
- SignalReadyToSend(this);
-}
-
-void Connection::Prune() {
- if (!pruned_ || active()) {
- RTC_LOG(LS_INFO) << ToString() << ": Connection pruned";
- pruned_ = true;
- requests_.Clear();
- set_write_state(STATE_WRITE_TIMEOUT);
- }
-}
-
-void Connection::Destroy() {
- // TODO(deadbeef, nisse): This may leak if an application closes a
- // PeerConnection and then quickly destroys the PeerConnectionFactory (along
- // with the networking thread on which this message is posted). Also affects
- // tests, with a workaround in
- // AutoSocketServerThread::~AutoSocketServerThread.
- RTC_LOG(LS_VERBOSE) << ToString() << ": Connection destroyed";
- port_->thread()->Post(RTC_FROM_HERE, this, MSG_DELETE);
- LogCandidatePairConfig(webrtc::IceCandidatePairConfigType::kDestroyed);
-}
-
-void Connection::FailAndDestroy() {
- set_state(IceCandidatePairState::FAILED);
- Destroy();
-}
-
-void Connection::FailAndPrune() {
- set_state(IceCandidatePairState::FAILED);
- Prune();
-}
-
-void Connection::PrintPingsSinceLastResponse(std::string* s, size_t max) {
- rtc::StringBuilder oss;
- if (pings_since_last_response_.size() > max) {
- for (size_t i = 0; i < max; i++) {
- const SentPing& ping = pings_since_last_response_[i];
- oss << rtc::hex_encode(ping.id) << " ";
- }
- oss << "... " << (pings_since_last_response_.size() - max) << " more";
- } else {
- for (const SentPing& ping : pings_since_last_response_) {
- oss << rtc::hex_encode(ping.id) << " ";
- }
- }
- *s = oss.str();
-}
-
-void Connection::UpdateState(int64_t now) {
- int rtt = ConservativeRTTEstimate(rtt_);
-
- if (RTC_LOG_CHECK_LEVEL(LS_VERBOSE)) {
- std::string pings;
- PrintPingsSinceLastResponse(&pings, 5);
- RTC_LOG(LS_VERBOSE) << ToString()
- << ": UpdateState()"
- ", ms since last received response="
- << now - last_ping_response_received_
- << ", ms since last received data="
- << now - last_data_received_ << ", rtt=" << rtt
- << ", pings_since_last_response=" << pings;
- }
-
- // Check the writable state. (The order of these checks is important.)
- //
- // Before becoming unwritable, we allow for a fixed number of pings to fail
- // (i.e., receive no response). We also have to give the response time to
- // get back, so we include a conservative estimate of this.
- //
- // Before timing out writability, we give a fixed amount of time. This is to
- // allow for changes in network conditions.
-
- if ((write_state_ == STATE_WRITABLE) &&
- TooManyFailures(pings_since_last_response_, unwritable_min_checks(), rtt,
- now) &&
- TooLongWithoutResponse(pings_since_last_response_, unwritable_timeout(),
- now)) {
- uint32_t max_pings = unwritable_min_checks();
- RTC_LOG(LS_INFO) << ToString() << ": Unwritable after " << max_pings
- << " ping failures and "
- << now - pings_since_last_response_[0].sent_time
- << " ms without a response,"
- " ms since last received ping="
- << now - last_ping_received_
- << " ms since last received data="
- << now - last_data_received_ << " rtt=" << rtt;
- set_write_state(STATE_WRITE_UNRELIABLE);
- }
- if ((write_state_ == STATE_WRITE_UNRELIABLE ||
- write_state_ == STATE_WRITE_INIT) &&
- TooLongWithoutResponse(pings_since_last_response_, inactive_timeout(),
- now)) {
- RTC_LOG(LS_INFO) << ToString() << ": Timed out after "
- << now - pings_since_last_response_[0].sent_time
- << " ms without a response, rtt=" << rtt;
- set_write_state(STATE_WRITE_TIMEOUT);
- }
-
- // Update the receiving state.
- UpdateReceiving(now);
- if (dead(now)) {
- Destroy();
- }
-}
-
-void Connection::Ping(int64_t now) {
- last_ping_sent_ = now;
- ConnectionRequest* req = new ConnectionRequest(this);
- // If not using renomination, we use "1" to mean "nominated" and "0" to mean
- // "not nominated". If using renomination, values greater than 1 are used for
- // re-nominated pairs.
- int nomination = use_candidate_attr_ ? 1 : 0;
- if (nomination_ > 0) {
- nomination = nomination_;
- }
- pings_since_last_response_.push_back(SentPing(req->id(), now, nomination));
- RTC_LOG(LS_VERBOSE) << ToString() << ": Sending STUN ping, id="
- << rtc::hex_encode(req->id())
- << ", nomination=" << nomination_;
- requests_.Send(req);
- state_ = IceCandidatePairState::IN_PROGRESS;
- num_pings_sent_++;
-}
-
-void Connection::ReceivedPing() {
- last_ping_received_ = rtc::TimeMillis();
- UpdateReceiving(last_ping_received_);
-}
-
-void Connection::ReceivedPingResponse(int rtt, const std::string& request_id) {
- RTC_DCHECK_GE(rtt, 0);
- // We've already validated that this is a STUN binding response with
- // the correct local and remote username for this connection.
- // So if we're not already, become writable. We may be bringing a pruned
- // connection back to life, but if we don't really want it, we can always
- // prune it again.
- auto iter = absl::c_find_if(
- pings_since_last_response_,
- [request_id](const SentPing& ping) { return ping.id == request_id; });
- if (iter != pings_since_last_response_.end() &&
- iter->nomination > acked_nomination_) {
- acked_nomination_ = iter->nomination;
- }
-
- total_round_trip_time_ms_ += rtt;
- current_round_trip_time_ms_ = static_cast<uint32_t>(rtt);
-
- pings_since_last_response_.clear();
- last_ping_response_received_ = rtc::TimeMillis();
- UpdateReceiving(last_ping_response_received_);
- set_write_state(STATE_WRITABLE);
- set_state(IceCandidatePairState::SUCCEEDED);
- if (rtt_samples_ > 0) {
- rtt_ = rtc::GetNextMovingAverage(rtt_, rtt, RTT_RATIO);
- } else {
- rtt_ = rtt;
- }
- rtt_samples_++;
-}
-
-bool Connection::dead(int64_t now) const {
- if (last_received() > 0) {
- // If it has ever received anything, we keep it alive until it hasn't
- // received anything for DEAD_CONNECTION_RECEIVE_TIMEOUT. This covers the
- // normal case of a successfully used connection that stops working. This
- // also allows a remote peer to continue pinging over a locally inactive
- // (pruned) connection.
- return (now > (last_received() + DEAD_CONNECTION_RECEIVE_TIMEOUT));
- }
-
- if (active()) {
- // If it has never received anything, keep it alive as long as it is
- // actively pinging and not pruned. Otherwise, the connection might be
- // deleted before it has a chance to ping. This is the normal case for a
- // new connection that is pinging but hasn't received anything yet.
- return false;
- }
-
- // If it has never received anything and is not actively pinging (pruned), we
- // keep it around for at least MIN_CONNECTION_LIFETIME to prevent connections
- // from being pruned too quickly during a network change event when two
- // networks would be up simultaneously but only for a brief period.
- return now > (time_created_ms_ + MIN_CONNECTION_LIFETIME);
-}
-
-bool Connection::stable(int64_t now) const {
- // A connection is stable if it's RTT has converged and it isn't missing any
- // responses. We should send pings at a higher rate until the RTT converges
- // and whenever a ping response is missing (so that we can detect
- // unwritability faster)
- return rtt_converged() && !missing_responses(now);
-}
-
-std::string Connection::ToDebugId() const {
- return rtc::ToHex(reinterpret_cast<uintptr_t>(this));
-}
-
-uint32_t Connection::ComputeNetworkCost() const {
- // TODO(honghaiz): Will add rtt as part of the network cost.
- return port()->network_cost() + remote_candidate_.network_cost();
-}
-
-std::string Connection::ToString() const {
- const absl::string_view CONNECT_STATE_ABBREV[2] = {
- "-", // not connected (false)
- "C", // connected (true)
- };
- const absl::string_view RECEIVE_STATE_ABBREV[2] = {
- "-", // not receiving (false)
- "R", // receiving (true)
- };
- const absl::string_view WRITE_STATE_ABBREV[4] = {
- "W", // STATE_WRITABLE
- "w", // STATE_WRITE_UNRELIABLE
- "-", // STATE_WRITE_INIT
- "x", // STATE_WRITE_TIMEOUT
- };
- const absl::string_view ICESTATE[4] = {
- "W", // STATE_WAITING
- "I", // STATE_INPROGRESS
- "S", // STATE_SUCCEEDED
- "F" // STATE_FAILED
- };
- const absl::string_view SELECTED_STATE_ABBREV[2] = {
- "-", // candidate pair not selected (false)
- "S", // selected (true)
- };
- const Candidate& local = local_candidate();
- const Candidate& remote = remote_candidate();
- rtc::StringBuilder ss;
- ss << "Conn[" << ToDebugId() << ":" << port_->content_name() << ":"
- << port_->Network()->ToString() << ":" << local.id() << ":"
- << local.component() << ":" << local.generation() << ":" << local.type()
- << ":" << local.protocol() << ":" << local.address().ToSensitiveString()
- << "->" << remote.id() << ":" << remote.component() << ":"
- << remote.priority() << ":" << remote.type() << ":" << remote.protocol()
- << ":" << remote.address().ToSensitiveString() << "|"
- << CONNECT_STATE_ABBREV[connected()] << RECEIVE_STATE_ABBREV[receiving()]
- << WRITE_STATE_ABBREV[write_state()] << ICESTATE[static_cast<int>(state())]
- << "|" << SELECTED_STATE_ABBREV[selected()] << "|" << remote_nomination()
- << "|" << nomination() << "|" << priority() << "|";
- if (rtt_ < DEFAULT_RTT) {
- ss << rtt_ << "]";
- } else {
- ss << "-]";
- }
- return ss.Release();
-}
-
-std::string Connection::ToSensitiveString() const {
- return ToString();
-}
-
-const webrtc::IceCandidatePairDescription& Connection::ToLogDescription() {
- if (log_description_.has_value()) {
- return log_description_.value();
- }
- const Candidate& local = local_candidate();
- const Candidate& remote = remote_candidate();
- const rtc::Network* network = port()->Network();
- log_description_ = webrtc::IceCandidatePairDescription();
- log_description_->local_candidate_type =
- GetCandidateTypeByString(local.type());
- log_description_->local_relay_protocol =
- GetProtocolByString(local.relay_protocol());
- log_description_->local_network_type = ConvertNetworkType(network->type());
- log_description_->local_address_family =
- GetAddressFamilyByInt(local.address().family());
- log_description_->remote_candidate_type =
- GetCandidateTypeByString(remote.type());
- log_description_->remote_address_family =
- GetAddressFamilyByInt(remote.address().family());
- log_description_->candidate_pair_protocol =
- GetProtocolByString(local.protocol());
- return log_description_.value();
-}
-
-void Connection::LogCandidatePairConfig(
- webrtc::IceCandidatePairConfigType type) {
- if (ice_event_log_ == nullptr) {
- return;
- }
- ice_event_log_->LogCandidatePairConfig(type, id(), ToLogDescription());
-}
-
-void Connection::LogCandidatePairEvent(webrtc::IceCandidatePairEventType type,
- uint32_t transaction_id) {
- if (ice_event_log_ == nullptr) {
- return;
- }
- ice_event_log_->LogCandidatePairEvent(type, id(), transaction_id);
-}
-
-void Connection::OnConnectionRequestResponse(ConnectionRequest* request,
- StunMessage* response) {
- // Log at LS_INFO if we receive a ping response on an unwritable
- // connection.
- rtc::LoggingSeverity sev = !writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
-
- int rtt = request->Elapsed();
-
- if (RTC_LOG_CHECK_LEVEL_V(sev)) {
- std::string pings;
- PrintPingsSinceLastResponse(&pings, 5);
- RTC_LOG_V(sev) << ToString() << ": Received STUN ping response, id="
- << rtc::hex_encode(request->id())
- << ", code=0" // Makes logging easier to parse.
- ", rtt="
- << rtt << ", pings_since_last_response=" << pings;
- }
- ReceivedPingResponse(rtt, request->id());
-
- stats_.recv_ping_responses++;
- LogCandidatePairEvent(
- webrtc::IceCandidatePairEventType::kCheckResponseReceived,
- response->reduced_transaction_id());
-
- MaybeUpdateLocalCandidate(request, response);
-}
-
-void Connection::OnConnectionRequestErrorResponse(ConnectionRequest* request,
- StunMessage* response) {
- int error_code = response->GetErrorCodeValue();
- RTC_LOG(LS_WARNING) << ToString() << ": Received STUN error response id="
- << rtc::hex_encode(request->id())
- << " code=" << error_code
- << " rtt=" << request->Elapsed();
-
- if (error_code == STUN_ERROR_UNKNOWN_ATTRIBUTE ||
- error_code == STUN_ERROR_SERVER_ERROR ||
- error_code == STUN_ERROR_UNAUTHORIZED) {
- // Recoverable error, retry
- } else if (error_code == STUN_ERROR_STALE_CREDENTIALS) {
- // Race failure, retry
- } else if (error_code == STUN_ERROR_ROLE_CONFLICT) {
- HandleRoleConflictFromPeer();
- } else {
- // This is not a valid connection.
- RTC_LOG(LS_ERROR) << ToString()
- << ": Received STUN error response, code=" << error_code
- << "; killing connection";
- FailAndDestroy();
- }
-}
-
-void Connection::OnConnectionRequestTimeout(ConnectionRequest* request) {
- // Log at LS_INFO if we miss a ping on a writable connection.
- rtc::LoggingSeverity sev = writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
- RTC_LOG_V(sev) << ToString() << ": Timing-out STUN ping "
- << rtc::hex_encode(request->id()) << " after "
- << request->Elapsed() << " ms";
-}
-
-void Connection::OnConnectionRequestSent(ConnectionRequest* request) {
- // Log at LS_INFO if we send a ping on an unwritable connection.
- rtc::LoggingSeverity sev = !writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
- RTC_LOG_V(sev) << ToString()
- << ": Sent STUN ping, id=" << rtc::hex_encode(request->id())
- << ", use_candidate=" << use_candidate_attr()
- << ", nomination=" << nomination();
- stats_.sent_ping_requests_total++;
- LogCandidatePairEvent(webrtc::IceCandidatePairEventType::kCheckSent,
- request->reduced_transaction_id());
- if (stats_.recv_ping_responses == 0) {
- stats_.sent_ping_requests_before_first_response++;
- }
-}
-
-void Connection::HandleRoleConflictFromPeer() {
- port_->SignalRoleConflict(port_);
-}
-
-void Connection::MaybeSetRemoteIceParametersAndGeneration(
- const IceParameters& ice_params,
- int generation) {
- if (remote_candidate_.username() == ice_params.ufrag &&
- remote_candidate_.password().empty()) {
- remote_candidate_.set_password(ice_params.pwd);
- }
- // TODO(deadbeef): A value of '0' for the generation is used for both
- // generation 0 and "generation unknown". It should be changed to an
- // absl::optional to fix this.
- if (remote_candidate_.username() == ice_params.ufrag &&
- remote_candidate_.password() == ice_params.pwd &&
- remote_candidate_.generation() == 0) {
- remote_candidate_.set_generation(generation);
- }
-}
-
-void Connection::MaybeUpdatePeerReflexiveCandidate(
- const Candidate& new_candidate) {
- if (remote_candidate_.type() == PRFLX_PORT_TYPE &&
- new_candidate.type() != PRFLX_PORT_TYPE &&
- remote_candidate_.protocol() == new_candidate.protocol() &&
- remote_candidate_.address() == new_candidate.address() &&
- remote_candidate_.username() == new_candidate.username() &&
- remote_candidate_.password() == new_candidate.password() &&
- remote_candidate_.generation() == new_candidate.generation()) {
- remote_candidate_ = new_candidate;
- }
-}
-
-void Connection::OnMessage(rtc::Message* pmsg) {
- RTC_DCHECK(pmsg->message_id == MSG_DELETE);
- RTC_LOG(LS_INFO) << "Connection deleted with number of pings sent: "
- << num_pings_sent_;
- SignalDestroyed(this);
- delete this;
-}
-
-int64_t Connection::last_received() const {
- return std::max(last_data_received_,
- std::max(last_ping_received_, last_ping_response_received_));
-}
-
-ConnectionInfo Connection::stats() {
- stats_.recv_bytes_second = round(recv_rate_tracker_.ComputeRate());
- stats_.recv_total_bytes = recv_rate_tracker_.TotalSampleCount();
- stats_.sent_bytes_second = round(send_rate_tracker_.ComputeRate());
- stats_.sent_total_bytes = send_rate_tracker_.TotalSampleCount();
- stats_.receiving = receiving_;
- stats_.writable = write_state_ == STATE_WRITABLE;
- stats_.timeout = write_state_ == STATE_WRITE_TIMEOUT;
- stats_.new_connection = !reported_;
- stats_.rtt = rtt_;
- stats_.key = this;
- stats_.state = state_;
- stats_.priority = priority();
- stats_.nominated = nominated();
- stats_.total_round_trip_time_ms = total_round_trip_time_ms_;
- stats_.current_round_trip_time_ms = current_round_trip_time_ms_;
- CopyCandidatesToStatsAndSanitizeIfNecessary();
- return stats_;
-}
-
-void Connection::MaybeUpdateLocalCandidate(ConnectionRequest* request,
- StunMessage* response) {
- // RFC 5245
- // The agent checks the mapped address from the STUN response. If the
- // transport address does not match any of the local candidates that the
- // agent knows about, the mapped address represents a new candidate -- a
- // peer reflexive candidate.
- const StunAddressAttribute* addr =
- response->GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
- if (!addr) {
- RTC_LOG(LS_WARNING)
- << "Connection::OnConnectionRequestResponse - "
- "No MAPPED-ADDRESS or XOR-MAPPED-ADDRESS found in the "
- "stun response message";
- return;
- }
-
- for (size_t i = 0; i < port_->Candidates().size(); ++i) {
- if (port_->Candidates()[i].address() == addr->GetAddress()) {
- if (local_candidate_index_ != i) {
- RTC_LOG(LS_INFO) << ToString()
- << ": Updating local candidate type to srflx.";
- local_candidate_index_ = i;
- // SignalStateChange to force a re-sort in P2PTransportChannel as this
- // Connection's local candidate has changed.
- SignalStateChange(this);
- }
- return;
- }
- }
-
- // RFC 5245
- // Its priority is set equal to the value of the PRIORITY attribute
- // in the Binding request.
- const StunUInt32Attribute* priority_attr =
- request->msg()->GetUInt32(STUN_ATTR_PRIORITY);
- if (!priority_attr) {
- RTC_LOG(LS_WARNING) << "Connection::OnConnectionRequestResponse - "
- "No STUN_ATTR_PRIORITY found in the "
- "stun response message";
- return;
- }
- const uint32_t priority = priority_attr->value();
- std::string id = rtc::CreateRandomString(8);
-
- Candidate new_local_candidate;
- new_local_candidate.set_id(id);
- new_local_candidate.set_component(local_candidate().component());
- new_local_candidate.set_type(PRFLX_PORT_TYPE);
- new_local_candidate.set_protocol(local_candidate().protocol());
- new_local_candidate.set_address(addr->GetAddress());
- new_local_candidate.set_priority(priority);
- new_local_candidate.set_username(local_candidate().username());
- new_local_candidate.set_password(local_candidate().password());
- new_local_candidate.set_network_name(local_candidate().network_name());
- new_local_candidate.set_network_type(local_candidate().network_type());
- new_local_candidate.set_related_address(local_candidate().address());
- new_local_candidate.set_generation(local_candidate().generation());
- new_local_candidate.set_foundation(ComputeFoundation(
- PRFLX_PORT_TYPE, local_candidate().protocol(),
- local_candidate().relay_protocol(), local_candidate().address()));
- new_local_candidate.set_network_id(local_candidate().network_id());
- new_local_candidate.set_network_cost(local_candidate().network_cost());
-
- // Change the local candidate of this Connection to the new prflx candidate.
- RTC_LOG(LS_INFO) << ToString() << ": Updating local candidate type to prflx.";
- local_candidate_index_ = port_->AddPrflxCandidate(new_local_candidate);
-
- // SignalStateChange to force a re-sort in P2PTransportChannel as this
- // Connection's local candidate has changed.
- SignalStateChange(this);
-}
-
-void Connection::CopyCandidatesToStatsAndSanitizeIfNecessary() {
- auto get_sanitized_copy = [](const Candidate& c) {
- bool use_hostname_address = c.type() == LOCAL_PORT_TYPE;
- bool filter_related_address = c.type() == STUN_PORT_TYPE;
- return c.ToSanitizedCopy(use_hostname_address, filter_related_address);
- };
-
- if (port_->Network()->GetMdnsResponder() != nullptr) {
- // When the mDNS obfuscation of local IPs is enabled, we sanitize local
- // candidates.
- stats_.local_candidate = get_sanitized_copy(local_candidate());
- } else {
- stats_.local_candidate = local_candidate();
- }
-
- if (!remote_candidate().address().hostname().empty()) {
- // If the remote endpoint signaled us a hostname candidate, we assume it is
- // supposed to be sanitized in the stats.
- //
- // A prflx remote candidate should not have a hostname set.
- RTC_DCHECK(remote_candidate().type() != PRFLX_PORT_TYPE);
- // A remote hostname candidate should have a resolved IP before we can form
- // a candidate pair.
- RTC_DCHECK(!remote_candidate().address().IsUnresolvedIP());
- stats_.remote_candidate = get_sanitized_copy(remote_candidate());
- } else {
- stats_.remote_candidate = remote_candidate();
- }
-}
-
-bool Connection::rtt_converged() const {
- return rtt_samples_ > (RTT_RATIO + 1);
-}
-
-bool Connection::missing_responses(int64_t now) const {
- if (pings_since_last_response_.empty()) {
- return false;
- }
-
- int64_t waiting = now - pings_since_last_response_[0].sent_time;
- return waiting > 2 * rtt();
-}
-
-ProxyConnection::ProxyConnection(Port* port,
- size_t index,
- const Candidate& remote_candidate)
- : Connection(port, index, remote_candidate) {}
-
-int ProxyConnection::Send(const void* data,
- size_t size,
- const rtc::PacketOptions& options) {
- stats_.sent_total_packets++;
- int sent =
- port_->SendTo(data, size, remote_candidate_.address(), options, true);
- if (sent <= 0) {
- RTC_DCHECK(sent < 0);
- error_ = port_->GetError();
- stats_.sent_discarded_packets++;
- } else {
- send_rate_tracker_.AddSamples(sent);
- }
- return sent;
-}
-
-int ProxyConnection::GetError() {
- return error_;
-}
-
} // namespace cricket
diff --git a/p2p/base/port.h b/p2p/base/port.h
index c42ee51..a0e2606 100644
--- a/p2p/base/port.h
+++ b/p2p/base/port.h
@@ -24,6 +24,8 @@
#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h"
#include "logging/rtc_event_log/ice_logger.h"
#include "p2p/base/candidate_pair_interface.h"
+#include "p2p/base/connection.h"
+#include "p2p/base/connection_info.h"
#include "p2p/base/p2p_constants.h"
#include "p2p/base/packet_socket_factory.h"
#include "p2p/base/port_interface.h"
@@ -43,9 +45,6 @@
namespace cricket {
-class Connection;
-class ConnectionRequest;
-
RTC_EXPORT extern const char LOCAL_PORT_TYPE[];
RTC_EXPORT extern const char STUN_PORT_TYPE[];
RTC_EXPORT extern const char PRFLX_PORT_TYPE[];
@@ -73,16 +72,6 @@
ICE_TYPE_PREFERENCE_HOST = 126
};
-// States are from RFC 5245. http://tools.ietf.org/html/rfc5245#section-5.7.4
-enum class IceCandidatePairState {
- WAITING = 0, // Check has not been performed, Waiting pair on CL.
- IN_PROGRESS, // Check has been sent, transaction is in progress.
- SUCCEEDED, // Check already done, produced a successful result.
- FAILED, // Check for this connection failed.
- // According to spec there should also be a frozen state, but nothing is ever
- // frozen because we have not implemented ICE freezing logic.
-};
-
enum class MdnsNameRegistrationStatus {
// IP concealment with mDNS is not enabled or the name registration process is
// not started yet.
@@ -125,52 +114,6 @@
typedef std::vector<CandidateStats> CandidateStatsList;
-// Stats that we can return about the connections for a transport channel.
-// TODO(hta): Rename to ConnectionStats
-struct ConnectionInfo {
- ConnectionInfo();
- ConnectionInfo(const ConnectionInfo&);
- ~ConnectionInfo();
-
- bool best_connection; // Is this the best connection we have?
- bool writable; // Has this connection received a STUN response?
- bool receiving; // Has this connection received anything?
- bool timeout; // Has this connection timed out?
- bool new_connection; // Is this a newly created connection?
- size_t rtt; // The STUN RTT for this connection.
- size_t sent_total_bytes; // Total bytes sent on this connection.
- size_t sent_bytes_second; // Bps over the last measurement interval.
- size_t sent_discarded_packets; // Number of outgoing packets discarded due to
- // socket errors.
- size_t sent_total_packets; // Number of total outgoing packets attempted for
- // sending.
- size_t sent_ping_requests_total; // Number of STUN ping request sent.
- size_t sent_ping_requests_before_first_response; // Number of STUN ping
- // sent before receiving the first response.
- size_t sent_ping_responses; // Number of STUN ping response sent.
-
- size_t recv_total_bytes; // Total bytes received on this connection.
- size_t recv_bytes_second; // Bps over the last measurement interval.
- size_t recv_ping_requests; // Number of STUN ping request received.
- size_t recv_ping_responses; // Number of STUN ping response received.
- Candidate local_candidate; // The local candidate for this connection.
- Candidate remote_candidate; // The remote candidate for this connection.
- void* key; // A static value that identifies this conn.
- // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-state
- IceCandidatePairState state;
- // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-priority
- uint64_t priority;
- // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-nominated
- bool nominated;
- // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-totalroundtriptime
- uint64_t total_round_trip_time_ms;
- // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime
- absl::optional<uint32_t> current_round_trip_time_ms;
-};
-
-// Information about all the candidate pairs of a channel.
-typedef std::vector<ConnectionInfo> ConnectionInfos;
-
const char* ProtoToString(ProtocolType proto);
bool StringToProto(const char* value, ProtocolType* proto);
@@ -386,6 +329,17 @@
void GetStunStats(absl::optional<StunStats>* stats) override {}
+ // Foundation: An arbitrary string that is the same for two candidates
+ // that have the same type, base IP address, protocol (UDP, TCP,
+ // etc.), and STUN or TURN server. If any of these are different,
+ // then the foundation will be different. Two candidate pairs with
+ // the same foundation pairs are likely to have similar network
+ // characteristics. Foundations are used in the frozen algorithm.
+ static std::string ComputeFoundation(const std::string& type,
+ const std::string& protocol,
+ const std::string& relay_protocol,
+ const rtc::SocketAddress& base_address);
+
protected:
enum { MSG_DESTROY_IF_DEAD = 0, MSG_FIRST_AVAILABLE };
@@ -520,354 +474,6 @@
friend class Connection;
};
-// Represents a communication link between a port on the local client and a
-// port on the remote client.
-class Connection : public CandidatePairInterface,
- public rtc::MessageHandler,
- public sigslot::has_slots<> {
- public:
- struct SentPing {
- SentPing(const std::string id, int64_t sent_time, uint32_t nomination)
- : id(id), sent_time(sent_time), nomination(nomination) {}
-
- std::string id;
- int64_t sent_time;
- uint32_t nomination;
- };
-
- ~Connection() override;
-
- // A unique ID assigned when the connection is created.
- uint32_t id() { return id_; }
-
- // The local port where this connection sends and receives packets.
- Port* port() { return port_; }
- const Port* port() const { return port_; }
-
- // Implementation of virtual methods in CandidatePairInterface.
- // Returns the description of the local port
- const Candidate& local_candidate() const override;
- // Returns the description of the remote port to which we communicate.
- const Candidate& remote_candidate() const override;
-
- // Returns the pair priority.
- uint64_t priority() const;
-
- enum WriteState {
- STATE_WRITABLE = 0, // we have received ping responses recently
- STATE_WRITE_UNRELIABLE = 1, // we have had a few ping failures
- STATE_WRITE_INIT = 2, // we have yet to receive a ping response
- STATE_WRITE_TIMEOUT = 3, // we have had a large number of ping failures
- };
-
- WriteState write_state() const { return write_state_; }
- bool writable() const { return write_state_ == STATE_WRITABLE; }
- bool receiving() const { return receiving_; }
-
- // Determines whether the connection has finished connecting. This can only
- // be false for TCP connections.
- bool connected() const { return connected_; }
- bool weak() const { return !(writable() && receiving() && connected()); }
- bool active() const { return write_state_ != STATE_WRITE_TIMEOUT; }
-
- // A connection is dead if it can be safely deleted.
- bool dead(int64_t now) const;
-
- // Estimate of the round-trip time over this connection.
- int rtt() const { return rtt_; }
-
- int unwritable_timeout() const;
- void set_unwritable_timeout(const absl::optional<int>& value_ms) {
- unwritable_timeout_ = value_ms;
- }
- int unwritable_min_checks() const;
- void set_unwritable_min_checks(const absl::optional<int>& value) {
- unwritable_min_checks_ = value;
- }
- int inactive_timeout() const;
- void set_inactive_timeout(const absl::optional<int>& value) {
- inactive_timeout_ = value;
- }
-
- // Gets the |ConnectionInfo| stats, where |best_connection| has not been
- // populated (default value false).
- ConnectionInfo stats();
-
- sigslot::signal1<Connection*> SignalStateChange;
-
- // Sent when the connection has decided that it is no longer of value. It
- // will delete itself immediately after this call.
- sigslot::signal1<Connection*> SignalDestroyed;
-
- // The connection can send and receive packets asynchronously. This matches
- // the interface of AsyncPacketSocket, which may use UDP or TCP under the
- // covers.
- virtual int Send(const void* data,
- size_t size,
- const rtc::PacketOptions& options) = 0;
-
- // Error if Send() returns < 0
- virtual int GetError() = 0;
-
- sigslot::signal4<Connection*, const char*, size_t, int64_t> SignalReadPacket;
-
- sigslot::signal1<Connection*> SignalReadyToSend;
-
- // Called when a packet is received on this connection.
- void OnReadPacket(const char* data, size_t size, int64_t packet_time_us);
-
- // Called when the socket is currently able to send.
- void OnReadyToSend();
-
- // Called when a connection is determined to be no longer useful to us. We
- // still keep it around in case the other side wants to use it. But we can
- // safely stop pinging on it and we can allow it to time out if the other
- // side stops using it as well.
- bool pruned() const { return pruned_; }
- void Prune();
-
- bool use_candidate_attr() const { return use_candidate_attr_; }
- void set_use_candidate_attr(bool enable);
-
- void set_nomination(uint32_t value) { nomination_ = value; }
-
- uint32_t remote_nomination() const { return remote_nomination_; }
- // One or several pairs may be nominated based on if Regular or Aggressive
- // Nomination is used. https://tools.ietf.org/html/rfc5245#section-8
- // |nominated| is defined both for the controlling or controlled agent based
- // on if a nomination has been pinged or acknowledged. The controlled agent
- // gets its |remote_nomination_| set when pinged by the controlling agent with
- // a nomination value. The controlling agent gets its |acked_nomination_| set
- // when receiving a response to a nominating ping.
- bool nominated() const { return acked_nomination_ || remote_nomination_; }
- // Public for unit tests.
- void set_remote_nomination(uint32_t remote_nomination) {
- remote_nomination_ = remote_nomination;
- }
- // Public for unit tests.
- uint32_t acked_nomination() const { return acked_nomination_; }
-
- void set_remote_ice_mode(IceMode mode) { remote_ice_mode_ = mode; }
-
- int receiving_timeout() const;
- void set_receiving_timeout(absl::optional<int> receiving_timeout_ms) {
- receiving_timeout_ = receiving_timeout_ms;
- }
-
- // Makes the connection go away.
- void Destroy();
-
- // Makes the connection go away, in a failed state.
- void FailAndDestroy();
-
- // Prunes the connection and sets its state to STATE_FAILED,
- // It will not be used or send pings although it can still receive packets.
- void FailAndPrune();
-
- // Checks that the state of this connection is up-to-date. The argument is
- // the current time, which is compared against various timeouts.
- void UpdateState(int64_t now);
-
- // Called when this connection should try checking writability again.
- int64_t last_ping_sent() const { return last_ping_sent_; }
- void Ping(int64_t now);
- void ReceivedPingResponse(int rtt, const std::string& request_id);
- int64_t last_ping_response_received() const {
- return last_ping_response_received_;
- }
- // Used to check if any STUN ping response has been received.
- int rtt_samples() const { return rtt_samples_; }
-
- // Called whenever a valid ping is received on this connection. This is
- // public because the connection intercepts the first ping for us.
- int64_t last_ping_received() const { return last_ping_received_; }
- void ReceivedPing();
- // Handles the binding request; sends a response if this is a valid request.
- void HandleBindingRequest(IceMessage* msg);
-
- int64_t last_data_received() const { return last_data_received_; }
-
- // Debugging description of this connection
- std::string ToDebugId() const;
- std::string ToString() const;
- std::string ToSensitiveString() const;
- // Structured description of this candidate pair.
- const webrtc::IceCandidatePairDescription& ToLogDescription();
- void set_ice_event_log(webrtc::IceEventLog* ice_event_log) {
- ice_event_log_ = ice_event_log;
- }
- // Prints pings_since_last_response_ into a string.
- void PrintPingsSinceLastResponse(std::string* pings, size_t max);
-
- bool reported() const { return reported_; }
- void set_reported(bool reported) { reported_ = reported; }
- // The following two methods are only used for logging in ToString above, and
- // this flag is set true by P2PTransportChannel for its selected candidate
- // pair.
- bool selected() const { return selected_; }
- void set_selected(bool selected) { selected_ = selected; }
-
- // This signal will be fired if this connection is nominated by the
- // controlling side.
- sigslot::signal1<Connection*> SignalNominated;
-
- // Invoked when Connection receives STUN error response with 487 code.
- void HandleRoleConflictFromPeer();
-
- IceCandidatePairState state() const { return state_; }
-
- int num_pings_sent() const { return num_pings_sent_; }
-
- IceMode remote_ice_mode() const { return remote_ice_mode_; }
-
- uint32_t ComputeNetworkCost() const;
-
- // Update the ICE password and/or generation of the remote candidate if the
- // ufrag in |params| matches the candidate's ufrag, and the
- // candidate's password and/or ufrag has not been set.
- void MaybeSetRemoteIceParametersAndGeneration(const IceParameters& params,
- int generation);
-
- // If |remote_candidate_| is peer reflexive and is equivalent to
- // |new_candidate| except the type, update |remote_candidate_| to
- // |new_candidate|.
- void MaybeUpdatePeerReflexiveCandidate(const Candidate& new_candidate);
-
- // Returns the last received time of any data, stun request, or stun
- // response in milliseconds
- int64_t last_received() const;
- // Returns the last time when the connection changed its receiving state.
- int64_t receiving_unchanged_since() const {
- return receiving_unchanged_since_;
- }
-
- bool stable(int64_t now) const;
-
- protected:
- enum { MSG_DELETE = 0, MSG_FIRST_AVAILABLE };
-
- // Constructs a new connection to the given remote port.
- Connection(Port* port, size_t index, const Candidate& candidate);
-
- // Called back when StunRequestManager has a stun packet to send
- void OnSendStunPacket(const void* data, size_t size, StunRequest* req);
-
- // Callbacks from ConnectionRequest
- virtual void OnConnectionRequestResponse(ConnectionRequest* req,
- StunMessage* response);
- void OnConnectionRequestErrorResponse(ConnectionRequest* req,
- StunMessage* response);
- void OnConnectionRequestTimeout(ConnectionRequest* req);
- void OnConnectionRequestSent(ConnectionRequest* req);
-
- bool rtt_converged() const;
-
- // If the response is not received within 2 * RTT, the response is assumed to
- // be missing.
- bool missing_responses(int64_t now) const;
-
- // Changes the state and signals if necessary.
- void set_write_state(WriteState value);
- void UpdateReceiving(int64_t now);
- void set_state(IceCandidatePairState state);
- void set_connected(bool value);
-
- uint32_t nomination() const { return nomination_; }
-
- void OnMessage(rtc::Message* pmsg) override;
-
- uint32_t id_;
- Port* port_;
- size_t local_candidate_index_;
- Candidate remote_candidate_;
-
- ConnectionInfo stats_;
- rtc::RateTracker recv_rate_tracker_;
- rtc::RateTracker send_rate_tracker_;
-
- private:
- // Update the local candidate based on the mapped address attribute.
- // If the local candidate changed, fires SignalStateChange.
- void MaybeUpdateLocalCandidate(ConnectionRequest* request,
- StunMessage* response);
-
- void CopyCandidatesToStatsAndSanitizeIfNecessary();
-
- void LogCandidatePairConfig(webrtc::IceCandidatePairConfigType type);
- void LogCandidatePairEvent(webrtc::IceCandidatePairEventType type,
- uint32_t transaction_id);
-
- WriteState write_state_;
- bool receiving_;
- bool connected_;
- bool pruned_;
- bool selected_ = false;
- // By default |use_candidate_attr_| flag will be true,
- // as we will be using aggressive nomination.
- // But when peer is ice-lite, this flag "must" be initialized to false and
- // turn on when connection becomes "best connection".
- bool use_candidate_attr_;
- // Used by the controlling side to indicate that this connection will be
- // selected for transmission if the peer supports ICE-renomination when this
- // value is positive. A larger-value indicates that a connection is nominated
- // later and should be selected by the controlled side with higher precedence.
- // A zero-value indicates not nominating this connection.
- uint32_t nomination_ = 0;
- // The last nomination that has been acknowledged.
- uint32_t acked_nomination_ = 0;
- // Used by the controlled side to remember the nomination value received from
- // the controlling side. When the peer does not support ICE re-nomination,
- // its value will be 1 if the connection has been nominated.
- uint32_t remote_nomination_ = 0;
-
- IceMode remote_ice_mode_;
- StunRequestManager requests_;
- int rtt_;
- int rtt_samples_ = 0;
- // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-totalroundtriptime
- uint64_t total_round_trip_time_ms_ = 0;
- // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime
- absl::optional<uint32_t> current_round_trip_time_ms_;
- int64_t last_ping_sent_; // last time we sent a ping to the other side
- int64_t last_ping_received_; // last time we received a ping from the other
- // side
- int64_t last_data_received_;
- int64_t last_ping_response_received_;
- int64_t receiving_unchanged_since_ = 0;
- std::vector<SentPing> pings_since_last_response_;
-
- absl::optional<int> unwritable_timeout_;
- absl::optional<int> unwritable_min_checks_;
- absl::optional<int> inactive_timeout_;
-
- bool reported_;
- IceCandidatePairState state_;
- // Time duration to switch from receiving to not receiving.
- absl::optional<int> receiving_timeout_;
- int64_t time_created_ms_;
- int num_pings_sent_ = 0;
-
- absl::optional<webrtc::IceCandidatePairDescription> log_description_;
- webrtc::IceEventLog* ice_event_log_ = nullptr;
-
- friend class Port;
- friend class ConnectionRequest;
-};
-
-// ProxyConnection defers all the interesting work to the port.
-class ProxyConnection : public Connection {
- public:
- ProxyConnection(Port* port, size_t index, const Candidate& remote_candidate);
-
- int Send(const void* data,
- size_t size,
- const rtc::PacketOptions& options) override;
- int GetError() override;
-
- private:
- int error_ = 0;
-};
-
} // namespace cricket
#endif // P2P_BASE_PORT_H_
diff --git a/p2p/base/relay_port.cc b/p2p/base/relay_port.cc
index ebaefa7..9aecb6e 100644
--- a/p2p/base/relay_port.cc
+++ b/p2p/base/relay_port.cc
@@ -11,6 +11,7 @@
#include <string.h>
#include <algorithm>
+#include "p2p/base/connection.h"
#include "p2p/base/relay_port.h"
#include "p2p/base/stun.h"
#include "p2p/base/stun_request.h"
diff --git a/p2p/base/stun_port.cc b/p2p/base/stun_port.cc
index 03d9553..65112f2 100644
--- a/p2p/base/stun_port.cc
+++ b/p2p/base/stun_port.cc
@@ -13,6 +13,7 @@
#include <utility>
#include <vector>
+#include "p2p/base/connection.h"
#include "p2p/base/p2p_constants.h"
#include "p2p/base/port_allocator.h"
#include "p2p/base/stun.h"
diff --git a/p2p/base/tcp_port.h b/p2p/base/tcp_port.h
index 60c2d04..f6953c0 100644
--- a/p2p/base/tcp_port.h
+++ b/p2p/base/tcp_port.h
@@ -16,6 +16,7 @@
#include <string>
#include "absl/memory/memory.h"
+#include "p2p/base/connection.h"
#include "p2p/base/port.h"
#include "rtc_base/async_packet_socket.h"
diff --git a/p2p/base/turn_port.cc b/p2p/base/turn_port.cc
index 098aefb..bcf574e 100644
--- a/p2p/base/turn_port.cc
+++ b/p2p/base/turn_port.cc
@@ -17,6 +17,7 @@
#include "absl/algorithm/container.h"
#include "absl/memory/memory.h"
#include "absl/types/optional.h"
+#include "p2p/base/connection.h"
#include "p2p/base/stun.h"
#include "rtc_base/async_packet_socket.h"
#include "rtc_base/byte_order.h"
diff --git a/p2p/base/turn_port_unittest.cc b/p2p/base/turn_port_unittest.cc
index b21a257..e713b2a 100644
--- a/p2p/base/turn_port_unittest.cc
+++ b/p2p/base/turn_port_unittest.cc
@@ -19,6 +19,7 @@
#include "absl/types/optional.h"
#include "api/units/time_delta.h"
#include "p2p/base/basic_packet_socket_factory.h"
+#include "p2p/base/connection.h"
#include "p2p/base/p2p_constants.h"
#include "p2p/base/port_allocator.h"
#include "p2p/base/stun_port.h"