diff options
Diffstat (limited to 'webrtc/p2p/base/p2ptransportchannel.cc')
-rw-r--r-- | webrtc/p2p/base/p2ptransportchannel.cc | 279 |
1 files changed, 167 insertions, 112 deletions
diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc index 623085f9a8..952cfab747 100644 --- a/webrtc/p2p/base/p2ptransportchannel.cc +++ b/webrtc/p2p/base/p2ptransportchannel.cc @@ -187,6 +187,7 @@ namespace cricket { // well on a 28.8K modem, which is the slowest connection on which the voice // quality is reasonable at all. static const uint32_t PING_PACKET_SIZE = 60 * 8; +// TODO(honghaiz): Change the word DELAY to INTERVAL whenever appropriate. // STRONG_PING_DELAY (480ms) is applied when the best connection is both // writable and receiving. static const uint32_t STRONG_PING_DELAY = 1000 * PING_PACKET_SIZE / 1000; @@ -201,7 +202,6 @@ static const uint32_t MAX_CURRENT_STRONG_DELAY = 900; static const int MIN_CHECK_RECEIVING_DELAY = 50; // ms - P2PTransportChannel::P2PTransportChannel(const std::string& transport_name, int component, P2PTransport* transport, @@ -215,14 +215,13 @@ P2PTransportChannel::P2PTransportChannel(const std::string& transport_name, best_connection_(NULL), pending_best_connection_(NULL), sort_dirty_(false), - was_writable_(false), remote_ice_mode_(ICEMODE_FULL), ice_role_(ICEROLE_UNKNOWN), tiebreaker_(0), - remote_candidate_generation_(0), gathering_state_(kIceGatheringNew), check_receiving_delay_(MIN_CHECK_RECEIVING_DELAY * 5), - receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50) { + receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50), + backup_connection_ping_interval_(0) { uint32_t weak_ping_delay = ::strtoul( webrtc::field_trial::FindFullName("WebRTC-StunInterPacketDelay").c_str(), nullptr, 10); @@ -241,6 +240,8 @@ P2PTransportChannel::~P2PTransportChannel() { // Add the allocator session to our list so that we know which sessions // are still active. void P2PTransportChannel::AddAllocatorSession(PortAllocatorSession* session) { + ASSERT(worker_thread_ == rtc::Thread::Current()); + session->set_generation(static_cast<uint32_t>(allocator_sessions_.size())); allocator_sessions_.push_back(session); @@ -295,9 +296,13 @@ void P2PTransportChannel::SetIceTiebreaker(uint64_t tiebreaker) { tiebreaker_ = tiebreaker; } +TransportChannelState P2PTransportChannel::GetState() const { + return state_; +} + // A channel is considered ICE completed once there is at most one active // connection per network and at least one active connection. -TransportChannelState P2PTransportChannel::GetState() const { +TransportChannelState P2PTransportChannel::ComputeState() const { if (!had_connection_) { return TransportChannelState::STATE_INIT; } @@ -341,25 +346,23 @@ void P2PTransportChannel::SetIceCredentials(const std::string& ice_ufrag, void P2PTransportChannel::SetRemoteIceCredentials(const std::string& ice_ufrag, const std::string& ice_pwd) { ASSERT(worker_thread_ == rtc::Thread::Current()); - bool ice_restart = false; - if (!remote_ice_ufrag_.empty() && !remote_ice_pwd_.empty()) { - ice_restart = (remote_ice_ufrag_ != ice_ufrag) || - (remote_ice_pwd_!= ice_pwd); + IceParameters* current_ice = remote_ice(); + IceParameters new_ice(ice_ufrag, ice_pwd); + if (!current_ice || *current_ice != new_ice) { + // Keep the ICE credentials so that newer connections + // are prioritized over the older ones. + remote_ice_parameters_.push_back(new_ice); + } + + // Update the pwd of remote candidate if needed. + for (RemoteCandidate& candidate : remote_candidates_) { + if (candidate.username() == ice_ufrag && candidate.password().empty()) { + candidate.set_password(ice_pwd); + } } - - remote_ice_ufrag_ = ice_ufrag; - remote_ice_pwd_ = ice_pwd; - // We need to update the credentials for any peer reflexive candidates. - std::vector<Connection*>::iterator it = connections_.begin(); - for (; it != connections_.end(); ++it) { - (*it)->MaybeSetRemoteIceCredentials(ice_ufrag, ice_pwd); - } - - if (ice_restart) { - // We need to keep track of the remote ice restart so newer - // connections are prioritized over the older. - ++remote_candidate_generation_; + for (Connection* conn : connections_) { + conn->MaybeSetRemoteIceCredentials(ice_ufrag, ice_pwd); } } @@ -371,18 +374,26 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) { gather_continually_ = config.gather_continually; LOG(LS_INFO) << "Set gather_continually to " << gather_continually_; - if (config.receiving_timeout_ms < 0) { - return; + if (config.backup_connection_ping_interval >= 0 && + backup_connection_ping_interval_ != + config.backup_connection_ping_interval) { + backup_connection_ping_interval_ = config.backup_connection_ping_interval; + LOG(LS_INFO) << "Set backup connection ping interval to " + << backup_connection_ping_interval_ << " milliseconds."; } - receiving_timeout_ = config.receiving_timeout_ms; - check_receiving_delay_ = - std::max(MIN_CHECK_RECEIVING_DELAY, receiving_timeout_ / 10); - for (Connection* connection : connections_) { - connection->set_receiving_timeout(receiving_timeout_); + if (config.receiving_timeout_ms >= 0 && + receiving_timeout_ != config.receiving_timeout_ms) { + receiving_timeout_ = config.receiving_timeout_ms; + check_receiving_delay_ = + std::max(MIN_CHECK_RECEIVING_DELAY, receiving_timeout_ / 10); + + for (Connection* connection : connections_) { + connection->set_receiving_timeout(receiving_timeout_); + } + LOG(LS_INFO) << "Set ICE receiving timeout to " << receiving_timeout_ + << " milliseconds"; } - LOG(LS_INFO) << "Set ICE receiving timeout to " << receiving_timeout_ - << " milliseconds"; } // Go into the state of processing candidates, and running in general @@ -519,11 +530,17 @@ void P2PTransportChannel::OnUnknownAddress( } } + uint32_t remote_generation = 0; // The STUN binding request may arrive after setRemoteDescription and before // adding remote candidate, so we need to set the password to the shared // password if the user name matches. - if (remote_password.empty() && remote_username == remote_ice_ufrag_) { - remote_password = remote_ice_pwd_; + if (remote_password.empty()) { + const IceParameters* ice_param = + FindRemoteIceFromUfrag(remote_username, &remote_generation); + // Note: if not found, the remote_generation will still be 0. + if (ice_param != nullptr) { + remote_password = ice_param->pwd; + } } Candidate remote_candidate; @@ -555,9 +572,9 @@ void P2PTransportChannel::OnUnknownAddress( // If the source transport address of the request does not match any // existing remote candidates, it represents a new peer reflexive remote // candidate. - remote_candidate = - Candidate(component(), ProtoToString(proto), address, 0, - remote_username, remote_password, PRFLX_PORT_TYPE, 0U, ""); + remote_candidate = Candidate(component(), ProtoToString(proto), address, 0, + remote_username, remote_password, + PRFLX_PORT_TYPE, remote_generation, ""); // From RFC 5245, section-7.2.1.3: // The foundation of the candidate is set to an arbitrary value, different @@ -604,14 +621,7 @@ void P2PTransportChannel::OnUnknownAddress( << (remote_candidate_is_new ? "peer reflexive" : "resurrected") << " candidate: " << remote_candidate.ToString(); AddConnection(connection); - connection->ReceivedPing(); - - bool received_use_candidate = - stun_msg->GetByteString(STUN_ATTR_USE_CANDIDATE) != nullptr; - if (received_use_candidate && ice_role_ == ICEROLE_CONTROLLED) { - connection->set_nominated(true); - OnNominated(connection); - } + connection->HandleBindingRequest(stun_msg); // Update the list of connections since we just added another. We do this // after sending the response since it could (in principle) delete the @@ -624,6 +634,21 @@ void P2PTransportChannel::OnRoleConflict(PortInterface* port) { // from Transport. } +const IceParameters* P2PTransportChannel::FindRemoteIceFromUfrag( + const std::string& ufrag, + uint32_t* generation) { + const auto& params = remote_ice_parameters_; + auto it = std::find_if( + params.rbegin(), params.rend(), + [ufrag](const IceParameters& param) { return param.ufrag == ufrag; }); + if (it == params.rend()) { + // Not found. + return nullptr; + } + *generation = params.rend() - it - 1; + return &(*it); +} + void P2PTransportChannel::OnNominated(Connection* conn) { ASSERT(worker_thread_ == rtc::Thread::Current()); ASSERT(ice_role_ == ICEROLE_CONTROLLED); @@ -648,19 +673,39 @@ void P2PTransportChannel::OnNominated(Connection* conn) { void P2PTransportChannel::AddRemoteCandidate(const Candidate& candidate) { ASSERT(worker_thread_ == rtc::Thread::Current()); - uint32_t generation = candidate.generation(); - // Network may not guarantee the order of the candidate delivery. If a - // remote candidate with an older generation arrives, drop it. - if (generation != 0 && generation < remote_candidate_generation_) { - LOG(LS_WARNING) << "Dropping a remote candidate because its generation " - << generation - << " is lower than the current remote generation " - << remote_candidate_generation_; + uint32_t generation = GetRemoteCandidateGeneration(candidate); + // If a remote candidate with a previous generation arrives, drop it. + if (generation < remote_ice_generation()) { + LOG(LS_WARNING) << "Dropping a remote candidate because its ufrag " + << candidate.username() + << " indicates it was for a previous generation."; return; } + Candidate new_remote_candidate(candidate); + new_remote_candidate.set_generation(generation); + // ICE candidates don't need to have username and password set, but + // the code below this (specifically, ConnectionRequest::Prepare in + // port.cc) uses the remote candidates's username. So, we set it + // here. + if (remote_ice()) { + if (candidate.username().empty()) { + new_remote_candidate.set_username(remote_ice()->ufrag); + } + if (new_remote_candidate.username() == remote_ice()->ufrag) { + if (candidate.password().empty()) { + new_remote_candidate.set_password(remote_ice()->pwd); + } + } else { + // The candidate belongs to the next generation. Its pwd will be set + // when the new remote ICE credentials arrive. + LOG(LS_WARNING) << "A remote candidate arrives with an unknown ufrag: " + << candidate.username(); + } + } + // Create connections to this remote candidate. - CreateConnections(candidate, NULL); + CreateConnections(new_remote_candidate, NULL); // Resort the connections list, which may have new elements. SortConnections(); @@ -673,20 +718,6 @@ bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate, PortInterface* origin_port) { ASSERT(worker_thread_ == rtc::Thread::Current()); - Candidate new_remote_candidate(remote_candidate); - new_remote_candidate.set_generation( - GetRemoteCandidateGeneration(remote_candidate)); - // ICE candidates don't need to have username and password set, but - // the code below this (specifically, ConnectionRequest::Prepare in - // port.cc) uses the remote candidates's username. So, we set it - // here. - if (remote_candidate.username().empty()) { - new_remote_candidate.set_username(remote_ice_ufrag_); - } - if (remote_candidate.password().empty()) { - new_remote_candidate.set_password(remote_ice_pwd_); - } - // If we've already seen the new remote candidate (in the current candidate // generation), then we shouldn't try creating connections for it. // We either already have a connection for it, or we previously created one @@ -695,7 +726,7 @@ bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate, // immediately be re-pruned, churning the network for no purpose. // This only applies to candidates received over signaling (i.e. origin_port // is NULL). - if (!origin_port && IsDuplicateRemoteCandidate(new_remote_candidate)) { + if (!origin_port && IsDuplicateRemoteCandidate(remote_candidate)) { // return true to indicate success, without creating any new connections. return true; } @@ -708,7 +739,7 @@ bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate, bool created = false; std::vector<PortInterface *>::reverse_iterator it; for (it = ports_.rbegin(); it != ports_.rend(); ++it) { - if (CreateConnection(*it, new_remote_candidate, origin_port)) { + if (CreateConnection(*it, remote_candidate, origin_port)) { if (*it == origin_port) created = true; } @@ -716,12 +747,12 @@ bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate, if ((origin_port != NULL) && std::find(ports_.begin(), ports_.end(), origin_port) == ports_.end()) { - if (CreateConnection(origin_port, new_remote_candidate, origin_port)) + if (CreateConnection(origin_port, remote_candidate, origin_port)) created = true; } // Remember this remote candidate so that we can add it to future ports. - RememberRemoteCandidate(new_remote_candidate, origin_port); + RememberRemoteCandidate(remote_candidate, origin_port); return created; } @@ -731,6 +762,9 @@ bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate, bool P2PTransportChannel::CreateConnection(PortInterface* port, const Candidate& remote_candidate, PortInterface* origin_port) { + if (!port->SupportsProtocol(remote_candidate.protocol())) { + return false; + } // Look for an existing connection with this remote address. If one is not // found, then we can create a new connection for this address. Connection* connection = port->GetConnection(remote_candidate.address()); @@ -777,11 +811,21 @@ bool P2PTransportChannel::FindConnection( uint32_t P2PTransportChannel::GetRemoteCandidateGeneration( const Candidate& candidate) { - // We need to keep track of the remote ice restart so newer - // connections are prioritized over the older. - ASSERT(candidate.generation() == 0 || - candidate.generation() == remote_candidate_generation_); - return remote_candidate_generation_; + // If the candidate has a ufrag, use it to find the generation. + if (!candidate.username().empty()) { + uint32_t generation = 0; + if (!FindRemoteIceFromUfrag(candidate.username(), &generation)) { + // If the ufrag is not found, assume the next/future generation. + generation = static_cast<uint32_t>(remote_ice_parameters_.size()); + } + return generation; + } + // If candidate generation is set, use that. + if (candidate.generation() > 0) { + return candidate.generation(); + } + // Otherwise, assume the generation from remote ice parameters. + return remote_ice_generation(); } // Check if remote candidate is already cached. @@ -990,17 +1034,13 @@ void P2PTransportChannel::SortConnections() { // Now update the writable state of the channel with the information we have // so far. - if (best_connection_ && best_connection_->writable()) { - HandleWritable(); - } else if (all_connections_timedout) { + if (all_connections_timedout) { HandleAllTimedOut(); - } else { - HandleNotWritable(); } // Update the state of this channel. This method is called whenever the // state of any connection changes, so this is a good place to do this. - UpdateChannelState(); + UpdateState(); } Connection* P2PTransportChannel::best_nominated_connection() const { @@ -1060,13 +1100,17 @@ void P2PTransportChannel::SwitchBestConnectionTo(Connection* conn) { } } -void P2PTransportChannel::UpdateChannelState() { - // The Handle* functions already set the writable state. We'll just double- - // check it here. +// Warning: UpdateState should eventually be called whenever a connection +// is added, deleted, or the write state of any connection changes so that the +// transport controller will get the up-to-date channel state. However it +// should not be called too often; in the case that multiple connection states +// change, it should be called after all the connection states have changed. For +// example, we call this at the end of SortConnections. +void P2PTransportChannel::UpdateState() { + state_ = ComputeState(); + bool writable = best_connection_ && best_connection_->writable(); - ASSERT(writable == this->writable()); - if (writable != this->writable()) - LOG(LS_ERROR) << "UpdateChannelState: writable state mismatch"; + set_writable(writable); bool receiving = false; for (const Connection* connection : connections_) { @@ -1078,11 +1122,8 @@ void P2PTransportChannel::UpdateChannelState() { set_receiving(receiving); } -// We checked the status of our connections and we had at least one that -// was writable, go into the writable state. -void P2PTransportChannel::HandleWritable() { - ASSERT(worker_thread_ == rtc::Thread::Current()); - if (writable()) { +void P2PTransportChannel::MaybeStopPortAllocatorSessions() { + if (!IsGettingPorts()) { return; } @@ -1098,18 +1139,6 @@ void P2PTransportChannel::HandleWritable() { } session->StopGettingPorts(); } - - was_writable_ = true; - set_writable(true); -} - -// Notify upper layer about channel not writable state, if it was before. -void P2PTransportChannel::HandleNotWritable() { - ASSERT(worker_thread_ == rtc::Thread::Current()); - if (was_writable_) { - was_writable_ = false; - set_writable(false); - } } // If all connections timed out, delete them all. @@ -1179,10 +1208,17 @@ void P2PTransportChannel::OnCheckAndPing() { thread()->PostDelayed(check_delay, this, MSG_CHECK_AND_PING); } +// A connection is considered a backup connection if the channel state +// is completed, the connection is not the best connection and it is active. +bool P2PTransportChannel::IsBackupConnection(Connection* conn) const { + return state_ == STATE_COMPLETED && conn != best_connection_ && + conn->active(); +} + // Is the connection in a state for us to even consider pinging the other side? // We consider a connection pingable even if it's not connected because that's // how a TCP connection is kicked into reconnecting on the active side. -bool P2PTransportChannel::IsPingable(Connection* conn) { +bool P2PTransportChannel::IsPingable(Connection* conn, uint32_t now) { const Candidate& remote = conn->remote_candidate(); // We should never get this far with an empty remote ufrag. ASSERT(!remote.username().empty()); @@ -1198,9 +1234,18 @@ bool P2PTransportChannel::IsPingable(Connection* conn) { return false; } - // If the channel is weak, ping all candidates. Otherwise, we only - // want to ping connections that have not timed out on writing. - return weak() || conn->write_state() != Connection::STATE_WRITE_TIMEOUT; + // If the channel is weakly connected, ping all connections. + if (weak()) { + return true; + } + + // Always ping active connections regardless whether the channel is completed + // or not, but backup connections are pinged at a slower rate. + if (IsBackupConnection(conn)) { + return (now >= conn->last_ping_response_received() + + backup_connection_ping_interval_); + } + return conn->active(); } // Returns the next pingable connection to ping. This will be the oldest @@ -1224,7 +1269,7 @@ Connection* P2PTransportChannel::FindNextPingableConnection() { Connection* oldest_needing_triggered_check = nullptr; Connection* oldest = nullptr; for (Connection* conn : connections_) { - if (!IsPingable(conn)) { + if (!IsPingable(conn, now)) { continue; } bool needs_triggered_check = @@ -1291,6 +1336,14 @@ void P2PTransportChannel::OnConnectionStateChange(Connection* connection) { } } + // May stop the allocator session when at least one connection becomes + // strongly connected after starting to get ports. It is not enough to check + // that the connection becomes weakly connected because the connection may be + // changing from (writable, receiving) to (writable, not receiving). + if (!connection->weak()) { + MaybeStopPortAllocatorSessions(); + } + // We have to unroll the stack before doing this because we may be changing // the state of connections while sorting. RequestSort(); @@ -1328,6 +1381,9 @@ void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) { RequestSort(); } + UpdateState(); + // SignalConnectionRemoved should be called after the channel state is + // updated because the receiver of the event may access the channel state. SignalConnectionRemoved(this); } @@ -1368,8 +1424,7 @@ void P2PTransportChannel::OnReadPacket(Connection* connection, } } -void P2PTransportChannel::OnSentPacket(PortInterface* port, - const rtc::SentPacket& sent_packet) { +void P2PTransportChannel::OnSentPacket(const rtc::SentPacket& sent_packet) { ASSERT(worker_thread_ == rtc::Thread::Current()); SignalSentPacket(this, sent_packet); |