From e8e7d7b0bc1073fbab9b7440fb173240e9794a35 Mon Sep 17 00:00:00 2001 From: Jonas Oreland Date: Wed, 29 May 2019 09:30:55 +0200 Subject: [PATCH] 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 Reviewed-by: Qingsi Wang Reviewed-by: Niels Moller Cr-Commit-Position: refs/heads/master@{#28101} --- p2p/BUILD.gn | 4 + p2p/base/connection.cc | 1133 +++++++++++++++++++ p2p/base/connection.h | 404 +++++++ p2p/base/connection_info.cc | 43 + p2p/base/connection_info.h | 79 ++ p2p/base/ice_transport_internal.h | 1 + p2p/base/p2p_transport_channel.cc | 1 + p2p/base/p2p_transport_channel_unittest.cc | 1 + p2p/base/port.cc | 1139 +------------------- p2p/base/port.h | 420 +------- p2p/base/relay_port.cc | 1 + p2p/base/stun_port.cc | 1 + p2p/base/tcp_port.h | 1 + p2p/base/turn_port.cc | 1 + p2p/base/turn_port_unittest.cc | 1 + 15 files changed, 1689 insertions(+), 1541 deletions(-) create mode 100644 p2p/base/connection.cc create mode 100644 p2p/base/connection.h create mode 100644 p2p/base/connection_info.cc create mode 100644 p2p/base/connection_info.h diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn index c389e81052..a07afefc79 100644 --- a/p2p/BUILD.gn +++ b/p2p/BUILD.gn @@ -25,6 +25,10 @@ rtc_static_library("rtc_p2p") { "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 0000000000..6fc93ed91b --- /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 + +#include +#include +#include +#include + +#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& 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& 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(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( + STUN_ATTR_RETRANSMIT_COUNT, + static_cast(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( + 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( + 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(STUN_ATTR_USE_CANDIDATE)); + } + if (connection_->nomination() && + connection_->nomination() != connection_->acked_nomination()) { + request->AddAttribute(absl::make_unique( + STUN_ATTR_NOMINATION, connection_->nomination())); + } + } else if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLED) { + request->AddAttribute(absl::make_unique( + 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( + 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 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(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(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(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(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 0000000000..601d34cc08 --- /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 +#include + +#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& value_ms) { + unwritable_timeout_ = value_ms; + } + int unwritable_min_checks() const; + void set_unwritable_min_checks(const absl::optional& value) { + unwritable_min_checks_ = value; + } + int inactive_timeout() const; + void set_inactive_timeout(const absl::optional& value) { + inactive_timeout_ = value; + } + + // Gets the |ConnectionInfo| stats, where |best_connection| has not been + // populated (default value false). + ConnectionInfo stats(); + + sigslot::signal1 SignalStateChange; + + // Sent when the connection has decided that it is no longer of value. It + // will delete itself immediately after this call. + sigslot::signal1 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 SignalReadPacket; + + sigslot::signal1 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 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 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 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 pings_since_last_response_; + + absl::optional unwritable_timeout_; + absl::optional unwritable_min_checks_; + absl::optional inactive_timeout_; + + bool reported_; + IceCandidatePairState state_; + // Time duration to switch from receiving to not receiving. + absl::optional receiving_timeout_; + int64_t time_created_ms_; + int num_pings_sent_ = 0; + + absl::optional 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 0000000000..a4f8036769 --- /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 0000000000..a62e8aec00 --- /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 + +#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 current_round_trip_time_ms; +}; + +// Information about all the candidate pairs of a channel. +typedef std::vector 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 8f2167316c..93eec14009 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 6b6727eddf..57e0e12734 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 623bb7c3e0..e20f92d1ba 100644 --- a/p2p/base/p2p_transport_channel_unittest.cc +++ b/p2p/base/p2p_transport_channel_unittest.cc @@ -13,6 +13,7 @@ #include #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 b6fca242ed..9574323507 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& 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& 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 @@ rtc::PacketInfoProtocolType ConvertProtocolTypeToPacketInfoProtocolType( } } -// 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_ACTIVE_STR[] = "active"; 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(Candidate candidate) { 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 @@ void Port::CopyPortInformationToPacketInfo(rtc::PacketInfo* info) const { 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( - 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( - STUN_ATTR_RETRANSMIT_COUNT, - static_cast(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( - 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( - 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( - STUN_ATTR_USE_CANDIDATE)); - } - if (connection_->nomination() && - connection_->nomination() != connection_->acked_nomination()) { - request->AddAttribute(absl::make_unique( - STUN_ATTR_NOMINATION, connection_->nomination())); - } - } else if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLED) { - request->AddAttribute(absl::make_unique( - 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( - 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 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(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(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(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(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 c42ee51ea9..a0e2606a22 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 @@ enum IcePriorityValue { 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 @@ class CandidateStats { typedef std::vector 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 current_round_trip_time_ms; -}; - -// Information about all the candidate pairs of a channel. -typedef std::vector ConnectionInfos; - const char* ProtoToString(ProtocolType proto); bool StringToProto(const char* value, ProtocolType* proto); @@ -386,6 +329,17 @@ class Port : public PortInterface, void GetStunStats(absl::optional* 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 @@ class Port : public PortInterface, 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& value_ms) { - unwritable_timeout_ = value_ms; - } - int unwritable_min_checks() const; - void set_unwritable_min_checks(const absl::optional& value) { - unwritable_min_checks_ = value; - } - int inactive_timeout() const; - void set_inactive_timeout(const absl::optional& value) { - inactive_timeout_ = value; - } - - // Gets the |ConnectionInfo| stats, where |best_connection| has not been - // populated (default value false). - ConnectionInfo stats(); - - sigslot::signal1 SignalStateChange; - - // Sent when the connection has decided that it is no longer of value. It - // will delete itself immediately after this call. - sigslot::signal1 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 SignalReadPacket; - - sigslot::signal1 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 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 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 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 pings_since_last_response_; - - absl::optional unwritable_timeout_; - absl::optional unwritable_min_checks_; - absl::optional inactive_timeout_; - - bool reported_; - IceCandidatePairState state_; - // Time duration to switch from receiving to not receiving. - absl::optional receiving_timeout_; - int64_t time_created_ms_; - int num_pings_sent_ = 0; - - absl::optional 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 ebaefa7823..9aecb6e6ac 100644 --- a/p2p/base/relay_port.cc +++ b/p2p/base/relay_port.cc @@ -11,6 +11,7 @@ #include #include +#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 03d95531c6..65112f2fec 100644 --- a/p2p/base/stun_port.cc +++ b/p2p/base/stun_port.cc @@ -13,6 +13,7 @@ #include #include +#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 60c2d0442e..f6953c06b3 100644 --- a/p2p/base/tcp_port.h +++ b/p2p/base/tcp_port.h @@ -16,6 +16,7 @@ #include #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 098aefb26f..bcf574e332 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 b21a25748a..e713b2a901 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"