From a0c44eaa82de0c33a94b2eb06fa1dd89fd27aa60 Mon Sep 17 00:00:00 2001 From: honghaiz Date: Wed, 23 Mar 2016 16:07:48 -0700 Subject: [PATCH] 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} --- webrtc/api/webrtcsdp.cc | 15 ++++++-- webrtc/api/webrtcsdp_unittest.cc | 30 ++++++++++++++++ webrtc/base/network.cc | 1 + webrtc/base/network.h | 11 ++++++ webrtc/base/network_unittest.cc | 9 +++++ webrtc/base/networkmonitor.h | 1 + webrtc/p2p/base/candidate.h | 34 ++++++++++++------ webrtc/p2p/base/p2ptransportchannel.cc | 21 ++++++----- webrtc/p2p/base/port.cc | 32 +++++++---------- webrtc/p2p/base/port.h | 4 +-- webrtc/p2p/base/port_unittest.cc | 49 ++++++++++++++++++++++++++ webrtc/p2p/base/stun.h | 6 ++-- 12 files changed, 167 insertions(+), 46 deletions(-) diff --git a/webrtc/api/webrtcsdp.cc b/webrtc/api/webrtcsdp.cc index 4a296d9ce2..93d4acc6c3 100644 --- a/webrtc/api/webrtcsdp.cc +++ b/webrtc/api/webrtcsdp.cc @@ -127,6 +127,7 @@ static const char kAttributeCandidateRport[] = "rport"; 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 @@ bool ParseCandidate(const std::string& message, Candidate* candidate, 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 @@ bool ParseCandidate(const std::string& message, Candidate* candidate, 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 @@ bool ParseCandidate(const std::string& message, Candidate* candidate, *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 @@ void BuildCandidate(const std::vector& candidates, 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 9559701642..c52720453b 100644 --- a/webrtc/api/webrtcsdp_unittest.cc +++ b/webrtc/api/webrtcsdp_unittest.cc @@ -2058,6 +2058,19 @@ TEST_F(WebRtcSdpTest, SerializeCandidates) { 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 @@ TEST_F(WebRtcSdpTest, DeserializeCandidate) { 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 @@ TEST_F(WebRtcSdpTest, DeserializeCandidate) { 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 4f3b91981e..4780e2f88d 100644 --- a/webrtc/base/network.cc +++ b/webrtc/base/network.cc @@ -286,6 +286,7 @@ void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks, // 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 680c005ea2..ee22d5e573 100644 --- a/webrtc/base/network.h +++ b/webrtc/base/network.h @@ -173,6 +173,11 @@ class NetworkManagerBase : public NetworkManager { 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 @@ class Network { 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 @@ class Network { 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 7ad45a37af..7133d8b405 100644 --- a/webrtc/base/network_unittest.cc +++ b/webrtc/base/network_unittest.cc @@ -299,6 +299,8 @@ TEST_F(NetworkTest, TestBasicMergeNetworkList) { 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 @@ TEST_F(NetworkTest, TestBasicMergeNetworkList) { 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 @@ TEST_F(NetworkTest, TestBasicMergeNetworkList) { 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 @@ TEST_F(NetworkTest, TestBasicMergeNetworkList) { 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 d9d6cc49d8..35ab2b120e 100644 --- a/webrtc/base/networkmonitor.h +++ b/webrtc/base/networkmonitor.h @@ -46,6 +46,7 @@ class NetworkBinderInterface { // 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 11481cdb05..4ab1b6f235 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 @@ class Candidate { 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 @@ class Candidate { 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 @@ class Candidate { 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 @@ 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. - 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 @@ class Candidate { // 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 @@ class Candidate { 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 @@ class Candidate { 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 759fd461db..e0428d6383 100644 --- a/webrtc/p2p/base/p2ptransportchannel.cc +++ b/webrtc/p2p/base/p2ptransportchannel.cc @@ -614,25 +614,30 @@ void P2PTransportChannel::OnUnknownAddress( } 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(network_info >> 16); + network_cost = static_cast(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(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 9207c9d042..eba15f69a3 100644 --- a/webrtc/p2p/base/port.cc +++ b/webrtc/p2p/base/port.cc @@ -242,25 +242,17 @@ void Port::AddAddress(const rtc::SocketAddress& address, 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 @@ class ConnectionRequest : public StunRequest { static_cast(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 @@ 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(), 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 65f82e4f25..4c5a15090e 100644 --- a/webrtc/p2p/base/port.h +++ b/webrtc/p2p/base/port.h @@ -296,7 +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_; } + int16_t network_cost() const { return network_cost_; } protected: 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 // (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 be5ced97c0..d27be29442 100644 --- a/webrtc/p2p/base/port_unittest.cc +++ b/webrtc/p2p/base/port_unittest.cc @@ -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 ExpectPortsCanConnect(bool can_connect, Port* p1, Port* p2); @@ -1757,6 +1761,51 @@ TEST_F(PortTest, TestUseCandidateAttribute) { ASSERT_TRUE(use_candidate_attr != NULL); } +TEST_F(PortTest, TestNetworkInfoAttribute) { + rtc::scoped_ptr lport( + CreateTestPort(kLocalAddr1, "lfrag", "lpass")); + // Set the network type for rport to be cellular so its cost will be 999. + SetNetworkType(rtc::ADAPTER_TYPE_CELLULAR); + rtc::scoped_ptr 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 9c8471304b..aada43c322 100644 --- a/webrtc/p2p/base/stun.h +++ b/webrtc/p2p/base/stun.h @@ -606,7 +606,9 @@ enum IceAttributeType { 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 @@ class IceMessage : public StunMessage { 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;