Add network cost as part of the connection ranking.
For now, the network cost is purely based on the network type (cellular has cost 0xFFFF and everything else has cost 0). Add cost to the candidate signaling and the stun request signaling (which is needed for peer reflexive candidates). BUG=webrtc:4325 Review URL: https://codereview.webrtc.org/1668073002 Cr-Commit-Position: refs/heads/master@{#11642}
This commit is contained in:
parent
2c38c20e7b
commit
e1a0c942d6
@ -313,6 +313,7 @@ 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;
|
||||
}
|
||||
return iter->second;
|
||||
|
||||
@ -126,6 +126,7 @@ static const char kAttributeCandidateRport[] = "rport";
|
||||
static const char kAttributeCandidateUfrag[] = "ufrag";
|
||||
static const char kAttributeCandidatePwd[] = "pwd";
|
||||
static const char kAttributeCandidateGeneration[] = "generation";
|
||||
static const char kAttributeCandidateNetworkCost[] = "network-cost";
|
||||
static const char kAttributeFingerprint[] = "fingerprint";
|
||||
static const char kAttributeSetup[] = "setup";
|
||||
static const char kAttributeFmtp[] = "fmtp";
|
||||
@ -1062,6 +1063,7 @@ bool ParseCandidate(const std::string& message, Candidate* candidate,
|
||||
std::string username;
|
||||
std::string password;
|
||||
uint32_t generation = 0;
|
||||
uint32_t network_cost = 0;
|
||||
for (size_t i = current_position; i + 1 < fields.size(); ++i) {
|
||||
// RFC 5245
|
||||
// *(SP extension-att-name SP extension-att-value)
|
||||
@ -1073,6 +1075,10 @@ bool ParseCandidate(const std::string& message, Candidate* candidate,
|
||||
username = fields[++i];
|
||||
} else if (fields[i] == kAttributeCandidatePwd) {
|
||||
password = fields[++i];
|
||||
} else if (fields[i] == kAttributeCandidateNetworkCost) {
|
||||
if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Skip the unknown extension.
|
||||
++i;
|
||||
@ -1084,6 +1090,7 @@ bool ParseCandidate(const std::string& message, Candidate* candidate,
|
||||
generation, foundation);
|
||||
candidate->set_related_address(related_address);
|
||||
candidate->set_tcptype(tcptype);
|
||||
candidate->set_network_cost(std::min(network_cost, cricket::kMaxNetworkCost));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1758,6 +1765,9 @@ void BuildCandidate(const std::vector<Candidate>& candidates,
|
||||
if (include_ufrag && !it->username().empty()) {
|
||||
os << " " << kAttributeCandidateUfrag << " " << it->username();
|
||||
}
|
||||
if (it->network_cost() > 0) {
|
||||
os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
|
||||
}
|
||||
|
||||
AddLine(os.str(), message);
|
||||
}
|
||||
|
||||
@ -27,6 +27,8 @@
|
||||
|
||||
namespace cricket {
|
||||
|
||||
const uint32_t kMaxNetworkCost = 999;
|
||||
|
||||
// Candidate for ICE based connection discovery.
|
||||
|
||||
class Candidate {
|
||||
@ -138,6 +140,15 @@ class Candidate {
|
||||
ist >> generation_;
|
||||
}
|
||||
|
||||
// |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.
|
||||
void set_network_cost(uint32_t network_cost) {
|
||||
ASSERT(network_cost <= kMaxNetworkCost);
|
||||
network_cost_ = network_cost;
|
||||
}
|
||||
uint32_t network_cost() const { return network_cost_; }
|
||||
|
||||
const std::string& foundation() const {
|
||||
return foundation_;
|
||||
}
|
||||
@ -211,10 +222,10 @@ class Candidate {
|
||||
std::ostringstream ost;
|
||||
std::string address = sensitive ? address_.ToSensitiveString() :
|
||||
address_.ToString();
|
||||
ost << "Cand[" << foundation_ << ":" << component_ << ":"
|
||||
<< protocol_ << ":" << priority_ << ":"
|
||||
<< address << ":" << type_ << ":" << related_address_ << ":"
|
||||
<< username_ << ":" << password_ << "]";
|
||||
ost << "Cand[" << foundation_ << ":" << component_ << ":" << protocol_
|
||||
<< ":" << priority_ << ":" << address << ":" << type_ << ":"
|
||||
<< related_address_ << ":" << username_ << ":" << password_ << ":"
|
||||
<< network_cost_ << "]";
|
||||
return ost.str();
|
||||
}
|
||||
|
||||
@ -233,6 +244,7 @@ class Candidate {
|
||||
std::string foundation_;
|
||||
rtc::SocketAddress related_address_;
|
||||
std::string tcptype_;
|
||||
uint32_t network_cost_ = 0;
|
||||
};
|
||||
|
||||
// Used during parsing and writing to map component to channel name
|
||||
|
||||
@ -39,9 +39,20 @@ cricket::PortInterface::CandidateOrigin GetOrigin(cricket::PortInterface* port,
|
||||
return cricket::PortInterface::ORIGIN_OTHER_PORT;
|
||||
}
|
||||
|
||||
// Compares two connections based only on static information about them.
|
||||
// Compares two connections based only on the candidate and network information.
|
||||
// Returns positive if |a| is better than |b|.
|
||||
int CompareConnectionCandidates(cricket::Connection* a,
|
||||
cricket::Connection* b) {
|
||||
uint32_t a_cost = a->ComputeNetworkCost();
|
||||
uint32_t b_cost = b->ComputeNetworkCost();
|
||||
// Smaller cost is better.
|
||||
if (a_cost < b_cost) {
|
||||
return 1;
|
||||
}
|
||||
if (a_cost > b_cost) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Compare connection priority. Lower values get sorted last.
|
||||
if (a->priority() > b->priority())
|
||||
return 1;
|
||||
@ -552,8 +563,6 @@ void P2PTransportChannel::OnUnknownAddress(
|
||||
}
|
||||
} else {
|
||||
// Create a new candidate with this address.
|
||||
int remote_candidate_priority;
|
||||
|
||||
// The priority of the candidate is set to the PRIORITY attribute
|
||||
// from the request.
|
||||
const StunUInt32Attribute* priority_attr =
|
||||
@ -566,7 +575,11 @@ void P2PTransportChannel::OnUnknownAddress(
|
||||
STUN_ERROR_REASON_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
remote_candidate_priority = priority_attr->value();
|
||||
int remote_candidate_priority = priority_attr->value();
|
||||
|
||||
const StunUInt32Attribute* cost_attr =
|
||||
stun_msg->GetUInt32(STUN_ATTR_NETWORK_COST);
|
||||
uint32_t network_cost = (cost_attr) ? cost_attr->value() : 0;
|
||||
|
||||
// RFC 5245
|
||||
// If the source transport address of the request does not match any
|
||||
@ -581,8 +594,8 @@ void P2PTransportChannel::OnUnknownAddress(
|
||||
// from the foundation for all other remote candidates.
|
||||
remote_candidate.set_foundation(
|
||||
rtc::ToString<uint32_t>(rtc::ComputeCrc32(remote_candidate.id())));
|
||||
|
||||
remote_candidate.set_priority(remote_candidate_priority);
|
||||
remote_candidate.set_network_cost(network_cost);
|
||||
}
|
||||
|
||||
// RFC5245, the agent constructs a pair whose local candidate is equal to
|
||||
@ -1311,7 +1324,7 @@ void P2PTransportChannel::PingConnection(Connection* conn) {
|
||||
if (remote_ice_mode_ == ICEMODE_FULL && ice_role_ == ICEROLE_CONTROLLING) {
|
||||
use_candidate = (conn == best_connection_) || (best_connection_ == NULL) ||
|
||||
(!best_connection_->writable()) ||
|
||||
(conn->priority() > best_connection_->priority());
|
||||
(CompareConnectionCandidates(best_connection_, conn) < 0);
|
||||
} else if (remote_ice_mode_ == ICEMODE_LITE && conn == best_connection_) {
|
||||
use_candidate = best_connection_->writable();
|
||||
}
|
||||
|
||||
@ -368,6 +368,13 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
void AddAddress(int endpoint, const SocketAddress& addr) {
|
||||
GetEndpoint(endpoint)->network_manager_.AddInterface(addr);
|
||||
}
|
||||
void AddAddress(int endpoint,
|
||||
const SocketAddress& addr,
|
||||
const std::string& ifname,
|
||||
rtc::AdapterType adapter_type) {
|
||||
GetEndpoint(endpoint)->network_manager_.AddInterface(addr, ifname,
|
||||
adapter_type);
|
||||
}
|
||||
void RemoveAddress(int endpoint, const SocketAddress& addr) {
|
||||
GetEndpoint(endpoint)->network_manager_.RemoveInterface(addr);
|
||||
}
|
||||
@ -1650,6 +1657,68 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControllingSide) {
|
||||
DestroyChannels();
|
||||
}
|
||||
|
||||
// Tests that a Wifi-Wifi connection has the highest precedence.
|
||||
TEST_F(P2PTransportChannelMultihomedTest, TestPreferWifiToWifiConnection) {
|
||||
// The interface names are chosen so that |cellular| would have higher
|
||||
// candidate priority if it is not for the network type.
|
||||
auto& wifi = kAlternateAddrs;
|
||||
auto& cellular = kPublicAddrs;
|
||||
AddAddress(0, wifi[0], "test0", rtc::ADAPTER_TYPE_WIFI);
|
||||
AddAddress(0, cellular[0], "test1", rtc::ADAPTER_TYPE_CELLULAR);
|
||||
AddAddress(1, wifi[1], "test0", rtc::ADAPTER_TYPE_WIFI);
|
||||
AddAddress(1, cellular[1], "test1", rtc::ADAPTER_TYPE_CELLULAR);
|
||||
|
||||
// Use only local ports for simplicity.
|
||||
SetAllocatorFlags(0, kOnlyLocalPorts);
|
||||
SetAllocatorFlags(1, kOnlyLocalPorts);
|
||||
|
||||
// Create channels and let them go writable, as usual.
|
||||
CreateChannels(1);
|
||||
|
||||
EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
|
||||
ep2_ch1()->receiving() && ep2_ch1()->writable(),
|
||||
1000, 1000);
|
||||
// Need to wait to make sure the connections on both networks are writable.
|
||||
EXPECT_TRUE_WAIT(ep1_ch1()->best_connection() &&
|
||||
LocalCandidate(ep1_ch1())->address().EqualIPs(wifi[0]) &&
|
||||
RemoteCandidate(ep1_ch1())->address().EqualIPs(wifi[1]),
|
||||
1000);
|
||||
EXPECT_TRUE_WAIT(ep2_ch1()->best_connection() &&
|
||||
LocalCandidate(ep2_ch1())->address().EqualIPs(wifi[1]) &&
|
||||
RemoteCandidate(ep2_ch1())->address().EqualIPs(wifi[0]),
|
||||
1000);
|
||||
}
|
||||
|
||||
// Tests that a Wifi-Cellular connection has higher precedence than
|
||||
// a Cellular-Cellular connection.
|
||||
TEST_F(P2PTransportChannelMultihomedTest, TestPreferWifiOverCellularNetwork) {
|
||||
// The interface names are chosen so that |cellular| would have higher
|
||||
// candidate priority if it is not for the network type.
|
||||
auto& wifi = kAlternateAddrs;
|
||||
auto& cellular = kPublicAddrs;
|
||||
AddAddress(0, cellular[0], "test1", rtc::ADAPTER_TYPE_CELLULAR);
|
||||
AddAddress(1, wifi[1], "test0", rtc::ADAPTER_TYPE_WIFI);
|
||||
AddAddress(1, cellular[1], "test1", rtc::ADAPTER_TYPE_CELLULAR);
|
||||
|
||||
// Use only local ports for simplicity.
|
||||
SetAllocatorFlags(0, kOnlyLocalPorts);
|
||||
SetAllocatorFlags(1, kOnlyLocalPorts);
|
||||
|
||||
// Create channels and let them go writable, as usual.
|
||||
CreateChannels(1);
|
||||
|
||||
EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
|
||||
ep2_ch1()->receiving() && ep2_ch1()->writable(),
|
||||
1000, 1000);
|
||||
// Need to wait to make sure the connections on both networks are writable.
|
||||
EXPECT_TRUE_WAIT(ep1_ch1()->best_connection() &&
|
||||
RemoteCandidate(ep1_ch1())->address().EqualIPs(wifi[1]),
|
||||
1000);
|
||||
EXPECT_TRUE_WAIT(ep2_ch1()->best_connection() &&
|
||||
LocalCandidate(ep2_ch1())->address().EqualIPs(wifi[1]),
|
||||
1000);
|
||||
}
|
||||
|
||||
// Test that the backup connection is pinged at a rate no faster than
|
||||
// what was configured.
|
||||
TEST_F(P2PTransportChannelMultihomedTest, TestPingBackupConnectionRate) {
|
||||
|
||||
@ -195,6 +195,10 @@ void Port::Construct() {
|
||||
ice_username_fragment_ = rtc::CreateRandomString(ICE_UFRAG_LENGTH);
|
||||
password_ = rtc::CreateRandomString(ICE_PWD_LENGTH);
|
||||
}
|
||||
// TODO(honghaiz): Make it configurable from user setting.
|
||||
network_cost_ =
|
||||
(network_->type() == rtc::ADAPTER_TYPE_CELLULAR) ? kMaxNetworkCost : 0;
|
||||
|
||||
LOG_J(LS_INFO, this) << "Port created";
|
||||
}
|
||||
|
||||
@ -250,6 +254,7 @@ void Port::AddAddress(const rtc::SocketAddress& address,
|
||||
c.set_password(password_);
|
||||
c.set_network_name(network_->name());
|
||||
c.set_network_type(network_->type());
|
||||
c.set_network_cost(network_cost_);
|
||||
c.set_generation(generation_);
|
||||
c.set_related_address(related_address);
|
||||
c.set_foundation(
|
||||
@ -692,6 +697,11 @@ class ConnectionRequest : public StunRequest {
|
||||
static_cast<uint32_t>(connection_->pings_since_last_response_.size() -
|
||||
1)));
|
||||
}
|
||||
uint32_t network_cost = connection_->port()->network_cost();
|
||||
if (network_cost > 0) {
|
||||
request->AddAttribute(
|
||||
new StunUInt32Attribute(STUN_ATTR_NETWORK_COST, network_cost));
|
||||
}
|
||||
|
||||
// Adding ICE_CONTROLLED or ICE_CONTROLLING attribute based on the role.
|
||||
if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLING) {
|
||||
@ -1151,6 +1161,11 @@ std::string Connection::ToDebugId() const {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
std::string Connection::ToString() const {
|
||||
const char CONNECT_STATE_ABBREV[2] = {
|
||||
'-', // not connected (false)
|
||||
@ -1389,6 +1404,7 @@ void Connection::MaybeAddPrflxCandidate(ConnectionRequest* request,
|
||||
new_local_candidate.set_password(local_candidate().password());
|
||||
new_local_candidate.set_network_name(local_candidate().network_name());
|
||||
new_local_candidate.set_network_type(local_candidate().network_type());
|
||||
new_local_candidate.set_network_cost(local_candidate().network_cost());
|
||||
new_local_candidate.set_related_address(local_candidate().address());
|
||||
new_local_candidate.set_foundation(ComputeFoundation(
|
||||
PRFLX_PORT_TYPE, local_candidate().protocol(),
|
||||
|
||||
@ -296,6 +296,7 @@ class Port : public PortInterface, public rtc::MessageHandler,
|
||||
void set_candidate_filter(uint32_t candidate_filter) {
|
||||
candidate_filter_ = candidate_filter;
|
||||
}
|
||||
int32_t network_cost() const { return network_cost_; }
|
||||
|
||||
protected:
|
||||
enum {
|
||||
@ -395,6 +396,11 @@ class Port : public PortInterface, public rtc::MessageHandler,
|
||||
// TurnPort will hide raddr to avoid local address leakage.
|
||||
uint32_t candidate_filter_;
|
||||
|
||||
// A virtual cost perceived by the user, usually based on the network type
|
||||
// (WiFi. vs. Cellular). It takes precedence over the priority when
|
||||
// comparing two connections.
|
||||
uint32_t network_cost_;
|
||||
|
||||
friend class Connection;
|
||||
};
|
||||
|
||||
@ -560,6 +566,8 @@ class Connection : public rtc::MessageHandler,
|
||||
|
||||
IceMode remote_ice_mode() const { return remote_ice_mode_; }
|
||||
|
||||
uint32_t ComputeNetworkCost() const;
|
||||
|
||||
// Update the ICE password of the remote candidate if |ice_ufrag| matches
|
||||
// the candidate's ufrag, and the candidate's passwrod has not been set.
|
||||
void MaybeSetRemoteIceCredentials(const std::string& ice_ufrag,
|
||||
|
||||
@ -602,10 +602,11 @@ class TurnMessage : public StunMessage {
|
||||
|
||||
// RFC 5245 ICE STUN attributes.
|
||||
enum IceAttributeType {
|
||||
STUN_ATTR_PRIORITY = 0x0024, // UInt32
|
||||
STUN_ATTR_USE_CANDIDATE = 0x0025, // No content, Length = 0
|
||||
STUN_ATTR_ICE_CONTROLLED = 0x8029, // UInt64
|
||||
STUN_ATTR_ICE_CONTROLLING = 0x802A // UInt64
|
||||
STUN_ATTR_PRIORITY = 0x0024, // UInt32
|
||||
STUN_ATTR_USE_CANDIDATE = 0x0025, // No content, Length = 0
|
||||
STUN_ATTR_ICE_CONTROLLED = 0x8029, // UInt64
|
||||
STUN_ATTR_ICE_CONTROLLING = 0x802A, // UInt64
|
||||
STUN_ATTR_NETWORK_COST = 0xC057 // UInt32
|
||||
};
|
||||
|
||||
// RFC 5245-defined errors.
|
||||
@ -619,7 +620,9 @@ class IceMessage : public StunMessage {
|
||||
protected:
|
||||
virtual StunAttributeValueType GetAttributeValueType(int type) const {
|
||||
switch (type) {
|
||||
case STUN_ATTR_PRIORITY: return STUN_VALUE_UINT32;
|
||||
case STUN_ATTR_PRIORITY:
|
||||
case STUN_ATTR_NETWORK_COST:
|
||||
return STUN_VALUE_UINT32;
|
||||
case STUN_ATTR_USE_CANDIDATE: return STUN_VALUE_BYTE_STRING;
|
||||
case STUN_ATTR_ICE_CONTROLLED: return STUN_VALUE_UINT64;
|
||||
case STUN_ATTR_ICE_CONTROLLING: return STUN_VALUE_UINT64;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user