Chromium Code Reviews| Index: webrtc/p2p/base/p2ptransportchannel.cc |
| diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc |
| index 094a8dcc8f1ffc937a436dc85890633c62d60760..578338446f193d11122674019539cc52101a342b 100644 |
| --- a/webrtc/p2p/base/p2ptransportchannel.cc |
| +++ b/webrtc/p2p/base/p2ptransportchannel.cc |
| @@ -124,6 +124,14 @@ class ConnectionCompare { |
| cricket::Connection* a = const_cast<cricket::Connection*>(ca); |
| cricket::Connection* b = const_cast<cricket::Connection*>(cb); |
| + // The IceProtocol is initialized to ICEPROTO_HYBRID and can be updated to |
| + // GICE or RFC5245 when an answer SDP is set, or when a STUN message is |
| + // received. So the port receiving the STUN message may have a different |
| + // IceProtocol if the answer SDP is not set yet. |
| + ASSERT(a->port()->IceProtocol() == b->port()->IceProtocol() || |
| + a->port()->IceProtocol() == cricket::ICEPROTO_HYBRID || |
| + b->port()->IceProtocol() == cricket::ICEPROTO_HYBRID); |
| + |
| // Compare first on writability and static preferences. |
| int cmp = CompareConnections(a, b); |
| if (cmp > 0) |
| @@ -184,6 +192,7 @@ P2PTransportChannel::P2PTransportChannel(const std::string& content_name, |
| pending_best_connection_(NULL), |
| sort_dirty_(false), |
| was_writable_(false), |
| + protocol_type_(ICEPROTO_HYBRID), |
| remote_ice_mode_(ICEMODE_FULL), |
| ice_role_(ICEROLE_UNKNOWN), |
| tiebreaker_(0), |
| @@ -284,6 +293,21 @@ TransportChannelState P2PTransportChannel::GetState() const { |
| return TransportChannelState::STATE_COMPLETED; |
| } |
| +bool P2PTransportChannel::GetIceProtocolType(IceProtocolType* type) const { |
| + *type = protocol_type_; |
| + return true; |
| +} |
| + |
| +void P2PTransportChannel::SetIceProtocolType(IceProtocolType type) { |
| + ASSERT(worker_thread_ == rtc::Thread::Current()); |
| + |
| + protocol_type_ = type; |
| + for (std::vector<PortInterface *>::iterator it = ports_.begin(); |
| + it != ports_.end(); ++it) { |
| + (*it)->SetIceProtocolType(protocol_type_); |
| + } |
| +} |
| + |
| void P2PTransportChannel::SetIceCredentials(const std::string& ice_ufrag, |
| const std::string& ice_pwd) { |
| ASSERT(worker_thread_ == rtc::Thread::Current()); |
| @@ -323,8 +347,9 @@ void P2PTransportChannel::SetRemoteIceCredentials(const std::string& ice_ufrag, |
| } |
| if (ice_restart) { |
| - // We need to keep track of the remote ice restart so newer |
| - // connections are prioritized over the older. |
| + // |candidate.generation()| is not signaled in ICEPROTO_RFC5245. |
| + // Therefore we need to keep track of the remote ice restart so |
| + // newer connections are prioritized over the older. |
| ++remote_candidate_generation_; |
| } |
| } |
| @@ -385,6 +410,7 @@ void P2PTransportChannel::OnPortReady(PortAllocatorSession *session, |
| // The session will handle this, and send an initiate/accept/modify message |
| // if one is pending. |
| + port->SetIceProtocolType(protocol_type_); |
| port->SetIceRole(ice_role_); |
| port->SetIceTiebreaker(tiebreaker_); |
| ports_.push_back(port); |
| @@ -479,30 +505,46 @@ void P2PTransportChannel::OnUnknownAddress( |
| } |
| } else { |
| // Create a new candidate with this address. |
| + std::string type; |
| int remote_candidate_priority; |
| - |
| - // The priority of the candidate is set to the PRIORITY attribute |
| - // from the request. |
| - const StunUInt32Attribute* priority_attr = |
| - stun_msg->GetUInt32(STUN_ATTR_PRIORITY); |
| - if (!priority_attr) { |
| - LOG(LS_WARNING) << "P2PTransportChannel::OnUnknownAddress - " |
| - << "No STUN_ATTR_PRIORITY found in the " |
| - << "stun request message"; |
| - port->SendBindingErrorResponse(stun_msg, address, |
| - STUN_ERROR_BAD_REQUEST, |
| - STUN_ERROR_REASON_BAD_REQUEST); |
| - return; |
| + if (port->IceProtocol() == ICEPROTO_RFC5245) { |
| + // 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. |
| + type = PRFLX_PORT_TYPE; |
| + |
| + // The priority of the candidate is set to the PRIORITY attribute |
| + // from the request. |
| + const StunUInt32Attribute* priority_attr = |
| + stun_msg->GetUInt32(STUN_ATTR_PRIORITY); |
| + if (!priority_attr) { |
| + LOG(LS_WARNING) << "P2PTransportChannel::OnUnknownAddress - " |
| + << "No STUN_ATTR_PRIORITY found in the " |
| + << "stun request message"; |
| + port->SendBindingErrorResponse(stun_msg, address, |
| + STUN_ERROR_BAD_REQUEST, |
| + STUN_ERROR_REASON_BAD_REQUEST); |
| + return; |
| + } |
| + remote_candidate_priority = priority_attr->value(); |
| + } else { |
| + // G-ICE doesn't support prflx candidate. |
| + // We set candidate type to STUN_PORT_TYPE if the binding request comes |
| + // from a relay port or the shared socket is used. Otherwise we use the |
| + // port's type as the candidate type. |
| + if (port->Type() == RELAY_PORT_TYPE || port->SharedSocket()) { |
| + type = STUN_PORT_TYPE; |
| + } else { |
| + type = port->Type(); |
| + } |
| + remote_candidate_priority = remote_candidate.GetPriority( |
| + ICE_TYPE_PREFERENCE_PRFLX, port->Network()->preference(), 0); |
| } |
| - remote_candidate_priority = priority_attr->value(); |
| - // 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, 0U, ""); |
| + remote_username, remote_password, type, 0U, ""); |
| // From RFC 5245, section-7.2.1.3: |
| // The foundation of the candidate is set to an arbitrary value, different |
| @@ -513,56 +555,81 @@ void P2PTransportChannel::OnUnknownAddress( |
| remote_candidate.set_priority(remote_candidate_priority); |
| } |
| - // RFC5245, the agent constructs a pair whose local candidate is equal to |
| - // the transport address on which the STUN request was received, and a |
| - // remote candidate equal to the source transport address where the |
| - // request came from. |
| + if (port->IceProtocol() == ICEPROTO_RFC5245) { |
| + // RFC5245, the agent constructs a pair whose local candidate is equal to |
| + // the transport address on which the STUN request was received, and a |
| + // remote candidate equal to the source transport address where the |
| + // request came from. |
| + |
| + // There shouldn't be an existing connection with this remote address. |
| + // When ports are muxed, this channel might get multiple unknown address |
| + // signals. In that case if the connection is already exists, we should |
| + // simply ignore the signal otherwise send server error. |
| + if (port->GetConnection(remote_candidate.address())) { |
| + if (port_muxed) { |
| + LOG(LS_INFO) << "Connection already exists for peer reflexive " |
| + << "candidate: " << remote_candidate.ToString(); |
| + return; |
| + } else { |
| + ASSERT(false); |
| + port->SendBindingErrorResponse(stun_msg, address, |
| + STUN_ERROR_SERVER_ERROR, |
| + STUN_ERROR_REASON_SERVER_ERROR); |
| + return; |
| + } |
| + } |
| - // There shouldn't be an existing connection with this remote address. |
| - // When ports are muxed, this channel might get multiple unknown address |
| - // signals. In that case if the connection is already exists, we should |
| - // simply ignore the signal otherwise send server error. |
| - if (port->GetConnection(remote_candidate.address())) { |
| - if (port_muxed) { |
| - LOG(LS_INFO) << "Connection already exists for peer reflexive " |
| - << "candidate: " << remote_candidate.ToString(); |
| - return; |
| - } else { |
| + Connection* connection = port->CreateConnection( |
| + remote_candidate, cricket::PortInterface::ORIGIN_THIS_PORT); |
| + if (!connection) { |
| ASSERT(false); |
| port->SendBindingErrorResponse(stun_msg, address, |
| STUN_ERROR_SERVER_ERROR, |
| STUN_ERROR_REASON_SERVER_ERROR); |
| return; |
| } |
| - } |
| - Connection* connection = port->CreateConnection( |
| - remote_candidate, cricket::PortInterface::ORIGIN_THIS_PORT); |
| - if (!connection) { |
| - ASSERT(false); |
| - port->SendBindingErrorResponse(stun_msg, address, |
| - STUN_ERROR_SERVER_ERROR, |
| - STUN_ERROR_REASON_SERVER_ERROR); |
| - return; |
| - } |
| + LOG(LS_INFO) << "Adding connection from " |
| + << (remote_candidate_is_new ? "peer reflexive" : "resurrected") |
| + << " candidate: " << remote_candidate.ToString(); |
| + AddConnection(connection); |
| + connection->ReceivedPing(); |
| - LOG(LS_INFO) << "Adding connection from " |
| - << (remote_candidate_is_new ? "peer reflexive" : "resurrected") |
| - << " candidate: " << remote_candidate.ToString(); |
| - AddConnection(connection); |
| - connection->ReceivedPing(); |
| + // Send the pinger a successful stun response. |
| + port->SendBindingResponse(stun_msg, address); |
| - bool received_use_candidate = |
| - stun_msg->GetByteString(STUN_ATTR_USE_CANDIDATE) != nullptr; |
| - if (received_use_candidate && ice_role_ == ICEROLE_CONTROLLED) { |
| - connection->set_nominated(true); |
| - OnNominated(connection); |
| - } |
| + bool received_use_candidate = |
| + stun_msg->GetByteString(STUN_ATTR_USE_CANDIDATE) != nullptr; |
| + if (received_use_candidate && ice_role_ == ICEROLE_CONTROLLED) { |
| + connection->set_nominated(true); |
| + OnNominated(connection); |
| + } |
| - // Update the list of connections since we just added another. We do this |
| - // after sending the response since it could (in principle) delete the |
| - // connection in question. |
| - SortConnections(); |
| + // Update the list of connections since we just added another. We do this |
| + // after sending the response since it could (in principle) delete the |
| + // connection in question. |
| + SortConnections(); |
| + } else { |
| + // Check for connectivity to this address. Create connections |
| + // to this address across all local ports. First, add this as a new remote |
| + // address |
| + if (!CreateConnections(remote_candidate, port, true)) { |
| + // Hopefully this won't occur, because changing a destination address |
| + // shouldn't cause a new connection to fail |
| + ASSERT(false); |
| + port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_SERVER_ERROR, |
| + STUN_ERROR_REASON_SERVER_ERROR); |
| + return; |
| + } |
| + |
| + // Send the pinger a successful stun response. |
| + port->SendBindingResponse(stun_msg, address); |
| + |
| + // Update the list of connections since we just added another. We do this |
| + // after sending the response since it could (in principle) delete the |
| + // connection in question. |
| + SortConnections(); |
| + } |
| } |
| void P2PTransportChannel::OnRoleConflict(PortInterface* port) { |
| @@ -583,6 +650,7 @@ void P2PTransportChannel::OnSignalingReady() { |
| void P2PTransportChannel::OnNominated(Connection* conn) { |
| ASSERT(worker_thread_ == rtc::Thread::Current()); |
| ASSERT(ice_role_ == ICEROLE_CONTROLLED); |
| + ASSERT(protocol_type_ == ICEPROTO_RFC5245); |
| if (conn->write_state() == Connection::STATE_WRITABLE) { |
| if (best_connection_ != conn) { |
| @@ -741,8 +809,13 @@ bool P2PTransportChannel::FindConnection( |
| uint32 P2PTransportChannel::GetRemoteCandidateGeneration( |
| const Candidate& candidate) { |
| - // We need to keep track of the remote ice restart so newer |
| - // connections are prioritized over the older. |
| + if (protocol_type_ == ICEPROTO_GOOGLE) { |
| + // The Candidate.generation() can be trusted. Nothing needs to be done. |
| + return candidate.generation(); |
| + } |
| + // |candidate.generation()| is not signaled in ICEPROTO_RFC5245. |
| + // Therefore we need to keep track of the remote ice restart so |
| + // newer connections are prioritized over the older. |
| ASSERT(candidate.generation() == 0 || |
| candidate.generation() == remote_candidate_generation_); |
| return remote_candidate_generation_; |
| @@ -920,6 +993,15 @@ void P2PTransportChannel::SortConnections() { |
| // will be sorted. |
| UpdateConnectionStates(); |
| + if (protocol_type_ == ICEPROTO_HYBRID) { |
| + // If we are in hybrid mode, we are not sending any ping requests, so there |
| + // is no point in sorting the connections. In hybrid state, ports can have |
| + // different protocol than hybrid and protocol may differ from one another. |
| + // Instead just update the state of this channel |
| + UpdateChannelState(); |
| + return; |
| + } |
| + |
| // Any changes after this point will require a re-sort. |
| sort_dirty_ = false; |
| @@ -942,7 +1024,10 @@ void P2PTransportChannel::SortConnections() { |
| // connection although it will have higher priority if it is writable. |
| // The controlled side can switch the best connection only if the current |
| // |best connection_| has not been nominated by the controlling side yet. |
| - if ((ice_role_ == ICEROLE_CONTROLLING || !best_nominated_connection()) && |
| + |
| + // We don't want to pick the best connections if channel is using RFC5245. |
|
minyue-webrtc
2015/08/21 13:19:56
Merge against https://codereview.webrtc.org/129933
|
| + if ((protocol_type_ != ICEPROTO_RFC5245 || ice_role_ == ICEROLE_CONTROLLING || |
| + !best_nominated_connection()) && |
| ShouldSwitch(best_connection_, top_connection)) { |
| LOG(LS_INFO) << "Switching best connection: " << top_connection->ToString(); |
| SwitchBestConnectionTo(top_connection); |
| @@ -1216,7 +1301,8 @@ Connection* P2PTransportChannel::FindNextPingableConnection() { |
| continue; |
| } |
| bool needs_triggered_check = |
| - (!conn->writable() && |
| + (protocol_type_ == ICEPROTO_RFC5245 && |
| + !conn->writable() && |
| conn->last_ping_received() > conn->last_ping_sent()); |
| if (needs_triggered_check && |
| (!oldest_needing_triggered_check || |
| @@ -1251,13 +1337,15 @@ Connection* P2PTransportChannel::FindNextPingableConnection() { |
| // b.2) |conn| is writable. |
| void P2PTransportChannel::PingConnection(Connection* conn) { |
| bool use_candidate = false; |
| - 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()); |
| - } else if (remote_ice_mode_ == ICEMODE_LITE && conn == best_connection_) { |
| - use_candidate = best_connection_->writable(); |
| + if (protocol_type_ == ICEPROTO_RFC5245) { |
| + 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()); |
| + } else if (remote_ice_mode_ == ICEMODE_LITE && conn == best_connection_) { |
| + use_candidate = best_connection_->writable(); |
| + } |
| } |
| conn->set_use_candidate_attr(use_candidate); |
| conn->Ping(rtc::Time()); |
| @@ -1270,7 +1358,7 @@ void P2PTransportChannel::OnConnectionStateChange(Connection* connection) { |
| // Update the best connection if the state change is from pending best |
| // connection and role is controlled. |
| - if (ice_role_ == ICEROLE_CONTROLLED) { |
| + if (protocol_type_ == ICEPROTO_RFC5245 && ice_role_ == ICEROLE_CONTROLLED) { |
| if (connection == pending_best_connection_ && connection->writable()) { |
| pending_best_connection_ = NULL; |
| LOG(LS_INFO) << "Switching best connection on controlled side" |