Index: webrtc/p2p/client/basicportallocator.cc |
diff --git a/webrtc/p2p/client/basicportallocator.cc b/webrtc/p2p/client/basicportallocator.cc |
index edd5fed9cf55843c8e56a7d8089c6ec4a61c4dba..3a7aa5a33340d1118a2b96c2acb33ec88254e094 100644 |
--- a/webrtc/p2p/client/basicportallocator.cc |
+++ b/webrtc/p2p/client/basicportallocator.cc |
@@ -184,7 +184,6 @@ BasicPortAllocatorSession::BasicPortAllocatorSession( |
socket_factory_(allocator->socket_factory()), |
allocation_started_(false), |
network_manager_started_(false), |
- running_(false), |
allocation_sequences_created_(false), |
prune_turn_ports_(allocator->prune_turn_ports()) { |
allocator_->network_manager()->SignalNetworksChanged.connect( |
@@ -239,27 +238,84 @@ void BasicPortAllocatorSession::SetCandidateFilter(uint32_t filter) { |
void BasicPortAllocatorSession::StartGettingPorts() { |
network_thread_ = rtc::Thread::Current(); |
+ PortAllocatorSession::StartGettingPorts(); |
if (!socket_factory_) { |
owned_socket_factory_.reset( |
new rtc::BasicPacketSocketFactory(network_thread_)); |
socket_factory_ = owned_socket_factory_.get(); |
} |
- running_ = true; |
network_thread_->Post(RTC_FROM_HERE, this, MSG_CONFIG_START); |
} |
void BasicPortAllocatorSession::StopGettingPorts() { |
ASSERT(rtc::Thread::Current() == network_thread_); |
- running_ = false; |
network_thread_->Post(RTC_FROM_HERE, this, MSG_CONFIG_STOP); |
ClearGettingPorts(); |
+ // Note: this must be called after ClearGettingPorts because both may set the |
+ // session state and we should set the state to STOPPED. |
+ PortAllocatorSession::StopGettingPorts(); |
} |
void BasicPortAllocatorSession::ClearGettingPorts() { |
+ ASSERT(rtc::Thread::Current() == network_thread_); |
network_thread_->Clear(this, MSG_ALLOCATE); |
- for (uint32_t i = 0; i < sequences_.size(); ++i) |
+ for (uint32_t i = 0; i < sequences_.size(); ++i) { |
sequences_[i]->Stop(); |
+ } |
+ PortAllocatorSession::ClearGettingPorts(); |
+} |
+ |
+std::vector<rtc::Network*> BasicPortAllocatorSession::GetFailedNetworks() { |
+ std::vector<rtc::Network*> networks = GetNetworks(); |
+ |
+ // A network interface may have both IPv4 and IPv6 networks. Only if |
+ // neither of the networks has any connections, the network interface |
+ // is considered failed and need to be regathered on. |
+ std::set<std::string> networks_with_connection; |
+ for (const PortData& data : ports_) { |
+ Port* port = data.port(); |
+ if (!port->connections().empty()) { |
+ networks_with_connection.insert(port->Network()->name()); |
+ } |
+ } |
+ |
+ networks.erase( |
+ std::remove_if(networks.begin(), networks.end(), |
+ [networks_with_connection](rtc::Network* network) { |
+ // If a network does not have any connection, it is |
+ // considered failed. |
+ return networks_with_connection.find(network->name()) != |
+ networks_with_connection.end(); |
+ }), |
+ networks.end()); |
+ return networks; |
+} |
+ |
+void BasicPortAllocatorSession::RegatherOnFailedNetworks() { |
+ // Find the list of networks that have no connection. |
+ std::vector<rtc::Network*> failed_networks = GetFailedNetworks(); |
+ if (failed_networks.empty()) { |
+ return; |
+ } |
+ |
+ // Mark a sequence as "network failed" if its network is in the list of failed |
+ // networks, so that it won't be considered as equivalent when the session |
+ // regathers ports and candidates. |
+ for (AllocationSequence* sequence : sequences_) { |
+ if (!sequence->network_failed() && |
+ std::find(failed_networks.begin(), failed_networks.end(), |
+ sequence->network()) != failed_networks.end()) { |
+ sequence->set_network_failed(); |
+ } |
+ } |
+ // Remove ports from being used locally and send signaling to remove |
+ // the candidates on the remote side. |
+ RemovePortsAndCandidates(failed_networks); |
+ |
+ if (allocation_started_ && network_manager_started_) { |
+ DoAllocate(); |
+ } |
} |
std::vector<PortInterface*> BasicPortAllocatorSession::ReadyPorts() const { |
@@ -278,20 +334,26 @@ std::vector<Candidate> BasicPortAllocatorSession::ReadyCandidates() const { |
if (!data.ready()) { |
continue; |
} |
+ GetCandidatesFromPort(data, &candidates); |
+ } |
+ return candidates; |
+} |
- for (const Candidate& candidate : data.port()->Candidates()) { |
- if (!CheckCandidateFilter(candidate)) { |
- continue; |
- } |
- ProtocolType pvalue; |
- if (!StringToProto(candidate.protocol().c_str(), &pvalue) || |
- !data.sequence()->ProtocolEnabled(pvalue)) { |
- continue; |
- } |
- candidates.push_back(SanitizeRelatedAddress(candidate)); |
+void BasicPortAllocatorSession::GetCandidatesFromPort( |
+ const PortData& data, |
+ std::vector<Candidate>* candidates) const { |
+ RTC_CHECK(candidates != nullptr); |
+ for (const Candidate& candidate : data.port()->Candidates()) { |
+ if (!CheckCandidateFilter(candidate)) { |
+ continue; |
+ } |
+ ProtocolType pvalue; |
+ if (!StringToProto(candidate.protocol().c_str(), &pvalue) || |
+ !data.sequence()->ProtocolEnabled(pvalue)) { |
+ continue; |
} |
+ candidates->push_back(SanitizeRelatedAddress(candidate)); |
} |
- return candidates; |
} |
Candidate BasicPortAllocatorSession::SanitizeRelatedAddress( |
@@ -437,9 +499,8 @@ void BasicPortAllocatorSession::OnAllocate() { |
allocation_started_ = true; |
} |
-void BasicPortAllocatorSession::GetNetworks( |
- std::vector<rtc::Network*>* networks) { |
- networks->clear(); |
+std::vector<rtc::Network*> BasicPortAllocatorSession::GetNetworks() { |
+ std::vector<rtc::Network*> networks; |
rtc::NetworkManager* network_manager = allocator_->network_manager(); |
ASSERT(network_manager != nullptr); |
// If the network permission state is BLOCKED, we just act as if the flag has |
@@ -453,37 +514,37 @@ void BasicPortAllocatorSession::GetNetworks( |
// traffic by OS is also used here to avoid any local or public IP leakage |
// during stun process. |
if (flags() & PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION) { |
- network_manager->GetAnyAddressNetworks(networks); |
+ network_manager->GetAnyAddressNetworks(&networks); |
} else { |
- network_manager->GetNetworks(networks); |
+ network_manager->GetNetworks(&networks); |
} |
- networks->erase(std::remove_if(networks->begin(), networks->end(), |
- [this](rtc::Network* network) { |
- return allocator_->network_ignore_mask() & |
- network->type(); |
- }), |
- networks->end()); |
+ networks.erase(std::remove_if(networks.begin(), networks.end(), |
+ [this](rtc::Network* network) { |
+ return allocator_->network_ignore_mask() & |
+ network->type(); |
+ }), |
+ networks.end()); |
if (flags() & PORTALLOCATOR_DISABLE_COSTLY_NETWORKS) { |
uint16_t lowest_cost = rtc::kNetworkCostMax; |
- for (rtc::Network* network : *networks) { |
+ for (rtc::Network* network : networks) { |
lowest_cost = std::min<uint16_t>(lowest_cost, network->GetCost()); |
} |
- networks->erase(std::remove_if(networks->begin(), networks->end(), |
- [lowest_cost](rtc::Network* network) { |
- return network->GetCost() > |
- lowest_cost + rtc::kNetworkCostLow; |
- }), |
- networks->end()); |
+ networks.erase(std::remove_if(networks.begin(), networks.end(), |
+ [lowest_cost](rtc::Network* network) { |
+ return network->GetCost() > |
+ lowest_cost + rtc::kNetworkCostLow; |
+ }), |
+ networks.end()); |
} |
+ return networks; |
} |
// For each network, see if we have a sequence that covers it already. If not, |
// create a new sequence to create the appropriate ports. |
void BasicPortAllocatorSession::DoAllocate() { |
bool done_signal_needed = false; |
- std::vector<rtc::Network*> networks; |
- GetNetworks(&networks); |
+ std::vector<rtc::Network*> networks = GetNetworks(); |
if (networks.empty()) { |
LOG(LS_WARNING) << "Machine has no networks; no ports will be allocated"; |
@@ -528,8 +589,9 @@ void BasicPortAllocatorSession::DoAllocate() { |
done_signal_needed = true; |
sequence->SignalPortAllocationComplete.connect( |
this, &BasicPortAllocatorSession::OnPortAllocationComplete); |
- if (running_) |
+ if (!IsStopped()) { |
sequence->Start(); |
+ } |
sequences_.push_back(sequence); |
} |
} |
@@ -539,17 +601,19 @@ void BasicPortAllocatorSession::DoAllocate() { |
} |
void BasicPortAllocatorSession::OnNetworksChanged() { |
- std::vector<rtc::Network*> networks; |
- GetNetworks(&networks); |
+ std::vector<rtc::Network*> networks = GetNetworks(); |
+ std::vector<rtc::Network*> failed_networks; |
for (AllocationSequence* sequence : sequences_) { |
- // Remove the network from the allocation sequence if it is not in |
+ // Mark the sequence as "network failed" if its network is not in |
// |networks|. |
- if (!sequence->network_removed() && |
+ if (!sequence->network_failed() && |
std::find(networks.begin(), networks.end(), sequence->network()) == |
networks.end()) { |
- sequence->OnNetworkRemoved(); |
+ sequence->OnNetworkFailed(); |
+ failed_networks.push_back(sequence->network()); |
} |
} |
+ RemovePortsAndCandidates(failed_networks); |
network_manager_started_ = true; |
if (allocation_started_) |
@@ -847,6 +911,32 @@ BasicPortAllocatorSession::PortData* BasicPortAllocatorSession::FindPort( |
return NULL; |
} |
+// Removes ports and candidates created on a given list of networks. |
+void BasicPortAllocatorSession::RemovePortsAndCandidates( |
+ const std::vector<rtc::Network*>& networks) { |
+ std::vector<PortInterface*> ports_to_remove; |
+ std::vector<Candidate> candidates_to_remove; |
+ for (PortData& data : ports_) { |
+ if (std::find(networks.begin(), networks.end(), |
+ data.sequence()->network()) == networks.end()) { |
+ continue; |
+ } |
+ ports_to_remove.push_back(data.port()); |
+ if (data.has_pairable_candidate()) { |
+ GetCandidatesFromPort(data, &candidates_to_remove); |
+ // Mark the port as having no pairable candidates so that its candidates |
+ // won't be removed multiple times. |
+ data.set_has_pairable_candidate(false); |
+ } |
+ } |
+ if (!ports_to_remove.empty()) { |
+ SignalPortsRemoved(this, ports_to_remove); |
+ } |
+ if (!candidates_to_remove.empty()) { |
+ SignalCandidatesRemoved(this, candidates_to_remove); |
+ } |
+} |
+ |
// AllocationSequence |
AllocationSequence::AllocationSequence(BasicPortAllocatorSession* session, |
@@ -884,10 +974,11 @@ void AllocationSequence::Clear() { |
turn_ports_.clear(); |
} |
-void AllocationSequence::OnNetworkRemoved() { |
- // Stop the allocation sequence if its network is gone. |
+void AllocationSequence::OnNetworkFailed() { |
+ RTC_DCHECK(!network_failed_); |
+ network_failed_ = true; |
+ // Stop the allocation sequence if its network failed. |
Stop(); |
- network_removed_ = true; |
} |
AllocationSequence::~AllocationSequence() { |
@@ -896,8 +987,8 @@ AllocationSequence::~AllocationSequence() { |
void AllocationSequence::DisableEquivalentPhases(rtc::Network* network, |
PortConfiguration* config, uint32_t* flags) { |
- if (network_removed_) { |
- // If the network of this allocation sequence has ever gone away, |
+ if (network_failed_) { |
+ // If the network of this allocation sequence has ever become failed, |
// it won't be equivalent to the new network. |
return; |
} |