Update the type and cost of existing networks
if the network monitor detects it after the native code does. Also set the network cost for ethernet, wifi, unknown, cellular network type to be 0, 10, 50, 900, so that unknown networks will have lower precedence than known networks with low cost (like Wifi) but higher precedence than known networks with high cost. And third, infer network type based on limited name matching in Android if there is no network monitor or network monitor did not find the type. BUG=webrtc:5890 R=pthatcher@chromium.org, pthatcher@webrtc.org Review URL: https://codereview.webrtc.org/1976683003 . Cr-Commit-Position: refs/heads/master@{#12833}
This commit is contained in:
parent
f7f62902c2
commit
351d77b702
@ -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<void>(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*
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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<sockaddr_in6*>(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> 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);
|
||||
|
||||
@ -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<const Network*> SignalInactive;
|
||||
sigslot::signal1<const Network*> 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_; }
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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_; }
|
||||
|
||||
@ -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<uint16_t>(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 {
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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<TestPort> lport(
|
||||
CreateTestPort(kLocalAddr1, "lfrag", "lpass"));
|
||||
std::unique_ptr<TestPort> 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<char>(),
|
||||
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<TestPort> 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<TestPort> 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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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<AddressResolver> 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.
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user