Add 16-bit network id to the candidate signaling.

Also include that in the stun-ping request as part of the
network-info attribute.
Change the network cost to be 16 bits.

BUG=

Review URL: https://codereview.webrtc.org/1815473002

Cr-Commit-Position: refs/heads/master@{#12110}
This commit is contained in:
honghaiz 2016-03-23 16:07:48 -07:00 committed by Commit bot
parent 887a19b9d2
commit a0c44eaa82
12 changed files with 167 additions and 46 deletions

View File

@ -127,6 +127,7 @@ static const char kAttributeCandidateRport[] = "rport";
static const char kAttributeCandidateUfrag[] = "ufrag"; static const char kAttributeCandidateUfrag[] = "ufrag";
static const char kAttributeCandidatePwd[] = "pwd"; static const char kAttributeCandidatePwd[] = "pwd";
static const char kAttributeCandidateGeneration[] = "generation"; static const char kAttributeCandidateGeneration[] = "generation";
static const char kAttributeCandidateNetworkId[] = "network-id";
static const char kAttributeCandidateNetworkCost[] = "network-cost"; static const char kAttributeCandidateNetworkCost[] = "network-cost";
static const char kAttributeFingerprint[] = "fingerprint"; static const char kAttributeFingerprint[] = "fingerprint";
static const char kAttributeSetup[] = "setup"; static const char kAttributeSetup[] = "setup";
@ -1092,7 +1093,8 @@ bool ParseCandidate(const std::string& message, Candidate* candidate,
std::string username; std::string username;
std::string password; std::string password;
uint32_t generation = 0; uint32_t generation = 0;
uint32_t network_cost = 0; uint16_t network_id = 0;
uint16_t network_cost = 0;
for (size_t i = current_position; i + 1 < fields.size(); ++i) { for (size_t i = current_position; i + 1 < fields.size(); ++i) {
// RFC 5245 // RFC 5245
// *(SP extension-att-name SP extension-att-value) // *(SP extension-att-name SP extension-att-value)
@ -1104,10 +1106,15 @@ bool ParseCandidate(const std::string& message, Candidate* candidate,
username = fields[++i]; username = fields[++i];
} else if (fields[i] == kAttributeCandidatePwd) { } else if (fields[i] == kAttributeCandidatePwd) {
password = fields[++i]; password = fields[++i];
} else if (fields[i] == kAttributeCandidateNetworkId) {
if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
return false;
}
} else if (fields[i] == kAttributeCandidateNetworkCost) { } else if (fields[i] == kAttributeCandidateNetworkCost) {
if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) { if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
return false; return false;
} }
network_cost = std::min(network_cost, cricket::kMaxNetworkCost);
} else { } else {
// Skip the unknown extension. // Skip the unknown extension.
++i; ++i;
@ -1116,10 +1123,9 @@ bool ParseCandidate(const std::string& message, Candidate* candidate,
*candidate = Candidate(component_id, cricket::ProtoToString(protocol), *candidate = Candidate(component_id, cricket::ProtoToString(protocol),
address, priority, username, password, candidate_type, address, priority, username, password, candidate_type,
generation, foundation); generation, foundation, network_id, network_cost);
candidate->set_related_address(related_address); candidate->set_related_address(related_address);
candidate->set_tcptype(tcptype); candidate->set_tcptype(tcptype);
candidate->set_network_cost(std::min(network_cost, cricket::kMaxNetworkCost));
return true; return true;
} }
@ -1814,6 +1820,9 @@ void BuildCandidate(const std::vector<Candidate>& candidates,
if (include_ufrag && !it->username().empty()) { if (include_ufrag && !it->username().empty()) {
os << " " << kAttributeCandidateUfrag << " " << it->username(); os << " " << kAttributeCandidateUfrag << " " << it->username();
} }
if (it->network_id() > 0) {
os << " " << kAttributeCandidateNetworkId << " " << it->network_id();
}
if (it->network_cost() > 0) { if (it->network_cost() > 0) {
os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost(); os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
} }

View File

@ -2058,6 +2058,19 @@ TEST_F(WebRtcSdpTest, SerializeCandidates) {
candidate_with_ufrag)); candidate_with_ufrag));
message = webrtc::SdpSerializeCandidate(*jcandidate_); message = webrtc::SdpSerializeCandidate(*jcandidate_);
EXPECT_EQ(std::string(kRawCandidate) + " ufrag ABC", message); EXPECT_EQ(std::string(kRawCandidate) + " ufrag ABC", message);
Candidate candidate_with_network_info(candidates_.front());
candidate_with_network_info.set_network_id(1);
jcandidate_.reset(new JsepIceCandidate(std::string("audio"), 0,
candidate_with_network_info));
message = webrtc::SdpSerializeCandidate(*jcandidate_);
EXPECT_EQ(std::string(kRawCandidate) + " network-id 1", message);
candidate_with_network_info.set_network_cost(999);
jcandidate_.reset(new JsepIceCandidate(std::string("audio"), 0,
candidate_with_network_info));
message = webrtc::SdpSerializeCandidate(*jcandidate_);
EXPECT_EQ(std::string(kRawCandidate) + " network-id 1 network-cost 999",
message);
} }
// TODO(mallinath) : Enable this test once WebRTCSdp capable of parsing // TODO(mallinath) : Enable this test once WebRTCSdp capable of parsing
@ -2325,6 +2338,7 @@ TEST_F(WebRtcSdpTest, DeserializeCandidate) {
EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate())); EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
EXPECT_EQ(0, jcandidate.candidate().network_cost());
// Candidate line without generation extension. // Candidate line without generation extension.
sdp = kSdpOneCandidate; sdp = kSdpOneCandidate;
@ -2336,6 +2350,22 @@ TEST_F(WebRtcSdpTest, DeserializeCandidate) {
expected.set_generation(0); expected.set_generation(0);
EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected)); EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
// Candidate with network id and/or cost.
sdp = kSdpOneCandidate;
Replace(" generation 2", " generation 2 network-id 2", &sdp);
EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
expected = jcandidate_->candidate();
expected.set_network_id(2);
EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
EXPECT_EQ(0, jcandidate.candidate().network_cost());
// Add network cost
Replace(" network-id 2", " network-id 2 network-cost 9", &sdp);
EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
EXPECT_EQ(9, jcandidate.candidate().network_cost());
sdp = kSdpTcpActiveCandidate; sdp = kSdpTcpActiveCandidate;
EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate)); EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
// Make a cricket::Candidate equivalent to kSdpTcpCandidate string. // Make a cricket::Candidate equivalent to kSdpTcpCandidate string.

View File

@ -286,6 +286,7 @@ void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks,
// This network is new. Place it in the network map. // This network is new. Place it in the network map.
merged_list.push_back(net); merged_list.push_back(net);
networks_map_[key] = net; networks_map_[key] = net;
net->set_id(next_available_network_id_++);
// Also, we might have accumulated IPAddresses from the first // Also, we might have accumulated IPAddresses from the first
// step, set it here. // step, set it here.
net->SetIPs(kv.second.ips, true); net->SetIPs(kv.second.ips, true);

View File

@ -173,6 +173,11 @@ class NetworkManagerBase : public NetworkManager {
IPAddress default_local_ipv4_address_; IPAddress default_local_ipv4_address_;
IPAddress default_local_ipv6_address_; IPAddress default_local_ipv6_address_;
// We use 16 bits to save the bandwidth consumption when sending the network
// id over the Internet. It is OK that the 16-bit integer overflows to get a
// network id 0 because we only compare the network ids in the old and the new
// best connections in the transport channel.
uint16_t next_available_network_id_ = 1;
}; };
// Basic implementation of the NetworkManager interface that gets list // Basic implementation of the NetworkManager interface that gets list
@ -339,6 +344,11 @@ class Network {
AdapterType type() const { return type_; } AdapterType type() const { return type_; }
void set_type(AdapterType type) { type_ = type; } void set_type(AdapterType type) { type_ = type; }
// 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_; }
void set_id(uint16_t id) { id_ = id; }
int preference() const { return preference_; } int preference() const { return preference_; }
void set_preference(int preference) { preference_ = preference; } void set_preference(int preference) { preference_ = preference; }
@ -372,6 +382,7 @@ class Network {
AdapterType type_; AdapterType type_;
int preference_; int preference_;
bool active_ = true; bool active_ = true;
uint16_t id_ = 0;
friend class NetworkManager; friend class NetworkManager;
}; };

View File

@ -299,6 +299,8 @@ TEST_F(NetworkTest, TestBasicMergeNetworkList) {
EXPECT_EQ(1U, list.size()); EXPECT_EQ(1U, list.size());
EXPECT_EQ(ipv4_network1.ToString(), list[0]->ToString()); EXPECT_EQ(ipv4_network1.ToString(), list[0]->ToString());
Network* net1 = list[0]; Network* net1 = list[0];
uint16_t net_id1 = net1->id();
EXPECT_EQ(1, net_id1);
list.clear(); list.clear();
// Replace ipv4_network1 with ipv4_network2. // Replace ipv4_network1 with ipv4_network2.
@ -315,6 +317,9 @@ TEST_F(NetworkTest, TestBasicMergeNetworkList) {
EXPECT_EQ(1U, list.size()); EXPECT_EQ(1U, list.size());
EXPECT_EQ(ipv4_network2.ToString(), list[0]->ToString()); EXPECT_EQ(ipv4_network2.ToString(), list[0]->ToString());
Network* net2 = list[0]; Network* net2 = list[0];
uint16_t net_id2 = net2->id();
// Network id will increase.
EXPECT_LT(net_id1, net_id2);
list.clear(); list.clear();
// Add Network2 back. // Add Network2 back.
@ -332,6 +337,8 @@ TEST_F(NetworkTest, TestBasicMergeNetworkList) {
EXPECT_EQ(2U, list.size()); EXPECT_EQ(2U, list.size());
EXPECT_TRUE((net1 == list[0] && net2 == list[1]) || EXPECT_TRUE((net1 == list[0] && net2 == list[1]) ||
(net1 == list[1] && net2 == list[0])); (net1 == list[1] && net2 == list[0]));
EXPECT_TRUE((net_id1 == list[0]->id() && net_id2 == list[1]->id()) ||
(net_id1 == list[1]->id() && net_id2 == list[0]->id()));
list.clear(); list.clear();
// Call MergeNetworkList() again and verify that we don't get update // Call MergeNetworkList() again and verify that we don't get update
@ -350,6 +357,8 @@ TEST_F(NetworkTest, TestBasicMergeNetworkList) {
EXPECT_EQ(2U, list.size()); EXPECT_EQ(2U, list.size());
EXPECT_TRUE((net1 == list[0] && net2 == list[1]) || EXPECT_TRUE((net1 == list[0] && net2 == list[1]) ||
(net1 == list[1] && net2 == list[0])); (net1 == list[1] && net2 == list[0]));
EXPECT_TRUE((net_id1 == list[0]->id() && net_id2 == list[1]->id()) ||
(net_id1 == list[1]->id() && net_id2 == list[0]->id()));
list.clear(); list.clear();
} }

View File

@ -46,6 +46,7 @@ class NetworkBinderInterface {
// This is needed because some operating systems (like Android) require a // This is needed because some operating systems (like Android) require a
// special bind call to put packets on a non-default network interface. // special bind call to put packets on a non-default network interface.
virtual int BindSocketToNetwork(int socket_fd, const IPAddress& address) = 0; virtual int BindSocketToNetwork(int socket_fd, const IPAddress& address) = 0;
virtual ~NetworkBinderInterface() {}
}; };
/* /*

View File

@ -27,7 +27,7 @@
namespace cricket { namespace cricket {
const uint32_t kMaxNetworkCost = 999; const uint16_t kMaxNetworkCost = 999;
// Candidate for ICE based connection discovery. // Candidate for ICE based connection discovery.
@ -40,7 +40,9 @@ class Candidate {
component_(0), component_(0),
priority_(0), priority_(0),
network_type_(rtc::ADAPTER_TYPE_UNKNOWN), network_type_(rtc::ADAPTER_TYPE_UNKNOWN),
generation_(0) {} generation_(0),
network_id_(0),
network_cost_(0) {}
Candidate(int component, Candidate(int component,
const std::string& protocol, const std::string& protocol,
@ -50,7 +52,9 @@ class Candidate {
const std::string& password, const std::string& password,
const std::string& type, const std::string& type,
uint32_t generation, uint32_t generation,
const std::string& foundation) const std::string& foundation,
uint16_t network_id = 0,
uint16_t network_cost = 0)
: id_(rtc::CreateRandomString(8)), : id_(rtc::CreateRandomString(8)),
component_(component), component_(component),
protocol_(protocol), protocol_(protocol),
@ -61,7 +65,9 @@ class Candidate {
type_(type), type_(type),
network_type_(rtc::ADAPTER_TYPE_UNKNOWN), network_type_(rtc::ADAPTER_TYPE_UNKNOWN),
generation_(generation), generation_(generation),
foundation_(foundation) {} foundation_(foundation),
network_id_(network_id),
network_cost_(network_cost) {}
const std::string & id() const { return id_; } const std::string & id() const { return id_; }
void set_id(const std::string & id) { id_ = id; } void set_id(const std::string & id) { id_ = id; }
@ -143,11 +149,15 @@ class Candidate {
// |network_cost| measures the cost/penalty of using this candidate. A network // |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 // cost of 0 indicates this candidate can be used freely. A value of
// |kMaxNetworkCost| indicates it should be used only as the last resort. // |kMaxNetworkCost| indicates it should be used only as the last resort.
void set_network_cost(uint32_t network_cost) { void set_network_cost(uint16_t network_cost) {
ASSERT(network_cost <= kMaxNetworkCost); ASSERT(network_cost <= kMaxNetworkCost);
network_cost_ = network_cost; network_cost_ = network_cost;
} }
uint32_t network_cost() const { return network_cost_; } uint16_t network_cost() const { return network_cost_; }
// An ID assigned to the network hosting the candidate.
uint16_t network_id() const { return network_id_; }
void set_network_id(uint16_t network_id) { network_id_ = network_id; }
const std::string& foundation() const { const std::string& foundation() const {
return foundation_; return foundation_;
@ -178,13 +188,14 @@ class Candidate {
// Determines whether this candidate is equivalent to the given one. // Determines whether this candidate is equivalent to the given one.
bool IsEquivalent(const Candidate& c) const { bool IsEquivalent(const Candidate& c) const {
// We ignore the network name, since that is just debug information, and // We ignore the network name, since that is just debug information, and
// the priority, since that should be the same if the rest is (and it's // the priority and the network cost, since they should be the same if the
// a float so equality checking is always worrisome). // rest are.
return (component_ == c.component_) && (protocol_ == c.protocol_) && return (component_ == c.component_) && (protocol_ == c.protocol_) &&
(address_ == c.address_) && (username_ == c.username_) && (address_ == c.address_) && (username_ == c.username_) &&
(password_ == c.password_) && (type_ == c.type_) && (password_ == c.password_) && (type_ == c.type_) &&
(generation_ == c.generation_) && (foundation_ == c.foundation_) && (generation_ == c.generation_) && (foundation_ == c.foundation_) &&
(related_address_ == c.related_address_); (related_address_ == c.related_address_) &&
(network_id_ == c.network_id_);
} }
// Determines whether this candidate can be considered equivalent to the // Determines whether this candidate can be considered equivalent to the
@ -238,7 +249,7 @@ class Candidate {
ost << "Cand[" << transport_name_ << ":" << foundation_ << ":" << component_ ost << "Cand[" << transport_name_ << ":" << foundation_ << ":" << component_
<< ":" << protocol_ << ":" << priority_ << ":" << address << ":" << ":" << protocol_ << ":" << priority_ << ":" << address << ":"
<< type_ << ":" << related_address_ << ":" << username_ << ":" << type_ << ":" << related_address_ << ":" << username_ << ":"
<< password_ << ":" << network_cost_ << "]"; << password_ << ":" << network_id_ << ":" << network_cost_ << "]";
return ost.str(); return ost.str();
} }
@ -257,8 +268,9 @@ class Candidate {
std::string foundation_; std::string foundation_;
rtc::SocketAddress related_address_; rtc::SocketAddress related_address_;
std::string tcptype_; std::string tcptype_;
uint32_t network_cost_ = 0;
std::string transport_name_; std::string transport_name_;
uint16_t network_id_;
uint16_t network_cost_;
}; };
// Used during parsing and writing to map component to channel name // Used during parsing and writing to map component to channel name

View File

@ -614,25 +614,30 @@ void P2PTransportChannel::OnUnknownAddress(
} }
int remote_candidate_priority = priority_attr->value(); int remote_candidate_priority = priority_attr->value();
const StunUInt32Attribute* cost_attr = uint16_t network_id = 0;
stun_msg->GetUInt32(STUN_ATTR_NETWORK_COST); uint16_t network_cost = 0;
uint32_t network_cost = (cost_attr) ? cost_attr->value() : 0; const StunUInt32Attribute* network_attr =
stun_msg->GetUInt32(STUN_ATTR_NETWORK_INFO);
if (network_attr) {
uint32_t network_info = network_attr->value();
network_id = static_cast<uint16_t>(network_info >> 16);
network_cost = static_cast<uint16_t>(network_info);
}
// RFC 5245 // RFC 5245
// If the source transport address of the request does not match any // If the source transport address of the request does not match any
// existing remote candidates, it represents a new peer reflexive remote // existing remote candidates, it represents a new peer reflexive remote
// candidate. // candidate.
remote_candidate = Candidate(component(), ProtoToString(proto), address, 0, remote_candidate = Candidate(
remote_username, remote_password, component(), ProtoToString(proto), address, remote_candidate_priority,
PRFLX_PORT_TYPE, remote_generation, ""); remote_username, remote_password, PRFLX_PORT_TYPE, remote_generation,
"", network_id, network_cost);
// From RFC 5245, section-7.2.1.3: // From RFC 5245, section-7.2.1.3:
// The foundation of the candidate is set to an arbitrary value, different // The foundation of the candidate is set to an arbitrary value, different
// from the foundation for all other remote candidates. // from the foundation for all other remote candidates.
remote_candidate.set_foundation( remote_candidate.set_foundation(
rtc::ToString<uint32_t>(rtc::ComputeCrc32(remote_candidate.id()))); 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 // RFC5245, the agent constructs a pair whose local candidate is equal to

View File

@ -242,25 +242,17 @@ void Port::AddAddress(const rtc::SocketAddress& address,
ASSERT(!tcptype.empty()); ASSERT(!tcptype.empty());
} }
Candidate c; std::string foundation =
c.set_id(rtc::CreateRandomString(8)); ComputeFoundation(type, protocol, relay_protocol, base_address);
c.set_component(component_); Candidate c(component_, protocol, address, 0U, username_fragment(), password_,
c.set_type(type); type, generation_, foundation, network_->id(), network_cost_);
c.set_protocol(protocol); c.set_priority(
c.GetPriority(type_preference, network_->preference(), relay_preference));
c.set_relay_protocol(relay_protocol); c.set_relay_protocol(relay_protocol);
c.set_tcptype(tcptype); c.set_tcptype(tcptype);
c.set_address(address);
c.set_priority(c.GetPriority(type_preference, network_->preference(),
relay_preference));
c.set_username(username_fragment());
c.set_password(password_);
c.set_network_name(network_->name()); c.set_network_name(network_->name());
c.set_network_type(network_->type()); c.set_network_type(network_->type());
c.set_network_cost(network_cost_);
c.set_generation(generation_);
c.set_related_address(related_address); c.set_related_address(related_address);
c.set_foundation(
ComputeFoundation(type, protocol, relay_protocol, base_address));
candidates_.push_back(c); candidates_.push_back(c);
SignalCandidateReady(this, c); SignalCandidateReady(this, c);
@ -705,11 +697,10 @@ class ConnectionRequest : public StunRequest {
static_cast<uint32_t>(connection_->pings_since_last_response_.size() - static_cast<uint32_t>(connection_->pings_since_last_response_.size() -
1))); 1)));
} }
uint32_t network_cost = connection_->port()->network_cost(); uint32_t network_info = connection_->port()->Network()->id();
if (network_cost > 0) { network_info = (network_info << 16) | connection_->port()->network_cost();
request->AddAttribute( request->AddAttribute(
new StunUInt32Attribute(STUN_ATTR_NETWORK_COST, network_cost)); new StunUInt32Attribute(STUN_ATTR_NETWORK_INFO, network_info));
}
// Adding ICE_CONTROLLED or ICE_CONTROLLING attribute based on the role. // Adding ICE_CONTROLLED or ICE_CONTROLLING attribute based on the role.
if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLING) { if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLING) {
@ -1412,11 +1403,12 @@ void Connection::MaybeAddPrflxCandidate(ConnectionRequest* request,
new_local_candidate.set_password(local_candidate().password()); new_local_candidate.set_password(local_candidate().password());
new_local_candidate.set_network_name(local_candidate().network_name()); 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_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_related_address(local_candidate().address());
new_local_candidate.set_foundation(ComputeFoundation( new_local_candidate.set_foundation(ComputeFoundation(
PRFLX_PORT_TYPE, local_candidate().protocol(), PRFLX_PORT_TYPE, local_candidate().protocol(),
local_candidate().relay_protocol(), local_candidate().address())); local_candidate().relay_protocol(), local_candidate().address()));
new_local_candidate.set_network_id(local_candidate().network_id());
new_local_candidate.set_network_cost(local_candidate().network_cost());
// Change the local candidate of this Connection to the new prflx candidate. // Change the local candidate of this Connection to the new prflx candidate.
local_candidate_index_ = port_->AddPrflxCandidate(new_local_candidate); local_candidate_index_ = port_->AddPrflxCandidate(new_local_candidate);

View File

@ -296,7 +296,7 @@ class Port : public PortInterface, public rtc::MessageHandler,
void set_candidate_filter(uint32_t candidate_filter) { void set_candidate_filter(uint32_t candidate_filter) {
candidate_filter_ = candidate_filter; candidate_filter_ = candidate_filter;
} }
int32_t network_cost() const { return network_cost_; } int16_t network_cost() const { return network_cost_; }
protected: protected:
enum { enum {
@ -403,7 +403,7 @@ class Port : public PortInterface, public rtc::MessageHandler,
// A virtual cost perceived by the user, usually based on the network type // A virtual cost perceived by the user, usually based on the network type
// (WiFi. vs. Cellular). It takes precedence over the priority when // (WiFi. vs. Cellular). It takes precedence over the priority when
// comparing two connections. // comparing two connections.
uint32_t network_cost_; uint16_t network_cost_;
friend class Connection; friend class Connection;
}; };

View File

@ -559,6 +559,10 @@ class PortTest : public testing::Test, public sigslot::has_slots<> {
} }
} }
void SetNetworkType(rtc::AdapterType adapter_type) {
network_.set_type(adapter_type);
}
void TestCrossFamilyPorts(int type); void TestCrossFamilyPorts(int type);
void ExpectPortsCanConnect(bool can_connect, Port* p1, Port* p2); void ExpectPortsCanConnect(bool can_connect, Port* p1, Port* p2);
@ -1757,6 +1761,51 @@ TEST_F(PortTest, TestUseCandidateAttribute) {
ASSERT_TRUE(use_candidate_attr != NULL); ASSERT_TRUE(use_candidate_attr != NULL);
} }
TEST_F(PortTest, TestNetworkInfoAttribute) {
rtc::scoped_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);
rtc::scoped_ptr<TestPort> rport(
CreateTestPort(kLocalAddr2, "rfrag", "rpass"));
lport->SetIceRole(cricket::ICEROLE_CONTROLLING);
lport->SetIceTiebreaker(kTiebreaker1);
rport->SetIceRole(cricket::ICEROLE_CONTROLLED);
rport->SetIceTiebreaker(kTiebreaker2);
uint16_t lnetwork_id = 9;
lport->Network()->set_id(lnetwork_id);
// Send a fake ping from lport to rport.
lport->PrepareAddress();
rport->PrepareAddress();
Connection* lconn =
lport->CreateConnection(rport->Candidates()[0], Port::ORIGIN_MESSAGE);
lconn->Ping(0);
ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, 1000);
IceMessage* msg = lport->last_stun_msg();
const StunUInt32Attribute* network_info_attr =
msg->GetUInt32(STUN_ATTR_NETWORK_INFO);
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);
// Send a fake ping from rport to lport.
uint16_t rnetwork_id = 8;
rport->Network()->set_id(rnetwork_id);
Connection* rconn =
rport->CreateConnection(lport->Candidates()[0], Port::ORIGIN_MESSAGE);
rconn->Ping(0);
ASSERT_TRUE_WAIT(rport->last_stun_msg() != NULL, 1000);
msg = rport->last_stun_msg();
network_info_attr = msg->GetUInt32(STUN_ATTR_NETWORK_INFO);
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);
}
// Test handling STUN messages. // Test handling STUN messages.
TEST_F(PortTest, TestHandleStunMessage) { TEST_F(PortTest, TestHandleStunMessage) {
// Our port will act as the "remote" port. // Our port will act as the "remote" port.

View File

@ -606,7 +606,9 @@ enum IceAttributeType {
STUN_ATTR_USE_CANDIDATE = 0x0025, // No content, Length = 0 STUN_ATTR_USE_CANDIDATE = 0x0025, // No content, Length = 0
STUN_ATTR_ICE_CONTROLLED = 0x8029, // UInt64 STUN_ATTR_ICE_CONTROLLED = 0x8029, // UInt64
STUN_ATTR_ICE_CONTROLLING = 0x802A, // UInt64 STUN_ATTR_ICE_CONTROLLING = 0x802A, // UInt64
STUN_ATTR_NETWORK_COST = 0xC057 // UInt32 // UInt32. The higher 16 bits are the network ID. The lower 16 bits are the
// network cost.
STUN_ATTR_NETWORK_INFO = 0xC057
}; };
// RFC 5245-defined errors. // RFC 5245-defined errors.
@ -621,7 +623,7 @@ class IceMessage : public StunMessage {
virtual StunAttributeValueType GetAttributeValueType(int type) const { virtual StunAttributeValueType GetAttributeValueType(int type) const {
switch (type) { switch (type) {
case STUN_ATTR_PRIORITY: case STUN_ATTR_PRIORITY:
case STUN_ATTR_NETWORK_COST: case STUN_ATTR_NETWORK_INFO:
return STUN_VALUE_UINT32; return STUN_VALUE_UINT32;
case STUN_ATTR_USE_CANDIDATE: return STUN_VALUE_BYTE_STRING; case STUN_ATTR_USE_CANDIDATE: return STUN_VALUE_BYTE_STRING;
case STUN_ATTR_ICE_CONTROLLED: return STUN_VALUE_UINT64; case STUN_ATTR_ICE_CONTROLLED: return STUN_VALUE_UINT64;