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}
diff --git a/webrtc/api/webrtcsdp.cc b/webrtc/api/webrtcsdp.cc
index 4a296d9..93d4acc 100644
--- a/webrtc/api/webrtcsdp.cc
+++ b/webrtc/api/webrtcsdp.cc
@@ -127,6 +127,7 @@
static const char kAttributeCandidateUfrag[] = "ufrag";
static const char kAttributeCandidatePwd[] = "pwd";
static const char kAttributeCandidateGeneration[] = "generation";
+static const char kAttributeCandidateNetworkId[] = "network-id";
static const char kAttributeCandidateNetworkCost[] = "network-cost";
static const char kAttributeFingerprint[] = "fingerprint";
static const char kAttributeSetup[] = "setup";
@@ -1092,7 +1093,8 @@
std::string username;
std::string password;
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) {
// RFC 5245
// *(SP extension-att-name SP extension-att-value)
@@ -1104,10 +1106,15 @@
username = fields[++i];
} else if (fields[i] == kAttributeCandidatePwd) {
password = fields[++i];
+ } else if (fields[i] == kAttributeCandidateNetworkId) {
+ if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
+ return false;
+ }
} else if (fields[i] == kAttributeCandidateNetworkCost) {
if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
return false;
}
+ network_cost = std::min(network_cost, cricket::kMaxNetworkCost);
} else {
// Skip the unknown extension.
++i;
@@ -1116,10 +1123,9 @@
*candidate = Candidate(component_id, cricket::ProtoToString(protocol),
address, priority, username, password, candidate_type,
- generation, foundation);
+ generation, foundation, network_id, network_cost);
candidate->set_related_address(related_address);
candidate->set_tcptype(tcptype);
- candidate->set_network_cost(std::min(network_cost, cricket::kMaxNetworkCost));
return true;
}
@@ -1814,6 +1820,9 @@
if (include_ufrag && !it->username().empty()) {
os << " " << kAttributeCandidateUfrag << " " << it->username();
}
+ if (it->network_id() > 0) {
+ os << " " << kAttributeCandidateNetworkId << " " << it->network_id();
+ }
if (it->network_cost() > 0) {
os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
}
diff --git a/webrtc/api/webrtcsdp_unittest.cc b/webrtc/api/webrtcsdp_unittest.cc
index 9559701..c527204 100644
--- a/webrtc/api/webrtcsdp_unittest.cc
+++ b/webrtc/api/webrtcsdp_unittest.cc
@@ -2058,6 +2058,19 @@
candidate_with_ufrag));
message = webrtc::SdpSerializeCandidate(*jcandidate_);
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
@@ -2325,6 +2338,7 @@
EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
+ EXPECT_EQ(0, jcandidate.candidate().network_cost());
// Candidate line without generation extension.
sdp = kSdpOneCandidate;
@@ -2336,6 +2350,22 @@
expected.set_generation(0);
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;
EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
// Make a cricket::Candidate equivalent to kSdpTcpCandidate string.
diff --git a/webrtc/base/network.cc b/webrtc/base/network.cc
index 4f3b919..4780e2f 100644
--- a/webrtc/base/network.cc
+++ b/webrtc/base/network.cc
@@ -286,6 +286,7 @@
// This network is new. Place it in the network map.
merged_list.push_back(net);
networks_map_[key] = net;
+ net->set_id(next_available_network_id_++);
// Also, we might have accumulated IPAddresses from the first
// step, set it here.
net->SetIPs(kv.second.ips, true);
diff --git a/webrtc/base/network.h b/webrtc/base/network.h
index 680c005..ee22d5e 100644
--- a/webrtc/base/network.h
+++ b/webrtc/base/network.h
@@ -173,6 +173,11 @@
IPAddress default_local_ipv4_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
@@ -339,6 +344,11 @@
AdapterType type() const { return 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_; }
void set_preference(int preference) { preference_ = preference; }
@@ -372,6 +382,7 @@
AdapterType type_;
int preference_;
bool active_ = true;
+ uint16_t id_ = 0;
friend class NetworkManager;
};
diff --git a/webrtc/base/network_unittest.cc b/webrtc/base/network_unittest.cc
index 7ad45a3..7133d8b 100644
--- a/webrtc/base/network_unittest.cc
+++ b/webrtc/base/network_unittest.cc
@@ -299,6 +299,8 @@
EXPECT_EQ(1U, list.size());
EXPECT_EQ(ipv4_network1.ToString(), list[0]->ToString());
Network* net1 = list[0];
+ uint16_t net_id1 = net1->id();
+ EXPECT_EQ(1, net_id1);
list.clear();
// Replace ipv4_network1 with ipv4_network2.
@@ -315,6 +317,9 @@
EXPECT_EQ(1U, list.size());
EXPECT_EQ(ipv4_network2.ToString(), list[0]->ToString());
Network* net2 = list[0];
+ uint16_t net_id2 = net2->id();
+ // Network id will increase.
+ EXPECT_LT(net_id1, net_id2);
list.clear();
// Add Network2 back.
@@ -332,6 +337,8 @@
EXPECT_EQ(2U, list.size());
EXPECT_TRUE((net1 == list[0] && net2 == list[1]) ||
(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();
// Call MergeNetworkList() again and verify that we don't get update
@@ -350,6 +357,8 @@
EXPECT_EQ(2U, list.size());
EXPECT_TRUE((net1 == list[0] && net2 == list[1]) ||
(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();
}
diff --git a/webrtc/base/networkmonitor.h b/webrtc/base/networkmonitor.h
index d9d6cc4..35ab2b1 100644
--- a/webrtc/base/networkmonitor.h
+++ b/webrtc/base/networkmonitor.h
@@ -46,6 +46,7 @@
// This is needed because some operating systems (like Android) require a
// special bind call to put packets on a non-default network interface.
virtual int BindSocketToNetwork(int socket_fd, const IPAddress& address) = 0;
+ virtual ~NetworkBinderInterface() {}
};
/*
diff --git a/webrtc/p2p/base/candidate.h b/webrtc/p2p/base/candidate.h
index 11481cd..4ab1b6f 100644
--- a/webrtc/p2p/base/candidate.h
+++ b/webrtc/p2p/base/candidate.h
@@ -27,7 +27,7 @@
namespace cricket {
-const uint32_t kMaxNetworkCost = 999;
+const uint16_t kMaxNetworkCost = 999;
// Candidate for ICE based connection discovery.
@@ -40,7 +40,9 @@
component_(0),
priority_(0),
network_type_(rtc::ADAPTER_TYPE_UNKNOWN),
- generation_(0) {}
+ generation_(0),
+ network_id_(0),
+ network_cost_(0) {}
Candidate(int component,
const std::string& protocol,
@@ -50,7 +52,9 @@
const std::string& password,
const std::string& type,
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)),
component_(component),
protocol_(protocol),
@@ -61,7 +65,9 @@
type_(type),
network_type_(rtc::ADAPTER_TYPE_UNKNOWN),
generation_(generation),
- foundation_(foundation) {}
+ foundation_(foundation),
+ network_id_(network_id),
+ network_cost_(network_cost) {}
const std::string & id() const { return id_; }
void set_id(const std::string & id) { id_ = id; }
@@ -143,11 +149,15 @@
// |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) {
+ void set_network_cost(uint16_t network_cost) {
ASSERT(network_cost <= kMaxNetworkCost);
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 {
return foundation_;
@@ -178,13 +188,14 @@
// Determines whether this candidate is equivalent to the given one.
bool IsEquivalent(const Candidate& c) const {
// 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
- // a float so equality checking is always worrisome).
+ // the priority and the network cost, since they should be the same if the
+ // rest are.
return (component_ == c.component_) && (protocol_ == c.protocol_) &&
(address_ == c.address_) && (username_ == c.username_) &&
(password_ == c.password_) && (type_ == c.type_) &&
(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
@@ -238,7 +249,7 @@
ost << "Cand[" << transport_name_ << ":" << foundation_ << ":" << component_
<< ":" << protocol_ << ":" << priority_ << ":" << address << ":"
<< type_ << ":" << related_address_ << ":" << username_ << ":"
- << password_ << ":" << network_cost_ << "]";
+ << password_ << ":" << network_id_ << ":" << network_cost_ << "]";
return ost.str();
}
@@ -257,8 +268,9 @@
std::string foundation_;
rtc::SocketAddress related_address_;
std::string tcptype_;
- uint32_t network_cost_ = 0;
std::string transport_name_;
+ uint16_t network_id_;
+ uint16_t network_cost_;
};
// Used during parsing and writing to map component to channel name
diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc
index 759fd46..e0428d6 100644
--- a/webrtc/p2p/base/p2ptransportchannel.cc
+++ b/webrtc/p2p/base/p2ptransportchannel.cc
@@ -614,25 +614,30 @@
}
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;
+ uint16_t network_id = 0;
+ uint16_t network_cost = 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
// If the source transport address of the request does not match any
// existing remote candidates, it represents a new peer reflexive remote
// candidate.
- remote_candidate = Candidate(component(), ProtoToString(proto), address, 0,
- remote_username, remote_password,
- PRFLX_PORT_TYPE, remote_generation, "");
+ remote_candidate = Candidate(
+ component(), ProtoToString(proto), address, remote_candidate_priority,
+ remote_username, remote_password, PRFLX_PORT_TYPE, remote_generation,
+ "", network_id, network_cost);
// From RFC 5245, section-7.2.1.3:
// The foundation of the candidate is set to an arbitrary value, different
// 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
diff --git a/webrtc/p2p/base/port.cc b/webrtc/p2p/base/port.cc
index 9207c9d..eba15f6 100644
--- a/webrtc/p2p/base/port.cc
+++ b/webrtc/p2p/base/port.cc
@@ -242,25 +242,17 @@
ASSERT(!tcptype.empty());
}
- Candidate c;
- c.set_id(rtc::CreateRandomString(8));
- c.set_component(component_);
- c.set_type(type);
- c.set_protocol(protocol);
+ std::string foundation =
+ ComputeFoundation(type, protocol, relay_protocol, base_address);
+ Candidate c(component_, protocol, address, 0U, username_fragment(), password_,
+ type, generation_, foundation, network_->id(), network_cost_);
+ c.set_priority(
+ c.GetPriority(type_preference, network_->preference(), relay_preference));
c.set_relay_protocol(relay_protocol);
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_type(network_->type());
- c.set_network_cost(network_cost_);
- c.set_generation(generation_);
c.set_related_address(related_address);
- c.set_foundation(
- ComputeFoundation(type, protocol, relay_protocol, base_address));
candidates_.push_back(c);
SignalCandidateReady(this, c);
@@ -705,11 +697,10 @@
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));
- }
+ uint32_t network_info = connection_->port()->Network()->id();
+ network_info = (network_info << 16) | connection_->port()->network_cost();
+ request->AddAttribute(
+ new StunUInt32Attribute(STUN_ATTR_NETWORK_INFO, network_info));
// Adding ICE_CONTROLLED or ICE_CONTROLLING attribute based on the role.
if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLING) {
@@ -1412,11 +1403,12 @@
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(),
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.
local_candidate_index_ = port_->AddPrflxCandidate(new_local_candidate);
diff --git a/webrtc/p2p/base/port.h b/webrtc/p2p/base/port.h
index 65f82e4..4c5a150 100644
--- a/webrtc/p2p/base/port.h
+++ b/webrtc/p2p/base/port.h
@@ -296,7 +296,7 @@
void set_candidate_filter(uint32_t candidate_filter) {
candidate_filter_ = candidate_filter;
}
- int32_t network_cost() const { return network_cost_; }
+ int16_t network_cost() const { return network_cost_; }
protected:
enum {
@@ -403,7 +403,7 @@
// 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_;
+ uint16_t network_cost_;
friend class Connection;
};
diff --git a/webrtc/p2p/base/port_unittest.cc b/webrtc/p2p/base/port_unittest.cc
index be5ced9..d27be29 100644
--- a/webrtc/p2p/base/port_unittest.cc
+++ b/webrtc/p2p/base/port_unittest.cc
@@ -559,6 +559,10 @@
}
}
+ void SetNetworkType(rtc::AdapterType adapter_type) {
+ network_.set_type(adapter_type);
+ }
+
void TestCrossFamilyPorts(int type);
void ExpectPortsCanConnect(bool can_connect, Port* p1, Port* p2);
@@ -1757,6 +1761,51 @@
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_F(PortTest, TestHandleStunMessage) {
// Our port will act as the "remote" port.
diff --git a/webrtc/p2p/base/stun.h b/webrtc/p2p/base/stun.h
index 9c84713..aada43c 100644
--- a/webrtc/p2p/base/stun.h
+++ b/webrtc/p2p/base/stun.h
@@ -606,7 +606,9 @@
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
+ // 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.
@@ -621,7 +623,7 @@
virtual StunAttributeValueType GetAttributeValueType(int type) const {
switch (type) {
case STUN_ATTR_PRIORITY:
- case STUN_ATTR_NETWORK_COST:
+ case STUN_ATTR_NETWORK_INFO:
return STUN_VALUE_UINT32;
case STUN_ATTR_USE_CANDIDATE: return STUN_VALUE_BYTE_STRING;
case STUN_ATTR_ICE_CONTROLLED: return STUN_VALUE_UINT64;