From b89ac622f3f5a7bb065a08cb1efba10a0e8cae23 Mon Sep 17 00:00:00 2001 From: Mirko Bonadei Date: Wed, 22 Aug 2018 08:26:19 +0000 Subject: [PATCH] Reland "Enable any address ports by default." This reverts commit 1165949341b6f61c5d728999bfbdaf68fd5c15aa. Reason for revert: Speculative reland (the revert breaks a downstream project). Original change's description: > Revert "Reland "Enable any address ports by default."" > > This reverts commit ac5bbd940ed31f8a58095952f4dcdcbb1b58203c. > > Reason for revert: Speculative revert, possibly breaking downstream projects > > Original change's description: > > Reland "Enable any address ports by default." > > > > This reverts commit 056a68da896d9a578b9ea83e56d261648ea0adc6. > > > > Reason for revert: Trying to reland. > > > > Original change's description: > > > Revert "Enable any address ports by default." > > > > > > This reverts commit f04148c810aad2a0809dc8978650c55308381c47. > > > > > > Reason for revert: Speculative revert. I suspect this is breaking a > > > downstream test (I'll reland if it is not the culprit). > > > > > > Original change's description: > > > > Enable any address ports by default. > > > > > > > > Ports not bound to any specific network interface are allocated by > > > > default. These any address ports are pruned after allocation, > > > > conditional on the allocation results of normal ports that are bound to > > > > the enumerated interfaces. > > > > > > > > Bug: webrtc:9313 > > > > Change-Id: I3ce12eeab0cf3547224e5f8c188d061fc530e145 > > > > Reviewed-on: https://webrtc-review.googlesource.com/78383 > > > > Commit-Queue: Qingsi Wang > > > > Reviewed-by: Taylor Brandstetter > > > > Cr-Commit-Position: refs/heads/master@{#23673} > > > > > > TBR=deadbeef@webrtc.org,pthatcher@webrtc.org,qingsi@google.com > > > > > > Change-Id: I3b3dc42c7de46d198d4b9c270020dcf1100dd907 > > > No-Presubmit: true > > > No-Tree-Checks: true > > > No-Try: true > > > Bug: webrtc:9313 > > > Reviewed-on: https://webrtc-review.googlesource.com/84300 > > > Reviewed-by: Mirko Bonadei > > > Commit-Queue: Mirko Bonadei > > > Cr-Commit-Position: refs/heads/master@{#23678} > > > > TBR=deadbeef@webrtc.org,mbonadei@webrtc.org,pthatcher@webrtc.org,qingsi@google.com > > > > # Not skipping CQ checks because original CL landed > 1 day ago. > > > > Bug: webrtc:9313 > > Change-Id: I98442346babb5d8953d37dc5825efaf79804ed7f > > Reviewed-on: https://webrtc-review.googlesource.com/85000 > > Reviewed-by: Mirko Bonadei > > Commit-Queue: Qingsi Wang > > Cr-Commit-Position: refs/heads/master@{#23720} > > TBR=deadbeef@webrtc.org,mbonadei@webrtc.org,pthatcher@webrtc.org,qingsi@google.com,qingsi@webrtc.org > > # Not skipping CQ checks because original CL landed > 1 day ago. > > Bug: webrtc:9313 > Change-Id: Ie5da4133a371532f717af144f183e299e759f152 > Reviewed-on: https://webrtc-review.googlesource.com/95340 > Commit-Queue: Mirko Bonadei > Reviewed-by: Mirko Bonadei > Reviewed-by: Qingsi Wang > Cr-Commit-Position: refs/heads/master@{#24374} TBR=deadbeef@webrtc.org,mbonadei@webrtc.org,pthatcher@webrtc.org,qingsi@google.com,qingsi@webrtc.org Change-Id: I52bf487d441ce8ccedee7e348b9ed9ade0fd9d1c No-Presubmit: true No-Tree-Checks: true No-Try: true Bug: webrtc:9313 Reviewed-on: https://webrtc-review.googlesource.com/95440 Reviewed-by: Mirko Bonadei Commit-Queue: Mirko Bonadei Cr-Commit-Position: refs/heads/master@{#24379} --- p2p/base/p2pconstants.cc | 4 + p2p/base/p2pconstants.h | 7 +- p2p/base/p2ptransportchannel_unittest.cc | 46 +++- p2p/base/portallocator.h | 12 +- p2p/base/stunserver.cc | 4 +- p2p/base/stunserver.h | 2 +- p2p/base/teststunserver.cc | 2 +- p2p/client/basicportallocator.cc | 272 +++++++++++++++++----- p2p/client/basicportallocator.h | 19 +- p2p/client/basicportallocator_unittest.cc | 145 +++++++++++- 10 files changed, 433 insertions(+), 80 deletions(-) diff --git a/p2p/base/p2pconstants.cc b/p2p/base/p2pconstants.cc index 7dad2d0348..5df2861877 100644 --- a/p2p/base/p2pconstants.cc +++ b/p2p/base/p2pconstants.cc @@ -72,4 +72,8 @@ const int CONNECTION_WRITE_TIMEOUT = 15 * 1000; // 15 seconds // of increased memory, but in some networks (2G), we observe up to 60s RTTs. const int CONNECTION_RESPONSE_TIMEOUT = 60 * 1000; // 60 seconds +// TODO(qingsi): Review and calibrate the value (bugs.webrtc.org/9427). +const int kMaxWaitMsBeforeSignalingAnyAddressPortsAndCandidates = + 2.5 * 1000; // 2.5 seconds + } // namespace cricket diff --git a/p2p/base/p2pconstants.h b/p2p/base/p2pconstants.h index b2e92eff13..3378335bb5 100644 --- a/p2p/base/p2pconstants.h +++ b/p2p/base/p2pconstants.h @@ -102,7 +102,12 @@ extern const int CONNECTION_RESPONSE_TIMEOUT; // The minimum time we will wait before destroying a connection after creating // it. extern const int MIN_CONNECTION_LIFETIME; - +// TODO(qingsi): Rename all constants to kConstant style. +// +// The maximum time in milliseconds we will wait before signaling any address +// ports and candidates gathered from these ports, if the candidate allocation +// is not done yet. +extern const int kMaxWaitMsBeforeSignalingAnyAddressPortsAndCandidates; } // namespace cricket #endif // P2P_BASE_P2PCONSTANTS_H_ diff --git a/p2p/base/p2ptransportchannel_unittest.cc b/p2p/base/p2ptransportchannel_unittest.cc index c56ace7662..1c3ae9ab77 100644 --- a/p2p/base/p2ptransportchannel_unittest.cc +++ b/p2p/base/p2ptransportchannel_unittest.cc @@ -52,6 +52,7 @@ static const int kOnlyLocalPorts = cricket::PORTALLOCATOR_DISABLE_STUN | cricket::PORTALLOCATOR_DISABLE_RELAY | cricket::PORTALLOCATOR_DISABLE_TCP; static const int LOW_RTT = 20; +static const SocketAddress kAnyAddr("0.0.0.0", 0); // Addresses on the public internet. static const SocketAddress kPublicAddrs[2] = {SocketAddress("11.11.11.11", 0), SocketAddress("22.22.22.22", 0)}; @@ -429,6 +430,7 @@ class P2PTransportChannelTestBase : public testing::Test, rtc::NATSocketServer* nat() { return nss_.get(); } rtc::FirewallSocketServer* fw() { return ss_.get(); } + rtc::VirtualSocketServer* vss() { return vss_.get(); } Endpoint* GetEndpoint(int endpoint) { if (endpoint == 0) { @@ -1042,10 +1044,12 @@ class P2PTransportChannelTest : public P2PTransportChannelTestBase { AddAddress(endpoint, kPublicAddrs[endpoint]); // Block all UDP fw()->AddRule(false, rtc::FP_UDP, rtc::FD_ANY, kPublicAddrs[endpoint]); + fw()->AddRule(false, rtc::FP_UDP, rtc::FD_ANY, kAnyAddr); if (config == BLOCK_UDP_AND_INCOMING_TCP) { // Block TCP inbound to the endpoint fw()->AddRule(false, rtc::FP_TCP, SocketAddress(), kPublicAddrs[endpoint]); + fw()->AddRule(false, rtc::FP_TCP, SocketAddress(), kAnyAddr); } else if (config == BLOCK_ALL_BUT_OUTGOING_HTTP) { // Block all TCP to/from the endpoint except 80/443 out fw()->AddRule(true, rtc::FP_TCP, kPublicAddrs[endpoint], @@ -1054,12 +1058,14 @@ class P2PTransportChannelTest : public P2PTransportChannelTestBase { SocketAddress(rtc::IPAddress(INADDR_ANY), 443)); fw()->AddRule(false, rtc::FP_TCP, rtc::FD_ANY, kPublicAddrs[endpoint]); + fw()->AddRule(false, rtc::FP_TCP, rtc::FD_ANY, kAnyAddr); } else if (config == PROXY_HTTPS) { // Block all TCP to/from the endpoint except to the proxy server fw()->AddRule(true, rtc::FP_TCP, kPublicAddrs[endpoint], kHttpsProxyAddrs[endpoint]); fw()->AddRule(false, rtc::FP_TCP, rtc::FD_ANY, kPublicAddrs[endpoint]); + fw()->AddRule(false, rtc::FP_TCP, rtc::FD_ANY, kAnyAddr); SetProxy(endpoint, rtc::PROXY_HTTPS); } else if (config == PROXY_SOCKS) { // Block all TCP to/from the endpoint except to the proxy server @@ -1067,6 +1073,7 @@ class P2PTransportChannelTest : public P2PTransportChannelTestBase { kSocksProxyAddrs[endpoint]); fw()->AddRule(false, rtc::FP_TCP, rtc::FD_ANY, kPublicAddrs[endpoint]); + fw()->AddRule(false, rtc::FP_TCP, rtc::FD_ANY, kAnyAddr); SetProxy(endpoint, rtc::PROXY_SOCKS5); } break; @@ -1706,16 +1713,8 @@ TEST_F(P2PTransportChannelTest, IncomingOnlyOpen) { // connections. This has been observed in some scenarios involving // VPNs/firewalls. TEST_F(P2PTransportChannelTest, CanOnlyMakeOutgoingTcpConnections) { - // The PORTALLOCATOR_ENABLE_ANY_ADDRESS_PORTS flag is required if the - // application needs this use case to work, since the application must accept - // the tradeoff that more candidates need to be allocated. - // - // TODO(deadbeef): Later, make this flag the default, and do more elegant - // things to ensure extra candidates don't waste resources? - ConfigureEndpoints( - OPEN, OPEN, - kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_ANY_ADDRESS_PORTS, - kDefaultPortAllocatorFlags); + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, + kDefaultPortAllocatorFlags); // In order to simulate nothing working but outgoing TCP connections, prevent // the endpoint from binding to its interface's address as well as the // "any" addresses. It can then only make a connection by using "Connect()". @@ -3066,6 +3065,33 @@ TEST_F(P2PTransportChannelMultihomedTest, TestRestoreBackupConnection) { DestroyChannels(); } +// Test that when explicit binding to network interfaces is disallowed, we may +// still establish a connection by using the any address fallback. +TEST_F(P2PTransportChannelMultihomedTest, + BindingToAnyAddressRevealsViableRouteWhenExplicitBindingFails) { + rtc::ScopedFakeClock clock; + AddAddress(0, kPublicAddrs[0]); + AddAddress(1, kPublicAddrs[1]); + fw()->SetUnbindableIps({kPublicAddrs[0].ipaddr()}); + // When bound to the any address, the port allocator should discover the + // alternative local address. + vss()->SetAlternativeLocalAddress(kAnyAddr.ipaddr(), + kAlternateAddrs[0].ipaddr()); + SetAllocatorFlags(0, kOnlyLocalPorts); + SetAllocatorFlags(1, kOnlyLocalPorts); + IceConfig default_config; + CreateChannels(default_config, default_config); + EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && + ep2_ch1()->receiving() && + ep2_ch1()->writable(), + kMediumTimeout, clock); + EXPECT_TRUE( + ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection() && + LocalCandidate(ep1_ch1())->address().EqualIPs(kAlternateAddrs[0])); + + DestroyChannels(); +} + // A collection of tests which tests a single P2PTransportChannel by sending // pings. class P2PTransportChannelPingTest : public testing::Test, diff --git a/p2p/base/portallocator.h b/p2p/base/portallocator.h index 8bd709642c..4c27ba8a72 100644 --- a/p2p/base/portallocator.h +++ b/p2p/base/portallocator.h @@ -74,13 +74,11 @@ enum { // When specified, do not collect IPv6 ICE candidates on Wi-Fi. PORTALLOCATOR_ENABLE_IPV6_ON_WIFI = 0x4000, - // When this flag is set, ports not bound to any specific network interface - // will be used, in addition to normal ports bound to the enumerated - // interfaces. Without this flag, these "any address" ports would only be - // used when network enumeration fails or is disabled. But under certain - // conditions, these ports may succeed where others fail, so they may allow - // the application to work in a wider variety of environments, at the expense - // of having to allocate additional candidates. + // This flag is deprecated; we now always enable any address ports, only + // using them if they end up using interfaces that weren't otherwise + // accessible. + // + // TODO(qingsi): Remove this flag when downstream projects no longer use it. PORTALLOCATOR_ENABLE_ANY_ADDRESS_PORTS = 0x8000, // Exclude link-local network interfaces diff --git a/p2p/base/stunserver.cc b/p2p/base/stunserver.cc index 00eacf17bc..50cb654674 100644 --- a/p2p/base/stunserver.cc +++ b/p2p/base/stunserver.cc @@ -54,7 +54,7 @@ void StunServer::OnPacket(rtc::AsyncPacketSocket* socket, void StunServer::OnBindingRequest(StunMessage* msg, const rtc::SocketAddress& remote_addr) { StunMessage response; - GetStunBindReqponse(msg, remote_addr, &response); + GetStunBindResponse(msg, remote_addr, &response); SendResponse(response, remote_addr); } @@ -83,7 +83,7 @@ void StunServer::SendResponse(const StunMessage& msg, RTC_LOG_ERR(LS_ERROR) << "sendto"; } -void StunServer::GetStunBindReqponse(StunMessage* request, +void StunServer::GetStunBindResponse(StunMessage* request, const rtc::SocketAddress& remote_addr, StunMessage* response) const { response->SetType(STUN_BINDING_RESPONSE); diff --git a/p2p/base/stunserver.h b/p2p/base/stunserver.h index 9016d06c20..5d0f1305ab 100644 --- a/p2p/base/stunserver.h +++ b/p2p/base/stunserver.h @@ -52,7 +52,7 @@ class StunServer : public sigslot::has_slots<> { void SendResponse(const StunMessage& msg, const rtc::SocketAddress& addr); // A helper method to compose a STUN binding response. - void GetStunBindReqponse(StunMessage* request, + void GetStunBindResponse(StunMessage* request, const rtc::SocketAddress& remote_addr, StunMessage* response) const; diff --git a/p2p/base/teststunserver.cc b/p2p/base/teststunserver.cc index 13bc5773d9..7a18d7ad80 100644 --- a/p2p/base/teststunserver.cc +++ b/p2p/base/teststunserver.cc @@ -27,7 +27,7 @@ void TestStunServer::OnBindingRequest(StunMessage* msg, StunServer::OnBindingRequest(msg, remote_addr); } else { StunMessage response; - GetStunBindReqponse(msg, fake_stun_addr_, &response); + GetStunBindResponse(msg, fake_stun_addr_, &response); SendResponse(response, remote_addr); } } diff --git a/p2p/client/basicportallocator.cc b/p2p/client/basicportallocator.cc index 5b48f5e9d7..eb14a37758 100644 --- a/p2p/client/basicportallocator.cc +++ b/p2p/client/basicportallocator.cc @@ -25,6 +25,7 @@ #include "p2p/base/udpport.h" #include "rtc_base/checks.h" #include "rtc_base/helpers.h" +#include "rtc_base/ipaddress.h" #include "rtc_base/logging.h" #include "system_wrappers/include/metrics.h" @@ -39,6 +40,7 @@ enum { MSG_ALLOCATION_PHASE, MSG_SEQUENCEOBJECTS_CREATED, MSG_CONFIG_STOP, + MSG_SIGNAL_ANY_ADDRESS_PORTS, }; const int PHASE_UDP = 0; @@ -111,6 +113,10 @@ void FilterNetworks(NetworkList* networks, NetworkFilter filter) { networks->erase(start_to_remove, networks->end()); } +bool IsAnyAddressPort(const cricket::Port* port) { + return rtc::IPIsAny(port->Network()->GetBestIP()); +} + } // namespace namespace cricket { @@ -148,7 +154,7 @@ BasicPortAllocator::BasicPortAllocator(rtc::NetworkManager* network_manager, : network_manager_(network_manager), socket_factory_(socket_factory) { InitRelayPortFactory(nullptr); RTC_DCHECK(relay_port_factory_ != nullptr); - RTC_DCHECK(socket_factory_ != NULL); + RTC_DCHECK(socket_factory_ != nullptr); SetConfiguration(stun_servers, std::vector(), 0, false, nullptr); Construct(); @@ -160,7 +166,7 @@ BasicPortAllocator::BasicPortAllocator( const rtc::SocketAddress& relay_address_udp, const rtc::SocketAddress& relay_address_tcp, const rtc::SocketAddress& relay_address_ssl) - : network_manager_(network_manager), socket_factory_(NULL) { + : network_manager_(network_manager), socket_factory_(nullptr) { InitRelayPortFactory(nullptr); RTC_DCHECK(relay_port_factory_ != nullptr); RTC_DCHECK(network_manager_ != nullptr); @@ -262,7 +268,7 @@ BasicPortAllocatorSession::BasicPortAllocatorSession( ice_pwd, allocator->flags()), allocator_(allocator), - network_thread_(NULL), + network_thread_(nullptr), socket_factory_(allocator->socket_factory()), allocation_started_(false), network_manager_started_(false), @@ -275,7 +281,7 @@ BasicPortAllocatorSession::BasicPortAllocatorSession( BasicPortAllocatorSession::~BasicPortAllocatorSession() { allocator_->network_manager()->StopUpdating(); - if (network_thread_ != NULL) + if (network_thread_ != nullptr) network_thread_->Clear(this); for (uint32_t i = 0; i < sequences_.size(); ++i) { @@ -442,7 +448,7 @@ void BasicPortAllocatorSession::Regather( std::vector ports_to_prune = GetUnprunedPorts(networks); if (!ports_to_prune.empty()) { RTC_LOG(LS_INFO) << "Prune " << ports_to_prune.size() << " ports"; - PrunePortsAndRemoveCandidates(ports_to_prune); + PrunePortsAndSignalCandidatesRemoval(ports_to_prune); } if (allocation_started_ && network_manager_started_ && !IsStopped()) { @@ -564,6 +570,10 @@ void BasicPortAllocatorSession::OnMessage(rtc::Message* message) { RTC_DCHECK(rtc::Thread::Current() == network_thread_); OnConfigStop(); break; + case MSG_SIGNAL_ANY_ADDRESS_PORTS: + RTC_DCHECK(rtc::Thread::Current() == network_thread_); + SignalAnyAddressPortsAndCandidatesReadyIfNotRedundant(); + break; default: RTC_NOTREACHED(); } @@ -626,7 +636,7 @@ void BasicPortAllocatorSession::OnConfigStop() { // If we stopped anything that was running, send a done signal now. if (send_signal) { - MaybeSignalCandidatesAllocationDone(); + FireAllocationStatusSignalsIfNeeded(); } } @@ -654,22 +664,24 @@ std::vector BasicPortAllocatorSession::GetNetworks() { rtc::NetworkManager::ENUMERATION_BLOCKED) { set_flags(flags() | PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION); } - // If the adapter enumeration is disabled, we'll just bind to any address - // instead of specific NIC. This is to ensure the same routing for http - // 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); - } else { + + // If adapter enumeration is disabled, we'll just bind to any address + // instead of a specific NIC. This is to ensure that WebRTC traffic is routed + // by the OS in the same way that HTTP traffic would be, and no additional + // local or public IPs are leaked during ICE processing. + // + // Even when adapter enumeration is enabled, we still bind to the "any" + // address as a fallback, since this may potentially reveal network + // interfaces that weren't otherwise accessible. Note that the candidates + // gathered by binding to the "any" address won't be surfaced to the + // application if they're determined to be redundant (if they have the same + // address as a candidate gathered by binding to an interface explicitly). + if (!(flags() & PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION)) { network_manager->GetNetworks(&networks); - // If network enumeration fails, use the ANY address as a fallback, so we - // can at least try gathering candidates using the default route chosen by - // the OS. Or, if the PORTALLOCATOR_ENABLE_ANY_ADDRESS_PORTS flag is - // set, we'll use ANY address candidates either way. - if (networks.empty() || flags() & PORTALLOCATOR_ENABLE_ANY_ADDRESS_PORTS) { - network_manager->GetAnyAddressNetworks(&networks); - } } + + network_manager->GetAnyAddressNetworks(&networks); + // Filter out link-local networks if needed. if (flags() & PORTALLOCATOR_DISABLE_LINK_LOCAL_NETWORKS) { NetworkFilter link_local_filter( @@ -688,11 +700,12 @@ std::vector BasicPortAllocatorSession::GetNetworks() { if (flags() & PORTALLOCATOR_DISABLE_COSTLY_NETWORKS) { uint16_t lowest_cost = rtc::kNetworkCostMax; for (rtc::Network* network : networks) { - // Don't determine the lowest cost from a link-local network. - // On iOS, a device connected to the computer will get a link-local - // network for communicating with the computer, however this network can't - // be used to connect to a peer outside the network. - if (rtc::IPIsLinkLocal(network->GetBestIP())) { + // Don't determine the lowest cost from a link-local or any address + // network. On iOS, a device connected to the computer will get a + // link-local network for communicating with the computer, however this + // network can't be used to connect to a peer outside the network. + if (rtc::IPIsLinkLocal(network->GetBestIP()) || + rtc::IPIsAny(network->GetBestIP())) { continue; } lowest_cost = std::min(lowest_cost, network->GetCost()); @@ -790,6 +803,19 @@ void BasicPortAllocatorSession::DoAllocate(bool disable_equivalent) { if (done_signal_needed) { network_thread_->Post(RTC_FROM_HERE, this, MSG_SEQUENCEOBJECTS_CREATED); } + + // If adapter enumeration is enabled, then we prefer binding to individual + // network adapters, only using ports bound to the "any" address (0.0.0.0) if + // they reveal an interface not otherwise accessible. Normally these will be + // surfaced when candidate allocation completes, but sometimes candidate + // allocation can take a long time, if a STUN transaction times out for + // instance. So as a backup, we'll surface these ports/candidates after + // |kMaxWaitMsBeforeSignalingAnyAddressPortsAndCandidates| passes. + if (!(flags() & PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION)) { + network_thread_->PostDelayed( + RTC_FROM_HERE, kMaxWaitMsBeforeSignalingAnyAddressPortsAndCandidates, + this, MSG_SIGNAL_ANY_ADDRESS_PORTS); + } } void BasicPortAllocatorSession::OnNetworksChanged() { @@ -809,7 +835,7 @@ void BasicPortAllocatorSession::OnNetworksChanged() { if (!ports_to_prune.empty()) { RTC_LOG(LS_INFO) << "Prune " << ports_to_prune.size() << " ports because their networks were gone"; - PrunePortsAndRemoveCandidates(ports_to_prune); + PrunePortsAndSignalCandidatesRemoval(ports_to_prune); } if (allocation_started_ && !IsStopped()) { @@ -872,14 +898,14 @@ void BasicPortAllocatorSession::AddAllocatedPort(Port* port, void BasicPortAllocatorSession::OnAllocationSequenceObjectsCreated() { allocation_sequences_created_ = true; // Send candidate allocation complete signal if we have no sequences. - MaybeSignalCandidatesAllocationDone(); + FireAllocationStatusSignalsIfNeeded(); } void BasicPortAllocatorSession::OnCandidateReady(Port* port, const Candidate& c) { RTC_DCHECK(rtc::Thread::Current() == network_thread_); PortData* data = FindPort(port); - RTC_DCHECK(data != NULL); + RTC_DCHECK(data != nullptr); RTC_LOG(LS_INFO) << port->ToString() << ": Gathered candidate: " << c.ToSensitiveString(); // Discarding any candidate signal if port allocation status is @@ -908,23 +934,56 @@ void BasicPortAllocatorSession::OnCandidateReady(Port* port, } // If the current port is not pruned yet, SignalPortReady. if (!data->pruned()) { - RTC_LOG(LS_INFO) << port->ToString() << ": Port ready."; - SignalPortReady(this, port); port->KeepAliveUntilPruned(); + // We postpone the signaling of any address ports to when the candidates + // allocation is done or the candidate allocation process has start for + // more than kMaxWaitMsBeforeSignalingAnyAddressPortsAndCandidates, and + // we check whether they are redundant or not (in + // SignalAnyAddressPortsAndCandidatesReadyIfNotRedundant). Otherwise, + // connectivity checks will be sent from these possibly redundant ports, + // likely also resulting in "prflx" candidate pairs being created on the + // other side if not pruned in time. The signaling of any address ports + // that are not redundant happens in + // SignalAnyAddressPortsAndCandidatesReadyIfNotRedundant. + // + // If adapter enumeration is disabled, these "any" address ports + // are all we'll get, so we can signal them immediately. + // + // Same logic applies to candidates below. + + if (!IsAnyAddressPort(port) || + (flags() & PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION)) { + RTC_LOG(INFO) << port->ToString() << ": Port ready."; + SignalPortReady(this, port); + data->set_signaled(); + } } } if (data->ready() && CheckCandidateFilter(c)) { - std::vector candidates; - candidates.push_back(SanitizeRelatedAddress(c)); - SignalCandidatesReady(this, candidates); + // See comment above about why we delay signaling candidates from "any + // address" ports. + // + // For candidates gathered after the any address port is signaled, we will + // not perform the redundancy check anymore. Note that late candiates + // gathered from the any address port should be a srflx candidate from a + // late STUN binding response. + if (data->signaled()) { + std::vector candidates; + candidates.push_back(SanitizeRelatedAddress(c)); + SignalCandidatesReady(this, candidates); + } else { + RTC_LOG(INFO) << "Candidate not signaled yet because it is from the " + "any address port: " + << c.ToSensitiveString(); + } } else { RTC_LOG(LS_INFO) << "Discarding candidate because it doesn't match filter."; } // If we have pruned any port, maybe need to signal port allocation done. if (pruned) { - MaybeSignalCandidatesAllocationDone(); + FireAllocationStatusSignalsIfNeeded(); } } @@ -958,7 +1017,7 @@ bool BasicPortAllocatorSession::PruneTurnPorts(Port* newly_pairable_turn_port) { ComparePort(data.port(), best_turn_port) < 0) { pruned = true; if (data.port() != newly_pairable_turn_port) { - // These ports will be pruned in PrunePortsAndRemoveCandidates. + // These ports will be pruned in PrunePortsAndSignalCandidatesRemoval. ports_to_prune.push_back(&data); } else { data.Prune(); @@ -969,7 +1028,7 @@ bool BasicPortAllocatorSession::PruneTurnPorts(Port* newly_pairable_turn_port) { if (!ports_to_prune.empty()) { RTC_LOG(LS_INFO) << "Prune " << ports_to_prune.size() << " low-priority TURN ports"; - PrunePortsAndRemoveCandidates(ports_to_prune); + PrunePortsAndSignalCandidatesRemoval(ports_to_prune); } return pruned; } @@ -985,7 +1044,7 @@ void BasicPortAllocatorSession::OnPortComplete(Port* port) { RTC_LOG(LS_INFO) << port->ToString() << ": Port completed gathering candidates."; PortData* data = FindPort(port); - RTC_DCHECK(data != NULL); + RTC_DCHECK(data != nullptr); // Ignore any late signals. if (!data->inprogress()) { @@ -995,7 +1054,7 @@ void BasicPortAllocatorSession::OnPortComplete(Port* port) { // Moving to COMPLETE state. data->set_complete(); // Send candidate allocation complete signal if this was the last port. - MaybeSignalCandidatesAllocationDone(); + FireAllocationStatusSignalsIfNeeded(); } void BasicPortAllocatorSession::OnPortError(Port* port) { @@ -1003,7 +1062,7 @@ void BasicPortAllocatorSession::OnPortError(Port* port) { RTC_LOG(LS_INFO) << port->ToString() << ": Port encountered error while gathering candidates."; PortData* data = FindPort(port); - RTC_DCHECK(data != NULL); + RTC_DCHECK(data != nullptr); // We might have already given up on this port and stopped it. if (!data->inprogress()) { return; @@ -1013,7 +1072,7 @@ void BasicPortAllocatorSession::OnPortError(Port* port) { // But this signal itself is generic. data->set_error(); // Send candidate allocation complete signal if this was the last port. - MaybeSignalCandidatesAllocationDone(); + FireAllocationStatusSignalsIfNeeded(); } bool BasicPortAllocatorSession::CheckCandidateFilter(const Candidate& c) const { @@ -1057,24 +1116,34 @@ bool BasicPortAllocatorSession::CandidatePairable(const Candidate& c, // prevent even default IP addresses from leaking), we still don't want to // ping from them, even if device enumeration is disabled. Thus, we check for // both device enumeration and host candidates being disabled. - bool network_enumeration_disabled = c.address().IsAnyIP(); + bool candidate_has_any_address = c.address().IsAnyIP(); bool can_ping_from_candidate = (port->SharedSocket() || c.protocol() == TCP_PROTOCOL_NAME); bool host_candidates_disabled = !(candidate_filter_ & CF_HOST); return candidate_signalable || - (network_enumeration_disabled && can_ping_from_candidate && + (candidate_has_any_address && can_ping_from_candidate && !host_candidates_disabled); } void BasicPortAllocatorSession::OnPortAllocationComplete( AllocationSequence* seq) { // Send candidate allocation complete signal if all ports are done. - MaybeSignalCandidatesAllocationDone(); + FireAllocationStatusSignalsIfNeeded(); } -void BasicPortAllocatorSession::MaybeSignalCandidatesAllocationDone() { +void BasicPortAllocatorSession::FireAllocationStatusSignalsIfNeeded() { if (CandidatesAllocationDone()) { + // Now that allocation is done, we can surface any ports bound to the "any" + // address if they're not redundant (if they don't have the same address as + // a port bound to a specific interface). We don't surface them as soon as + // they're gathered because we may not know yet whether they're redundant. + // + // This also happens after a timeout of 2 seconds (see comment in + // DoAllocate); if allocation completes first we clear that timer since + // it's not needed. + network_thread_->Clear(this, MSG_SIGNAL_ANY_ADDRESS_PORTS); + SignalAnyAddressPortsAndCandidatesReadyIfNotRedundant(); if (pooled()) { RTC_LOG(LS_INFO) << "All candidates gathered for pooled session."; } else { @@ -1085,7 +1154,96 @@ void BasicPortAllocatorSession::MaybeSignalCandidatesAllocationDone() { } } -void BasicPortAllocatorSession::OnPortDestroyed(PortInterface* port) { +// We detect the redundancy in any address ports as follows: +// +// 1. Delay the signaling of all any address ports and candidates gathered from +// these ports, which happens in OnCandidateReady. +// +// 2. For all non-any address ports, collect the IPs of their candidates +// (ignoring "active" TCP candidates, since no sockets are created for them +// until a connection is made and there's no guarantee they'll work). +// +// 3. For each any address port, compare their candidates to the existing IPs +// collected from step 2, and this port can be signaled if it has candidates +// with unseen IPs. +void BasicPortAllocatorSession:: + SignalAnyAddressPortsAndCandidatesReadyIfNotRedundant() { + // Note that this is called either when allocation completes, or after a + // timeout, so some ports may still be waiting for STUN transactions to + // finish. + // + // First, get a list of all "any address" ports that have not yet been + // signaled, and a list of candidate IP addresses from all other ports. + std::vector maybe_signalable_any_address_ports; + std::set ips_from_non_any_address_ports; + for (PortData& port_data : ports_) { + if (!port_data.ready()) { + continue; + } + if (IsAnyAddressPort(port_data.port())) { + if (!port_data.signaled()) { + maybe_signalable_any_address_ports.push_back(&port_data); + } + } else { + for (const Candidate& c : port_data.port()->Candidates()) { + // If the port of the candidate is |DISCARD_PORT| (9), this is an + // "active" TCP candidate and it doesn't mean we actually bound a + // socket to this address, so ignore it. + if (c.address().port() != DISCARD_PORT) { + ips_from_non_any_address_ports.insert(c.address().ipaddr()); + } + } + } + } + // Now signal "any" address ports that have a unique address, and prune any + // that don't. + std::vector signalable_any_address_ports; + std::vector prunable_any_address_ports; + std::vector signalable_candidates_from_any_address_ports; + for (PortData* port_data : maybe_signalable_any_address_ports) { + bool port_signalable = false; + for (const Candidate& c : port_data->port()->Candidates()) { + if (!CandidatePairable(c, port_data->port()) || + ips_from_non_any_address_ports.count(c.address().ipaddr())) { + continue; + } + // Even when a port is bound to the "any" address, it should normally + // still have an associated IP (determined by calling "connect" and then + // "getsockaddr"). Though sometimes even this fails (meaning |is_any_ip| + // will be true), and thus we have no way of knowing whether the port is + // redundant or not. In that case, we'll use the port if we have + // *no* ports bound to specific addresses. This is needed for corner + // cases such as bugs.webrtc.org/7798. + bool is_any_ip = rtc::IPIsAny(c.address().ipaddr()); + if (is_any_ip && !ips_from_non_any_address_ports.empty()) { + continue; + } + port_signalable = true; + // Still need to check the candidiate filter and sanitize the related + // address before signaling the candidate itself. + if (CheckCandidateFilter(c)) { + signalable_candidates_from_any_address_ports.push_back( + SanitizeRelatedAddress(c)); + } + } + if (port_signalable) { + signalable_any_address_ports.push_back(port_data); + } else { + prunable_any_address_ports.push_back(port_data); + } + } + PrunePorts(prunable_any_address_ports); + for (PortData* port_data : signalable_any_address_ports) { + RTC_LOG(INFO) << port_data->port()->ToString() << ": Port ready."; + SignalPortReady(this, port_data->port()); + port_data->set_signaled(); + } + RTC_LOG(INFO) << "Signaling candidates from the any address ports."; + SignalCandidatesReady(this, signalable_candidates_from_any_address_ports); +} + +void BasicPortAllocatorSession::OnPortDestroyed( + PortInterface* port) { RTC_DCHECK(rtc::Thread::Current() == network_thread_); for (std::vector::iterator iter = ports_.begin(); iter != ports_.end(); ++iter) { @@ -1107,7 +1265,7 @@ BasicPortAllocatorSession::PortData* BasicPortAllocatorSession::FindPort( return &*it; } } - return NULL; + return nullptr; } std::vector @@ -1124,7 +1282,7 @@ BasicPortAllocatorSession::GetUnprunedPorts( return unpruned_ports; } -void BasicPortAllocatorSession::PrunePortsAndRemoveCandidates( +std::vector BasicPortAllocatorSession::PrunePorts( const std::vector& port_data_list) { std::vector pruned_ports; std::vector removed_candidates; @@ -1142,6 +1300,12 @@ void BasicPortAllocatorSession::PrunePortsAndRemoveCandidates( if (!pruned_ports.empty()) { SignalPortsPruned(this, pruned_ports); } + return removed_candidates; +} + +void BasicPortAllocatorSession::PrunePortsAndSignalCandidatesRemoval( + const std::vector& port_data_list) { + std::vector removed_candidates = PrunePorts(port_data_list); if (!removed_candidates.empty()) { RTC_LOG(LS_INFO) << "Removed " << removed_candidates.size() << " candidates"; @@ -1161,7 +1325,7 @@ AllocationSequence::AllocationSequence(BasicPortAllocatorSession* session, state_(kInit), flags_(flags), udp_socket_(), - udp_port_(NULL), + udp_port_(nullptr), phase_(0) {} void AllocationSequence::Init() { @@ -1173,13 +1337,13 @@ void AllocationSequence::Init() { udp_socket_->SignalReadPacket.connect(this, &AllocationSequence::OnReadPacket); } - // Continuing if |udp_socket_| is NULL, as local TCP and RelayPort using TCP - // are next available options to setup a communication channel. + // Continuing if |udp_socket_| is null, as local TCP and RelayPort using + // TCP are next available options to setup a communication channel. } } void AllocationSequence::Clear() { - udp_port_ = NULL; + udp_port_ = nullptr; relay_ports_.clear(); } @@ -1316,7 +1480,7 @@ void AllocationSequence::CreateUDPPorts() { // TODO(mallinath) - Remove UDPPort creating socket after shared socket // is enabled completely. - UDPPort* port = NULL; + UDPPort* port = nullptr; bool emit_local_candidate_for_anyaddress = !IsFlagSet(PORTALLOCATOR_DISABLE_DEFAULT_LOCAL_CANDIDATE); if (IsFlagSet(PORTALLOCATOR_ENABLE_SHARED_SOCKET) && udp_socket_) { @@ -1524,7 +1688,7 @@ void AllocationSequence::CreateTurnPort(const RelayServerConfig& config) { continue; } } - RTC_DCHECK(port != NULL); + RTC_DCHECK(port != nullptr); session_->AddAllocatedPort(port.release(), this, true); } } @@ -1570,7 +1734,7 @@ void AllocationSequence::OnReadPacket(rtc::AsyncPacketSocket* socket, void AllocationSequence::OnPortDestroyed(PortInterface* port) { if (udp_port_ == port) { - udp_port_ = NULL; + udp_port_ = nullptr; return; } diff --git a/p2p/client/basicportallocator.h b/p2p/client/basicportallocator.h index 07bf2f99b8..2105172e4e 100644 --- a/p2p/client/basicportallocator.h +++ b/p2p/client/basicportallocator.h @@ -168,6 +168,10 @@ class BasicPortAllocatorSession : public PortAllocatorSession, bool error() const { return state_ == STATE_ERROR; } bool pruned() const { return state_ == STATE_PRUNED; } bool inprogress() const { return state_ == STATE_INPROGRESS; } + // True if this port has been fired in SignalPortReady. This may be false + // even if ready() is true if the port was bound to the "any" address; see + // comment above SignalAnyAddressPortsAndCandidatesReadyIfNotRedundant. + bool signaled() const { return signaled_; } // Returns true if this port is ready to be used. bool ready() const { return has_pairable_candidate_ && state_ != STATE_ERROR && @@ -191,6 +195,7 @@ class BasicPortAllocatorSession : public PortAllocatorSession, RTC_DCHECK(state_ == STATE_INPROGRESS); state_ = STATE_ERROR; } + void set_signaled() { signaled_ = true; } private: enum State { @@ -203,6 +208,7 @@ class BasicPortAllocatorSession : public PortAllocatorSession, Port* port_ = nullptr; AllocationSequence* sequence_ = nullptr; bool has_pairable_candidate_ = false; + bool signaled_ = false; State state_ = STATE_INPROGRESS; }; @@ -224,7 +230,6 @@ class BasicPortAllocatorSession : public PortAllocatorSession, void OnPortError(Port* port); void OnProtocolEnabled(AllocationSequence* seq, ProtocolType proto); void OnPortDestroyed(PortInterface* port); - void MaybeSignalCandidatesAllocationDone(); void OnPortAllocationComplete(AllocationSequence* seq); PortData* FindPort(Port* port); std::vector GetNetworks(); @@ -241,9 +246,13 @@ class BasicPortAllocatorSession : public PortAllocatorSession, std::vector GetUnprunedPorts( const std::vector& networks); + // Prunes ports and removes candidates gathered from these ports locally. The + // list of the removed candidates are returned. + std::vector PrunePorts( + const std::vector& port_data_list); // Prunes ports and signal the remote side to remove the candidates that // were previously signaled from these ports. - void PrunePortsAndRemoveCandidates( + void PrunePortsAndSignalCandidatesRemoval( const std::vector& port_data_list); // Gets filtered and sanitized candidates generated from a port and // append to |candidates|. @@ -252,6 +261,12 @@ class BasicPortAllocatorSession : public PortAllocatorSession, Port* GetBestTurnPortForNetwork(const std::string& network_name) const; // Returns true if at least one TURN port is pruned. bool PruneTurnPorts(Port* newly_pairable_turn_port); + // Fires signals related to aggregate status update in the allocation, + // including candidates allocation done, and any address ports and their + // candidates ready. + void FireAllocationStatusSignalsIfNeeded(); + // TODO(qingsi): Rename "any address" to "wildcard address" in p2p/. + void SignalAnyAddressPortsAndCandidatesReadyIfNotRedundant(); BasicPortAllocator* allocator_; rtc::Thread* network_thread_; diff --git a/p2p/client/basicportallocator_unittest.cc b/p2p/client/basicportallocator_unittest.cc index b49be8a8f0..b3e52ffec5 100644 --- a/p2p/client/basicportallocator_unittest.cc +++ b/p2p/client/basicportallocator_unittest.cc @@ -1150,15 +1150,20 @@ TEST_F(BasicPortAllocatorTest, TestGetAllPortsWithOneSecondStepDelay) { allocator_->set_step_delay(kDefaultStepDelay); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); + // Host and STUN candidates from kClientAddr. ASSERT_EQ_SIMULATED_WAIT(2U, candidates_.size(), 1000, fake_clock); + // UDP and STUN ports on kClientAddr. EXPECT_EQ(2U, ports_.size()); + // Host, STUN and relay candidates from kClientAddr. ASSERT_EQ_SIMULATED_WAIT(6U, candidates_.size(), 2000, fake_clock); + // UDP, STUN and relay ports on kClientAddr. EXPECT_EQ(3U, ports_.size()); EXPECT_TRUE(HasCandidate(candidates_, "relay", "udp", kRelayUdpIntAddr)); EXPECT_TRUE(HasCandidate(candidates_, "relay", "udp", kRelayUdpExtAddr)); EXPECT_TRUE(HasCandidate(candidates_, "relay", "tcp", kRelayTcpIntAddr)); EXPECT_TRUE( HasCandidate(candidates_, "relay", "ssltcp", kRelaySslTcpIntAddr)); + // One more TCP candidate from kClientAddr. ASSERT_EQ_SIMULATED_WAIT(7U, candidates_.size(), 1500, fake_clock); EXPECT_TRUE(HasCandidate(candidates_, "local", "tcp", kClientAddr)); EXPECT_EQ(4U, ports_.size()); @@ -1468,17 +1473,20 @@ TEST_F(BasicPortAllocatorTest, TestGetAllPortsNoSockets) { // Testing STUN timeout. TEST_F(BasicPortAllocatorTest, TestGetAllPortsNoUdpAllowed) { fss_->AddRule(false, rtc::FP_UDP, rtc::FD_ANY, kClientAddr); + fss_->AddRule(false, rtc::FP_UDP, rtc::FD_ANY, kAnyAddr); AddInterface(kClientAddr); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); EXPECT_EQ_SIMULATED_WAIT(2U, candidates_.size(), kDefaultAllocationTimeout, fake_clock); + // UDP and TCP ports on kClientAddr. EXPECT_EQ(2U, ports_.size()); EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientAddr)); EXPECT_TRUE(HasCandidate(candidates_, "local", "tcp", kClientAddr)); // RelayPort connection timeout is 3sec. TCP connection with RelayServer // will be tried after about 3 seconds. EXPECT_EQ_SIMULATED_WAIT(6U, candidates_.size(), 3500, fake_clock); + // UDP, TCP and relay ports on kClientAddr. EXPECT_EQ(3U, ports_.size()); EXPECT_TRUE(HasCandidate(candidates_, "relay", "udp", kRelayUdpIntAddr)); EXPECT_TRUE(HasCandidate(candidates_, "relay", "tcp", kRelayTcpIntAddr)); @@ -1985,12 +1993,14 @@ TEST_F(BasicPortAllocatorTest, TestSharedSocketNoUdpAllowed) { PORTALLOCATOR_DISABLE_TCP | PORTALLOCATOR_ENABLE_SHARED_SOCKET); fss_->AddRule(false, rtc::FP_UDP, rtc::FD_ANY, kClientAddr); + fss_->AddRule(false, rtc::FP_UDP, rtc::FD_ANY, kAnyAddr); AddInterface(kClientAddr); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_EQ_SIMULATED_WAIT(1U, ports_.size(), kDefaultAllocationTimeout, + ASSERT_EQ_SIMULATED_WAIT(1U, candidates_.size(), kDefaultAllocationTimeout, fake_clock); - EXPECT_EQ(1U, candidates_.size()); + // UDP ports on kClientAddr. + EXPECT_EQ(1U, ports_.size()); EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientAddr)); // STUN timeout is 9.5sec. We need to wait to get candidate done signal. EXPECT_TRUE_SIMULATED_WAIT(candidate_allocation_done_, kStunTimeoutMs, @@ -1998,6 +2008,137 @@ TEST_F(BasicPortAllocatorTest, TestSharedSocketNoUdpAllowed) { EXPECT_EQ(1U, candidates_.size()); } +// Test that any address ports that are redundant do not surface. +TEST_F(BasicPortAllocatorTest, RedundantAnyAddressPortsDoNotSurface) { + allocator().set_flags(allocator().flags() | PORTALLOCATOR_DISABLE_RELAY | + PORTALLOCATOR_DISABLE_TCP | + PORTALLOCATOR_ENABLE_SHARED_SOCKET); + AddInterface(kClientAddr); + // The any address ports will be duplicates of kClientAddr. + vss_->SetAlternativeLocalAddress(kAnyAddr.ipaddr(), kClientAddr.ipaddr()); + ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + EXPECT_TRUE_SIMULATED_WAIT(candidate_allocation_done_, kStunTimeoutMs, + fake_clock); + EXPECT_EQ(1U, ports_.size()); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientAddr)); +} + +// Test that candidates from the any address ports are not pruned if the +// explicit binding to enumerated networks fails. +TEST_F(BasicPortAllocatorTest, + CandidatesFromAnyAddressPortsCanSurfaceWhenExplicitBindingFails) { + allocator().set_flags(allocator().flags() | PORTALLOCATOR_DISABLE_RELAY | + PORTALLOCATOR_DISABLE_TCP | + PORTALLOCATOR_ENABLE_SHARED_SOCKET); + AddInterface(kClientAddr); + fss_->SetUnbindableIps({kClientAddr.ipaddr()}); + // The any address ports will be duplicates of kClientAddr, but the explict + // binding will fail. + vss_->SetAlternativeLocalAddress(kAnyAddr.ipaddr(), kClientAddr.ipaddr()); + ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + EXPECT_TRUE_SIMULATED_WAIT(candidate_allocation_done_, kStunTimeoutMs, + fake_clock); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kAnyAddr)); + EXPECT_EQ(1U, candidates_.size()); + EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientAddr)); +} + +// Test that for an endpoint whose network enumeration only reveals one address +// (kClientAddr), it can observe a different address when binding to the "any" +// address. BasicPortAllocator should detect this and surface candidates for +// each address. +TEST_F(BasicPortAllocatorTest, + CandidatesFromAnyAddressPortsCanSurfaceIfNotRedundant) { + allocator().set_flags(allocator().flags() | PORTALLOCATOR_DISABLE_RELAY | + PORTALLOCATOR_DISABLE_TCP | + PORTALLOCATOR_ENABLE_SHARED_SOCKET); + AddInterface(kClientAddr); + // When bound to the any address, the port allocator should discover the + // alternative local address. + vss_->SetAlternativeLocalAddress(kAnyAddr.ipaddr(), kClientAddr2.ipaddr()); + ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + EXPECT_TRUE_SIMULATED_WAIT(candidate_allocation_done_, kStunTimeoutMs, + fake_clock); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kAnyAddr)); + EXPECT_EQ(2U, candidates_.size()); + EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientAddr)); + EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientAddr2)); +} + +// Test that any address ports and their candidates are eventually signaled +// after the maximum wait interval for the completion of candidate allocation, +// when the any address ports and candidates are not redundant. +TEST_F(BasicPortAllocatorTest, + GetAnyAddressPortsAfterMaximumWaitForCandidateAllocationDone) { + ResetWithTurnServersNoNat(kTurnUdpIntAddr, rtc::SocketAddress()); + AddInterface(kClientAddr); + vss_->SetAlternativeLocalAddress(kAnyAddr.ipaddr(), kClientAddr2.ipaddr()); + // STUN binding request and TURN allocation request will time out. + fss_->AddRule(false, rtc::FP_UDP, rtc::FD_ANY, kClientAddr); + ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + SIMULATED_WAIT(false, kMaxWaitMsBeforeSignalingAnyAddressPortsAndCandidates, + fake_clock); + EXPECT_EQ(2U, candidates_.size()); + EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientAddr)); + EXPECT_TRUE(HasCandidate(candidates_, "local", "tcp", kClientAddr)); + SIMULATED_WAIT(false, 1, fake_clock); + EXPECT_FALSE(candidate_allocation_done_); + // Candidates from the any address ports. + EXPECT_EQ(6U, candidates_.size()); + EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientAddr2)); + EXPECT_TRUE(HasCandidate(candidates_, "stun", "udp", kClientAddr2)); + EXPECT_TRUE(HasCandidate(candidates_, "relay", "udp", kTurnUdpExtAddr)); + EXPECT_TRUE(HasCandidate(candidates_, "local", "tcp", kClientAddr2)); +} + +// Test that the TCP port with the wildcard address is signaled if no ports are +// bound to enuemrated networks. +TEST_F(BasicPortAllocatorTest, + GetAnyAddressTcpPortWhenNoPortsBoundToEnumeratedNetworks) { + ResetWithTurnServersNoNat(kTurnUdpIntAddr, rtc::SocketAddress()); + AddInterface(kClientAddr); + fss_->SetUnbindableIps({kClientAddr.ipaddr()}); + ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + SIMULATED_WAIT(false, + kMaxWaitMsBeforeSignalingAnyAddressPortsAndCandidates + 1, + fake_clock); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_TCP, kAnyAddr)); +} + +// Test that the STUN candidate from the any address port can still surface, if +// it is gathered after this port is signaled, . +TEST_F(BasicPortAllocatorTest, + StunCandidateFromAnyAddressPortsGatheredLateCanBeSignaled) { + ResetWithTurnServersNoNat(kTurnUdpIntAddr, rtc::SocketAddress()); + AddInterface(kClientAddr); + vss_->SetAlternativeLocalAddress(kAnyAddr.ipaddr(), kClientAddr2.ipaddr()); + fss_->AddRule(false, rtc::FP_UDP, rtc::FD_ANY, kClientAddr); + fss_->AddRule(false, rtc::FP_UDP, rtc::FD_ANY, kClientAddr2); + ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + SIMULATED_WAIT(false, + kMaxWaitMsBeforeSignalingAnyAddressPortsAndCandidates + 1000, + fake_clock); + EXPECT_FALSE(candidate_allocation_done_); + EXPECT_EQ(4U, candidates_.size()); + EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientAddr)); + EXPECT_TRUE(HasCandidate(candidates_, "local", "tcp", kClientAddr)); + EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientAddr2)); + EXPECT_TRUE(HasCandidate(candidates_, "local", "tcp", kClientAddr2)); + fss_->ClearRules(); + EXPECT_TRUE_SIMULATED_WAIT(candidate_allocation_done_, + kDefaultAllocationTimeout, fake_clock); + EXPECT_EQ(7U, candidates_.size()); + EXPECT_TRUE(HasCandidate(candidates_, "stun", "udp", kClientAddr)); + EXPECT_TRUE(HasCandidate(candidates_, "stun", "udp", kClientAddr2)); + EXPECT_TRUE(HasCandidate(candidates_, "relay", "udp", kTurnUdpExtAddr)); +} + // Test that when the NetworkManager doesn't have permission to enumerate // adapters, the PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION is specified // automatically.