diff --git a/webrtc/api/java/jni/androidnetworkmonitor_jni.cc b/webrtc/api/java/jni/androidnetworkmonitor_jni.cc index 6362764fcc..b7857e2ea9 100644 --- a/webrtc/api/java/jni/androidnetworkmonitor_jni.cc +++ b/webrtc/api/java/jni/androidnetworkmonitor_jni.cc @@ -268,13 +268,15 @@ int AndroidNetworkMonitor::BindSocketToNetwork(int socket_fd, void AndroidNetworkMonitor::OnNetworkConnected( const NetworkInformation& network_info) { - LOG(LS_INFO) << "Network connected: " << network_info.ToString(); worker_thread()->Invoke(rtc::Bind( &AndroidNetworkMonitor::OnNetworkConnected_w, this, network_info)); + // Fire SignalNetworksChanged to update the list of networks. + OnNetworksChanged(); } void AndroidNetworkMonitor::OnNetworkConnected_w( const NetworkInformation& network_info) { + LOG(LS_INFO) << "Network connected: " << network_info.ToString(); adapter_type_by_name_[network_info.interface_name] = AdapterTypeFromNetworkType(network_info.type); network_info_by_handle_[network_info.handle] = network_info; @@ -304,6 +306,8 @@ void AndroidNetworkMonitor::SetNetworkInfos( RTC_CHECK(thread_checker_.CalledOnValidThread()); network_handle_by_address_.clear(); network_info_by_handle_.clear(); + LOG(LS_INFO) << "Android network monitor found " << network_infos.size() + << " networks"; for (NetworkInformation network : network_infos) { OnNetworkConnected_w(network); } @@ -312,11 +316,13 @@ void AndroidNetworkMonitor::SetNetworkInfos( rtc::AdapterType AndroidNetworkMonitor::GetAdapterType( const std::string& if_name) { auto iter = adapter_type_by_name_.find(if_name); - if (iter == adapter_type_by_name_.end()) { - LOG(LS_WARNING) << "Get adapter type for an unknown interface: " << if_name; - return rtc::ADAPTER_TYPE_UNKNOWN; + rtc::AdapterType type = (iter == adapter_type_by_name_.end()) + ? rtc::ADAPTER_TYPE_UNKNOWN + : iter->second; + if (type == rtc::ADAPTER_TYPE_UNKNOWN) { + LOG(LS_WARNING) << "Get an unknown type for the interface " << if_name; } - return iter->second; + return type; } rtc::NetworkMonitorInterface* diff --git a/webrtc/api/webrtcsdp.cc b/webrtc/api/webrtcsdp.cc index b86d9038dd..fa052419c0 100644 --- a/webrtc/api/webrtcsdp.cc +++ b/webrtc/api/webrtcsdp.cc @@ -1117,7 +1117,7 @@ bool ParseCandidate(const std::string& message, Candidate* candidate, if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) { return false; } - network_cost = std::min(network_cost, cricket::kMaxNetworkCost); + network_cost = std::min(network_cost, rtc::kNetworkCostMax); } else { // Skip the unknown extension. ++i; diff --git a/webrtc/base/network.cc b/webrtc/base/network.cc index b6caaa8bbf..5031e967a5 100644 --- a/webrtc/base/network.cc +++ b/webrtc/base/network.cc @@ -292,6 +292,11 @@ void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks, Network* existing_net = existing->second; *changed = existing_net->SetIPs(kv.second.ips, *changed); merged_list.push_back(existing_net); + if (net->type() != ADAPTER_TYPE_UNKNOWN && + net->type() != existing_net->type()) { + existing_net->set_type(net->type()); + *changed = true; + } // If the existing network was not active, networks have changed. if (!existing_net->active()) { *changed = true; @@ -394,7 +399,7 @@ BasicNetworkManager::~BasicNetworkManager() { } void BasicNetworkManager::OnNetworksChanged() { - LOG(LS_VERBOSE) << "Network change was observed at the network manager"; + LOG(LS_INFO) << "Network change was observed"; UpdateNetworksOnce(); } @@ -452,24 +457,18 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, reinterpret_cast(cursor->ifa_addr)->sin6_scope_id; } + AdapterType adapter_type = ADAPTER_TYPE_UNKNOWN; + if (cursor->ifa_flags & IFF_LOOPBACK) { + adapter_type = ADAPTER_TYPE_LOOPBACK; + } else { + adapter_type = GetAdapterTypeFromName(cursor->ifa_name); + } int prefix_length = CountIPMaskBits(mask); prefix = TruncateIP(ip, prefix_length); std::string key = MakeNetworkKey(std::string(cursor->ifa_name), prefix, prefix_length); - auto existing_network = current_networks.find(key); - if (existing_network == current_networks.end()) { - AdapterType adapter_type = ADAPTER_TYPE_UNKNOWN; - if (cursor->ifa_flags & IFF_LOOPBACK) { - adapter_type = ADAPTER_TYPE_LOOPBACK; - } else if (network_monitor_) { - adapter_type = network_monitor_->GetAdapterType(cursor->ifa_name); - } -#if defined(WEBRTC_IOS) - // Cell networks are pdp_ipN on iOS. - if (strncmp(cursor->ifa_name, "pdp_ip", 6) == 0) { - adapter_type = ADAPTER_TYPE_CELLULAR; - } -#endif + auto iter = current_networks.find(key); + if (iter == current_networks.end()) { // TODO(phoglund): Need to recognize other types as well. std::unique_ptr network( new Network(cursor->ifa_name, cursor->ifa_name, prefix, prefix_length, @@ -483,7 +482,11 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, networks->push_back(network.release()); } } else { - (*existing_network).second->AddIP(ip); + Network* existing_network = iter->second; + existing_network->AddIP(ip); + if (adapter_type != ADAPTER_TYPE_UNKNOWN) { + existing_network->set_type(adapter_type); + } } } } @@ -787,6 +790,34 @@ void BasicNetworkManager::OnMessage(Message* msg) { } } +AdapterType BasicNetworkManager::GetAdapterTypeFromName( + const char* network_name) const { + // If there is a network_monitor, use it to get the adapter type. + // Otherwise, get the adapter type based on a few name matching rules. + if (network_monitor_) { + AdapterType type = network_monitor_->GetAdapterType(network_name); + if (type != ADAPTER_TYPE_UNKNOWN) { + return type; + } + } +#if defined(WEBRTC_IOS) + // Cell networks are pdp_ipN on iOS. + if (strncmp(network_name, "pdp_ip", 6) == 0) { + return ADAPTER_TYPE_CELLULAR; + } +#elif defined(WEBRTC_ANDROID) + if (strncmp(network_name, "rmnet", 5) == 0 || + strncmp(network_name, "v4-rmnet", 8) == 0) { + return ADAPTER_TYPE_CELLULAR; + } + if (strncmp(network_name, "wlan", 4) == 0) { + return ADAPTER_TYPE_WIFI; + } +#endif + + return ADAPTER_TYPE_UNKNOWN; +} + IPAddress BasicNetworkManager::QueryDefaultLocalAddress(int family) const { ASSERT(thread_ == Thread::Current()); ASSERT(thread_->socketserver() != nullptr); diff --git a/webrtc/base/network.h b/webrtc/base/network.h index a41da4a69a..05943fb912 100644 --- a/webrtc/base/network.h +++ b/webrtc/base/network.h @@ -37,6 +37,11 @@ class Network; class NetworkMonitorInterface; class Thread; +static const uint16_t kNetworkCostMax = 999; +static const uint16_t kNetworkCostHigh = 900; +static const uint16_t kNetworkCostUnknown = 50; +static const uint16_t kNetworkCostLow = 10; +static const uint16_t kNetworkCostMin = 0; // By default, ignore loopback interfaces on the host. const int kDefaultNetworkIgnoreMask = ADAPTER_TYPE_LOOPBACK; @@ -249,6 +254,8 @@ class BasicNetworkManager : public NetworkManagerBase, // Only updates the networks; does not reschedule the next update. void UpdateNetworksOnce(); + AdapterType GetAdapterTypeFromName(const char* network_name) const; + Thread* thread_; bool sent_first_update_; int start_count_; @@ -273,6 +280,7 @@ class Network { ~Network(); sigslot::signal1 SignalInactive; + sigslot::signal1 SignalTypeChanged; const DefaultLocalAddressProvider* default_local_address_provider() { return default_local_address_provider_; @@ -344,8 +352,28 @@ class Network { void set_ignored(bool ignored) { ignored_ = ignored; } AdapterType type() const { return type_; } - void set_type(AdapterType type) { type_ = type; } + void set_type(AdapterType type) { + if (type_ == type) { + return; + } + type_ = type; + SignalTypeChanged(this); + } + uint16_t GetCost() const { + switch (type_) { + case rtc::ADAPTER_TYPE_ETHERNET: + case rtc::ADAPTER_TYPE_LOOPBACK: + return kNetworkCostMin; + case rtc::ADAPTER_TYPE_WIFI: + case rtc::ADAPTER_TYPE_VPN: + return kNetworkCostLow; + case rtc::ADAPTER_TYPE_CELLULAR: + return kNetworkCostHigh; + default: + return kNetworkCostUnknown; + } + } // A unique id assigned by the network manager, which may be signaled // to the remote side in the candidate. uint16_t id() const { return id_; } diff --git a/webrtc/base/network_unittest.cc b/webrtc/base/network_unittest.cc index f3193e2b0f..9572c69746 100644 --- a/webrtc/base/network_unittest.cc +++ b/webrtc/base/network_unittest.cc @@ -34,6 +34,14 @@ class FakeNetworkMonitor : public NetworkMonitorBase { void Stop() override { started_ = false; } bool started() { return started_; } AdapterType GetAdapterType(const std::string& if_name) override { + // Note that the name matching rules are different from the + // GetAdapterTypeFromName in NetworkManager. + if (if_name.find("wifi") == 0) { + return ADAPTER_TYPE_WIFI; + } + if (if_name.find("cellular") == 0) { + return ADAPTER_TYPE_CELLULAR; + } return ADAPTER_TYPE_UNKNOWN; } @@ -102,6 +110,13 @@ class NetworkTest : public testing::Test, public sigslot::has_slots<> { network_manager.networks_map_.clear(); } + AdapterType GetAdapterType(BasicNetworkManager& network_manager) { + BasicNetworkManager::NetworkList list; + network_manager.GetNetworks(&list); + ASSERT(list.size() == 1u); + return list[0]->type(); + } + #if defined(WEBRTC_POSIX) // Separated from CreateNetworks for tests. static void CallConvertIfAddrs(const BasicNetworkManager& network_manager, @@ -144,6 +159,20 @@ class NetworkTest : public testing::Test, public sigslot::has_slots<> { return if_addr; } + struct ifaddrs* InstallIpv6Network(char* if_name, + const std::string& ipv6_address, + const std::string& ipv6_mask, + BasicNetworkManager& network_manager) { + ifaddrs* addr_list = nullptr; + addr_list = AddIpv6Address(addr_list, if_name, ipv6_address, ipv6_mask, 0); + NetworkManager::NetworkList result; + bool changed; + NetworkManager::Stats stats; + CallConvertIfAddrs(network_manager, addr_list, true, &result); + network_manager.MergeNetworkList(result, &changed, &stats); + return addr_list; + } + void ReleaseIfAddrs(struct ifaddrs* list) { struct ifaddrs* if_addr = list; while (if_addr != nullptr) { @@ -781,6 +810,93 @@ TEST_F(NetworkTest, TestConvertIfAddrsNotRunning) { CallConvertIfAddrs(manager, &list, true, &result); EXPECT_TRUE(result.empty()); } + +// Tests that the network type can be updated after the network monitor is +// started. +TEST_F(NetworkTest, TestGetAdapterTypeFromNetworkMonitor) { + char if_name1[20] = "wifi0"; + std::string ipv6_address1 = "1000:2000:3000:4000:0:0:0:1"; + std::string ipv6_address2 = "1000:2000:3000:8000:0:0:0:1"; + std::string ipv6_mask = "FFFF:FFFF:FFFF:FFFF::"; + BasicNetworkManager manager; + // A network created before the network monitor is started will get + // UNKNOWN type. + ifaddrs* addr_list = + InstallIpv6Network(if_name1, ipv6_address1, ipv6_mask, manager); + EXPECT_EQ(ADAPTER_TYPE_UNKNOWN, GetAdapterType(manager)); + ReleaseIfAddrs(addr_list); + // Note: Do not call ClearNetworks here in order to test that the type + // of an existing network can be changed after the network monitor starts + // and detects the network type correctly. + + // After the network monitor starts, the type will be updated. + FakeNetworkMonitorFactory* factory = new FakeNetworkMonitorFactory(); + NetworkMonitorFactory::SetFactory(factory); + // This brings up the hook with the network monitor. + manager.StartUpdating(); + // Add the same ipv6 address as before but it has the right network type + // detected by the network monitor now. + addr_list = InstallIpv6Network(if_name1, ipv6_address1, ipv6_mask, manager); + EXPECT_EQ(ADAPTER_TYPE_WIFI, GetAdapterType(manager)); + ReleaseIfAddrs(addr_list); + ClearNetworks(manager); + + // Add another network with the type inferred from the network monitor. + char if_name2[20] = "cellular0"; + addr_list = InstallIpv6Network(if_name2, ipv6_address2, ipv6_mask, manager); + EXPECT_EQ(ADAPTER_TYPE_CELLULAR, GetAdapterType(manager)); + ReleaseIfAddrs(addr_list); + ClearNetworks(manager); +} + +// Test that the network type can be determined based on name matching in +// a few cases. Note that UNKNOWN type for non-matching strings has been tested +// in the above test. +TEST_F(NetworkTest, TestGetAdapterTypeFromNameMatching) { + std::string ipv6_address1 = "1000:2000:3000:4000:0:0:0:1"; + std::string ipv6_address2 = "1000:2000:3000:8000:0:0:0:1"; + std::string ipv6_mask = "FFFF:FFFF:FFFF:FFFF::"; + BasicNetworkManager manager; + +#if defined(WEBRTC_IOS) + char if_name[20] = "pdp_ip0"; + ifaddrs* addr_list = + InstallIpv6Network(if_name, ipv6_address1, ipv6_mask, manager); + + EXPECT_EQ(ADAPTER_TYPE_CELLULAR, GetAdapterType(manager)); + ClearNetworks(manager); + ReleaseIfAddrs(addr_list); + +#elif defined(WEBRTC_ANDROID) + char if_name[20] = "rmnet0"; + ifaddrs* addr_list = + InstallIpv6Network(if_name, ipv6_address1, ipv6_mask, manager); + + EXPECT_EQ(ADAPTER_TYPE_CELLULAR, GetAdapterType(manager)); + ClearNetworks(manager); + ReleaseIfAddrs(addr_list); + + strcpy(if_name, "wlan1"); + addr_list = InstallIpv6Network(if_name, ipv6_address2, ipv6_mask, manager); + EXPECT_EQ(ADAPTER_TYPE_WIFI, GetAdapterType(manager)); + ClearNetworks(manager); + ReleaseIfAddrs(addr_list); + + strcpy(if_name, "v4-rmnet_data0"); + addr_list = InstallIpv6Network(if_name, ipv6_address2, ipv6_mask, manager); + EXPECT_EQ(ADAPTER_TYPE_CELLULAR, GetAdapterType(manager)); + ClearNetworks(manager); + ReleaseIfAddrs(addr_list); +#else + char if_name[20] = "wlan0"; + ifaddrs* addr_list = + InstallIpv6Network(if_name, ipv6_address1, ipv6_mask, manager); + + EXPECT_EQ(ADAPTER_TYPE_UNKNOWN, GetAdapterType(manager)); + ClearNetworks(manager); + ReleaseIfAddrs(addr_list); +#endif +} #endif // defined(WEBRTC_POSIX) #if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) diff --git a/webrtc/p2p/base/candidate.h b/webrtc/p2p/base/candidate.h index b918344740..4eb05f0c63 100644 --- a/webrtc/p2p/base/candidate.h +++ b/webrtc/p2p/base/candidate.h @@ -27,8 +27,6 @@ namespace cricket { -const uint16_t kMaxNetworkCost = 999; - // Candidate for ICE based connection discovery. class Candidate { @@ -148,9 +146,9 @@ class Candidate { // |network_cost| measures the cost/penalty of using this candidate. A network // cost of 0 indicates this candidate can be used freely. A value of - // |kMaxNetworkCost| indicates it should be used only as the last resort. + // rtc::kNetworkCostMax indicates it should be used only as the last resort. void set_network_cost(uint16_t network_cost) { - ASSERT(network_cost <= kMaxNetworkCost); + ASSERT(network_cost <= rtc::kNetworkCostMax); network_cost_ = network_cost; } uint16_t network_cost() const { return network_cost_; } diff --git a/webrtc/p2p/base/port.cc b/webrtc/p2p/base/port.cc index fbc64f2e0a..bd7368bf3a 100644 --- a/webrtc/p2p/base/port.cc +++ b/webrtc/p2p/base/port.cc @@ -75,7 +75,7 @@ const int RTT_RATIO = 3; // 3 : 1 // The delay before we begin checking if this port is useless. const int kPortTimeoutDelay = 30 * 1000; // 30 seconds -} +} // namespace namespace cricket { @@ -196,11 +196,10 @@ void Port::Construct() { password_ = rtc::CreateRandomString(ICE_PWD_LENGTH); } network_->SignalInactive.connect(this, &Port::OnNetworkInactive); - // TODO(honghaiz): Make it configurable from user setting. - network_cost_ = - (network_->type() == rtc::ADAPTER_TYPE_CELLULAR) ? kMaxNetworkCost : 0; + network_->SignalTypeChanged.connect(this, &Port::OnNetworkTypeChanged); + network_cost_ = network_->GetCost(); - LOG_J(LS_INFO, this) << "Port created"; + LOG_J(LS_INFO, this) << "Port created with network cost " << network_cost_; } Port::~Port() { @@ -645,6 +644,12 @@ void Port::OnNetworkInactive(const rtc::Network* network) { SignalNetworkInactive(this); } +void Port::OnNetworkTypeChanged(const rtc::Network* network) { + ASSERT(network == network_); + + UpdateNetworkCost(); +} + std::string Port::ToString() const { std::stringstream ss; ss << "Port[" << std::hex << this << std::dec << ":" << content_name_ << ":" @@ -653,6 +658,29 @@ std::string Port::ToString() const { return ss.str(); } +// TODO(honghaiz): Make the network cost configurable from user setting. +void Port::UpdateNetworkCost() { + uint16_t new_cost = network_->GetCost(); + if (network_cost_ == new_cost) { + return; + } + LOG(LS_INFO) << "Network cost changed from " << network_cost_ + << " to " << new_cost + << ". Number of candidates created: " << candidates_.size() + << ". Number of connections created: " << connections_.size(); + network_cost_ = new_cost; + for (cricket::Candidate& candidate : candidates_) { + candidate.set_network_cost(network_cost_); + } + // Network cost change will affect the connection selection criteria. + // Signal the connection state change on each connection to force a + // re-sort in P2PTransportChannel. + for (auto kv : connections_) { + Connection* conn = kv.second; + conn->SignalStateChange(conn); + } +} + void Port::EnablePortPackets() { enable_port_packets_ = true; } @@ -1008,6 +1036,21 @@ void Connection::HandleBindingRequest(IceMessage* msg) { 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() { @@ -1178,7 +1221,7 @@ std::string Connection::ToDebugId() const { uint32_t Connection::ComputeNetworkCost() const { // TODO(honghaiz): Will add rtt as part of the network cost. - return local_candidate().network_cost() + remote_candidate_.network_cost(); + return port()->network_cost() + remote_candidate_.network_cost(); } std::string Connection::ToString() const { diff --git a/webrtc/p2p/base/port.h b/webrtc/p2p/base/port.h index 937f3cd028..e72e9dcc31 100644 --- a/webrtc/p2p/base/port.h +++ b/webrtc/p2p/base/port.h @@ -304,6 +304,8 @@ class Port : public PortInterface, public rtc::MessageHandler, MSG_FIRST_AVAILABLE }; + virtual void UpdateNetworkCost(); + void set_type(const std::string& type) { type_ = type; } void AddAddress(const rtc::SocketAddress& address, @@ -362,6 +364,8 @@ class Port : public PortInterface, public rtc::MessageHandler, void OnNetworkInactive(const rtc::Network* network); + void OnNetworkTypeChanged(const rtc::Network* network); + rtc::Thread* thread_; rtc::PacketSocketFactory* factory_; std::string type_; diff --git a/webrtc/p2p/base/port_unittest.cc b/webrtc/p2p/base/port_unittest.cc index efc2609401..a5cbd92a7b 100644 --- a/webrtc/p2p/base/port_unittest.cc +++ b/webrtc/p2p/base/port_unittest.cc @@ -759,7 +759,6 @@ class PortTest : public testing::Test, public sigslot::has_slots<> { return &nat_socket_factory1_; } - protected: rtc::VirtualSocketServer* vss() { return ss_.get(); } private: @@ -1762,11 +1761,69 @@ TEST_F(PortTest, TestUseCandidateAttribute) { ASSERT_TRUE(use_candidate_attr != NULL); } +// Tests that when the network type changes, the network cost of the port will +// change, the network cost of the local candidates will change. Also tests that +// the remote network costs are updated with the stun binding requests. +TEST_F(PortTest, TestNetworkCostChange) { + std::unique_ptr lport( + CreateTestPort(kLocalAddr1, "lfrag", "lpass")); + std::unique_ptr rport( + CreateTestPort(kLocalAddr2, "rfrag", "rpass")); + lport->SetIceRole(cricket::ICEROLE_CONTROLLING); + lport->SetIceTiebreaker(kTiebreaker1); + rport->SetIceRole(cricket::ICEROLE_CONTROLLED); + rport->SetIceTiebreaker(kTiebreaker2); + lport->PrepareAddress(); + rport->PrepareAddress(); + + // Default local port cost is rtc::kNetworkCostUnknown. + EXPECT_EQ(rtc::kNetworkCostUnknown, lport->network_cost()); + ASSERT_TRUE(!lport->Candidates().empty()); + for (const cricket::Candidate& candidate : lport->Candidates()) { + EXPECT_EQ(rtc::kNetworkCostUnknown, candidate.network_cost()); + } + + // Change the network type to wifi. + SetNetworkType(rtc::ADAPTER_TYPE_WIFI); + EXPECT_EQ(rtc::kNetworkCostLow, lport->network_cost()); + for (const cricket::Candidate& candidate : lport->Candidates()) { + EXPECT_EQ(rtc::kNetworkCostLow, candidate.network_cost()); + } + + // Add a connection and then change the network type. + Connection* lconn = + lport->CreateConnection(rport->Candidates()[0], Port::ORIGIN_MESSAGE); + // Change the network type to cellular. + SetNetworkType(rtc::ADAPTER_TYPE_CELLULAR); + EXPECT_EQ(rtc::kNetworkCostHigh, lport->network_cost()); + for (const cricket::Candidate& candidate : lport->Candidates()) { + EXPECT_EQ(rtc::kNetworkCostHigh, candidate.network_cost()); + } + + SetNetworkType(rtc::ADAPTER_TYPE_WIFI); + Connection* rconn = + rport->CreateConnection(lport->Candidates()[0], Port::ORIGIN_MESSAGE); + SetNetworkType(rtc::ADAPTER_TYPE_CELLULAR); + lconn->Ping(0); + // The rconn's remote candidate cost is rtc::kNetworkCostLow, but the ping + // contains an attribute of network cost of rtc::kNetworkCostHigh. Once the + // message is handled in rconn, The rconn's remote candidate will have cost + // rtc::kNetworkCostHigh; + EXPECT_EQ(rtc::kNetworkCostLow, rconn->remote_candidate().network_cost()); + ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, 1000); + IceMessage* msg = lport->last_stun_msg(); + EXPECT_EQ(STUN_BINDING_REQUEST, msg->type()); + // Pass the binding request to rport. + rconn->OnReadPacket(lport->last_stun_buf()->data(), + lport->last_stun_buf()->size(), rtc::PacketTime()); + // Wait until rport sends the response and then check the remote network cost. + ASSERT_TRUE_WAIT(rport->last_stun_msg() != NULL, 1000); + EXPECT_EQ(rtc::kNetworkCostHigh, rconn->remote_candidate().network_cost()); +} + TEST_F(PortTest, TestNetworkInfoAttribute) { std::unique_ptr lport( CreateTestPort(kLocalAddr1, "lfrag", "lpass")); - // Set the network type for rport to be cellular so its cost will be 999. - SetNetworkType(rtc::ADAPTER_TYPE_CELLULAR); std::unique_ptr rport( CreateTestPort(kLocalAddr2, "rfrag", "rpass")); lport->SetIceRole(cricket::ICEROLE_CONTROLLING); @@ -1789,10 +1846,12 @@ TEST_F(PortTest, TestNetworkInfoAttribute) { ASSERT_TRUE(network_info_attr != NULL); uint32_t network_info = network_info_attr->value(); EXPECT_EQ(lnetwork_id, network_info >> 16); - // Default network cost is 0. - EXPECT_EQ(0U, network_info & 0xFFFF); + // Default network has unknown type and cost kNetworkCostUnknown. + EXPECT_EQ(rtc::kNetworkCostUnknown, network_info & 0xFFFF); + // Set the network type to be cellular so its cost will be kNetworkCostHigh. // Send a fake ping from rport to lport. + SetNetworkType(rtc::ADAPTER_TYPE_CELLULAR); uint16_t rnetwork_id = 8; rport->Network()->set_id(rnetwork_id); Connection* rconn = @@ -1804,7 +1863,7 @@ TEST_F(PortTest, TestNetworkInfoAttribute) { ASSERT_TRUE(network_info_attr != NULL); network_info = network_info_attr->value(); EXPECT_EQ(rnetwork_id, network_info >> 16); - EXPECT_EQ(cricket::kMaxNetworkCost, network_info & 0xFFFF); + EXPECT_EQ(rtc::kNetworkCostHigh, network_info & 0xFFFF); } // Test handling STUN messages. diff --git a/webrtc/p2p/base/stunport.cc b/webrtc/p2p/base/stunport.cc index 8ed8c44887..15c00957ef 100644 --- a/webrtc/p2p/base/stunport.cc +++ b/webrtc/p2p/base/stunport.cc @@ -25,22 +25,14 @@ namespace cricket { // TODO: Move these to a common place (used in relayport too) const int KEEPALIVE_DELAY = 10 * 1000; // 10 seconds - sort timeouts const int RETRY_TIMEOUT = 50 * 1000; // 50 seconds -// Lifetime chosen for STUN ports on low-cost networks. -const int INFINITE_LIFETIME = -1; -// Lifetime for STUN ports on high-cost networks: 2 minutes -const int HIGH_COST_PORT_KEEPALIVE_LIFETIME = 2 * 60 * 1000; // Handles a binding request sent to the STUN server. class StunBindingRequest : public StunRequest { public: StunBindingRequest(UDPPort* port, const rtc::SocketAddress& addr, - int64_t start_time, - int lifetime) - : port_(port), - server_addr_(addr), - start_time_(start_time), - lifetime_(lifetime) {} + int64_t start_time) + : port_(port), server_addr_(addr), start_time_(start_time) {} virtual ~StunBindingRequest() { } @@ -67,7 +59,7 @@ class StunBindingRequest : public StunRequest { // The keep-alive requests will be stopped after its lifetime has passed. if (WithinLifetime(rtc::TimeMillis())) { port_->requests_.SendDelayed( - new StunBindingRequest(port_, server_addr_, start_time_, lifetime_), + new StunBindingRequest(port_, server_addr_, start_time_), port_->stun_keepalive_delay()); } } @@ -89,7 +81,7 @@ class StunBindingRequest : public StunRequest { if (WithinLifetime(now) && rtc::TimeDiff(now, start_time_) < RETRY_TIMEOUT) { port_->requests_.SendDelayed( - new StunBindingRequest(port_, server_addr_, start_time_, lifetime_), + new StunBindingRequest(port_, server_addr_, start_time_), port_->stun_keepalive_delay()); } } @@ -105,14 +97,13 @@ class StunBindingRequest : public StunRequest { // Returns true if |now| is within the lifetime of the request (a negative // lifetime means infinite). bool WithinLifetime(int64_t now) const { - return lifetime_ < 0 || rtc::TimeDiff(now, start_time_) <= lifetime_; + int lifetime = port_->stun_keepalive_lifetime(); + return lifetime < 0 || rtc::TimeDiff(now, start_time_) <= lifetime; } UDPPort* port_; const rtc::SocketAddress server_addr_; int64_t start_time_; - // The time duration for which this request will be rescheduled. - int lifetime_; }; UDPPort::AddressResolver::AddressResolver( @@ -220,12 +211,7 @@ UDPPort::UDPPort(rtc::Thread* thread, } bool UDPPort::Init() { - // If this is a zero-cost network, it will keep on sending STUN binding - // requests indefinitely to keep the NAT binding alive. Otherwise, stop - // sending STUN binding requests after HIGH_COST_PORT_KEEPALIVE_LIFETIME. - stun_keepalive_lifetime_ = (network_cost() == 0) - ? INFINITE_LIFETIME - : HIGH_COST_PORT_KEEPALIVE_LIFETIME; + stun_keepalive_lifetime_ = GetStunKeepaliveLifetime(); if (!SharedSocket()) { ASSERT(socket_ == NULL); socket_ = socket_factory()->CreateUdpSocket( @@ -299,6 +285,11 @@ int UDPPort::SendTo(const void* data, size_t size, return sent; } +void UDPPort::UpdateNetworkCost() { + Port::UpdateNetworkCost(); + stun_keepalive_lifetime_ = GetStunKeepaliveLifetime(); +} + int UDPPort::SetOption(rtc::Socket::Option opt, int value) { return socket_->SetOption(opt, value); } @@ -411,8 +402,8 @@ void UDPPort::SendStunBindingRequest(const rtc::SocketAddress& stun_addr) { } else if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND) { // Check if |server_addr_| is compatible with the port's ip. if (IsCompatibleAddress(stun_addr)) { - requests_.Send(new StunBindingRequest(this, stun_addr, rtc::TimeMillis(), - stun_keepalive_lifetime_)); + requests_.Send( + new StunBindingRequest(this, stun_addr, rtc::TimeMillis())); } else { // Since we can't send stun messages to the server, we should mark this // port ready. diff --git a/webrtc/p2p/base/stunport.h b/webrtc/p2p/base/stunport.h index cd844aa273..82be6fae2a 100644 --- a/webrtc/p2p/base/stunport.h +++ b/webrtc/p2p/base/stunport.h @@ -26,6 +26,11 @@ class SignalThread; namespace cricket { +// Lifetime chosen for STUN ports on low-cost networks. +static const int INFINITE_LIFETIME = -1; +// Lifetime for STUN ports on high-cost networks: 2 minutes +static const int HIGH_COST_PORT_KEEPALIVE_LIFETIME = 2 * 60 * 1000; + // Communicates using the address on the outside of a NAT. class UDPPort : public Port { public: @@ -145,6 +150,8 @@ class UDPPort : public Port { const rtc::PacketOptions& options, bool payload); + virtual void UpdateNetworkCost(); + void OnLocalAddressReady(rtc::AsyncPacketSocket* socket, const rtc::SocketAddress& address); void OnReadPacket(rtc::AsyncPacketSocket* socket, @@ -219,6 +226,15 @@ class UDPPort : public Port { bool HasCandidateWithAddress(const rtc::SocketAddress& addr) const; + // If this is a low-cost network, it will keep on sending STUN binding + // requests indefinitely to keep the NAT binding alive. Otherwise, stop + // sending STUN binding requests after HIGH_COST_PORT_KEEPALIVE_LIFETIME. + int GetStunKeepaliveLifetime() { + return (network_cost() >= rtc::kNetworkCostHigh) + ? HIGH_COST_PORT_KEEPALIVE_LIFETIME + : INFINITE_LIFETIME; + } + ServerAddresses server_addresses_; ServerAddresses bind_request_succeeded_servers_; ServerAddresses bind_request_failed_servers_; @@ -228,7 +244,7 @@ class UDPPort : public Port { std::unique_ptr resolver_; bool ready_; int stun_keepalive_delay_; - int stun_keepalive_lifetime_; + int stun_keepalive_lifetime_ = INFINITE_LIFETIME; // This is true by default and false when // PORTALLOCATOR_DISABLE_DEFAULT_LOCAL_CANDIDATE is specified. diff --git a/webrtc/p2p/base/stunport_unittest.cc b/webrtc/p2p/base/stunport_unittest.cc index 702ec2504e..ba4ca364b4 100644 --- a/webrtc/p2p/base/stunport_unittest.cc +++ b/webrtc/p2p/base/stunport_unittest.cc @@ -26,6 +26,7 @@ using rtc::SocketAddress; static const SocketAddress kLocalAddr("127.0.0.1", 0); static const SocketAddress kStunAddr1("127.0.0.1", 5000); static const SocketAddress kStunAddr2("127.0.0.1", 4000); +static const SocketAddress kStunAddr3("127.0.0.1", 3000); static const SocketAddress kBadAddr("0.0.0.1", 5000); static const SocketAddress kStunHostnameAddr("localhost", 5000); static const SocketAddress kBadHostnameAddr("not-a-real-hostname", 5000); @@ -306,31 +307,39 @@ TEST_F(StunPortTest, TestTwoCandidatesWithTwoStunServersAcrossNat) { } // Test that the stun_keepalive_lifetime is set correctly based on the network -// type on a STUN port. +// type on a STUN port. Also test that it will be updated if the network type +// changes. TEST_F(StunPortTest, TestStunPortGetStunKeepaliveLifetime) { - // Lifetime for the default network type is |kInfiniteLifetime|. + // Lifetime for the default (unknown) network type is |kInfiniteLifetime|. CreateStunPort(kStunAddr1); EXPECT_EQ(kInfiniteLifetime, port()->stun_keepalive_lifetime()); - // Lifetime for the cellular network is |kHighCostPortKeepaliveLifetimeMs| SetNetworkType(rtc::ADAPTER_TYPE_CELLULAR); - CreateStunPort(kStunAddr2); EXPECT_EQ(kHighCostPortKeepaliveLifetimeMs, port()->stun_keepalive_lifetime()); + + // Lifetime for the wifi network is |kInfiniteLifetime|. + SetNetworkType(rtc::ADAPTER_TYPE_WIFI); + CreateStunPort(kStunAddr2); + EXPECT_EQ(kInfiniteLifetime, port()->stun_keepalive_lifetime()); } // Test that the stun_keepalive_lifetime is set correctly based on the network -// type on a shared STUN port (UDPPort). +// type on a shared STUN port (UDPPort). Also test that it will be updated +// if the network type changes. TEST_F(StunPortTest, TestUdpPortGetStunKeepaliveLifetime) { - // Lifetime for the default network type is |kInfiniteLifetime|. + // Lifetime for the default (unknown) network type is |kInfiniteLifetime|. CreateSharedStunPort(kStunAddr1); EXPECT_EQ(kInfiniteLifetime, port()->stun_keepalive_lifetime()); - - // Lifetime for the cellular network is |kHighCostPortKeepaliveLifetimeMs| + // Lifetime for the cellular network is |kHighCostPortKeepaliveLifetimeMs|. SetNetworkType(rtc::ADAPTER_TYPE_CELLULAR); - CreateSharedStunPort(kStunAddr2); EXPECT_EQ(kHighCostPortKeepaliveLifetimeMs, port()->stun_keepalive_lifetime()); + + // Lifetime for the wifi network type is |kInfiniteLifetime|. + SetNetworkType(rtc::ADAPTER_TYPE_WIFI); + CreateSharedStunPort(kStunAddr2); + EXPECT_EQ(kInfiniteLifetime, port()->stun_keepalive_lifetime()); } // Test that STUN binding requests will be stopped shortly if the keep-alive