STUN PING request
This patch introduces a new type of STUN ping, GOOG_PING_REQUEST/RESPONSE which is similar to a STUN_BINDING but does not transmit any values. The Connection class automatically sends these if no STUN attributes has changed since last call to Connection::Ping() if the remote peer has signaled that it supports it. BUG=webrtc:11100 Change-Id: Ib1b590f0b90ca6cb56f2eb07cd62f976e246bc8c Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/159961 Commit-Queue: Jonas Oreland <jonaso@webrtc.org> Reviewed-by: Taylor <deadbeef@webrtc.org> Reviewed-by: Björn Terelius <terelius@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/master@{#30062}
This commit is contained in:
parent
c907d4f223
commit
9a52bd733c
@ -1115,6 +1115,55 @@ bool StunUInt16ListAttribute::Write(ByteBufferWriter* buf) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string StunMethodToString(int msg_type) {
|
||||
switch (msg_type) {
|
||||
case STUN_BINDING_REQUEST:
|
||||
return "STUN BINDING request";
|
||||
case STUN_BINDING_INDICATION:
|
||||
return "STUN BINDING indication";
|
||||
case STUN_BINDING_RESPONSE:
|
||||
return "STUN BINDING response";
|
||||
case STUN_BINDING_ERROR_RESPONSE:
|
||||
return "STUN BINDING error response";
|
||||
case GOOG_PING_REQUEST:
|
||||
return "GOOG PING request";
|
||||
case GOOG_PING_RESPONSE:
|
||||
return "GOOG PING response";
|
||||
case GOOG_PING_ERROR_RESPONSE:
|
||||
return "GOOG PING error response";
|
||||
case STUN_ALLOCATE_REQUEST:
|
||||
return "TURN ALLOCATE request";
|
||||
case STUN_ALLOCATE_RESPONSE:
|
||||
return "TURN ALLOCATE response";
|
||||
case STUN_ALLOCATE_ERROR_RESPONSE:
|
||||
return "TURN ALLOCATE error response";
|
||||
case TURN_REFRESH_REQUEST:
|
||||
return "TURN REFRESH request";
|
||||
case TURN_REFRESH_RESPONSE:
|
||||
return "TURN REFRESH response";
|
||||
case TURN_REFRESH_ERROR_RESPONSE:
|
||||
return "TURN REFRESH error response";
|
||||
case TURN_SEND_INDICATION:
|
||||
return "TURN SEND INDICATION";
|
||||
case TURN_DATA_INDICATION:
|
||||
return "TURN DATA INDICATION";
|
||||
case TURN_CREATE_PERMISSION_REQUEST:
|
||||
return "TURN CREATE PERMISSION request";
|
||||
case TURN_CREATE_PERMISSION_RESPONSE:
|
||||
return "TURN CREATE PERMISSION response";
|
||||
case TURN_CREATE_PERMISSION_ERROR_RESPONSE:
|
||||
return "TURN CREATE PERMISSION error response";
|
||||
case TURN_CHANNEL_BIND_REQUEST:
|
||||
return "TURN CHANNEL BIND request";
|
||||
case TURN_CHANNEL_BIND_RESPONSE:
|
||||
return "TURN CHANNEL BIND response";
|
||||
case TURN_CHANNEL_BIND_ERROR_RESPONSE:
|
||||
return "TURN CHANNEL BIND error response";
|
||||
default:
|
||||
return "UNKNOWN<" + std::to_string(msg_type) + ">";
|
||||
}
|
||||
}
|
||||
|
||||
int GetStunSuccessResponseType(int req_type) {
|
||||
return IsStunRequestType(req_type) ? (req_type | 0x100) : -1;
|
||||
}
|
||||
|
||||
@ -34,7 +34,9 @@ enum StunMessageType {
|
||||
STUN_BINDING_RESPONSE = 0x0101,
|
||||
STUN_BINDING_ERROR_RESPONSE = 0x0111,
|
||||
|
||||
// Method 0x80
|
||||
// Method 0x80, GOOG-PING is a variant of STUN BINDING
|
||||
// that is sent instead of a STUN BINDING if the binding
|
||||
// was identical to the one before.
|
||||
GOOG_PING_REQUEST = 0x200,
|
||||
GOOG_PING_RESPONSE = 0x300,
|
||||
GOOG_PING_ERROR_RESPONSE = 0x310,
|
||||
@ -513,6 +515,9 @@ class StunUInt16ListAttribute : public StunAttribute {
|
||||
std::vector<uint16_t>* attr_types_;
|
||||
};
|
||||
|
||||
// Return a string e.g "STUN BINDING request".
|
||||
std::string StunMethodToString(int msg_type);
|
||||
|
||||
// Returns the (successful) response type for the given request type.
|
||||
// Returns -1 if |request_type| is not a valid request type.
|
||||
int GetStunSuccessResponseType(int request_type);
|
||||
@ -669,11 +674,15 @@ enum IceAttributeType {
|
||||
|
||||
// When adding new attributes to STUN_ATTR_GOOG_MISC_INFO
|
||||
// (which is a list of uint16_t), append the indices of these attributes below
|
||||
// and do NOT change the exisiting indices. The indices of attributes must be
|
||||
// and do NOT change the existing indices. The indices of attributes must be
|
||||
// consistent with those used in ConnectionRequest::Prepare when forming a STUN
|
||||
// message for the ICE connectivity check, and they are used when parsing a
|
||||
// received STUN message.
|
||||
enum class IceGoogMiscInfoAttributeIndex {};
|
||||
enum class IceGoogMiscInfoBindingRequestAttributeIndex {};
|
||||
|
||||
enum class IceGoogMiscInfoBindingResponseAttributeIndex {
|
||||
SUPPORT_GOOG_PING_VERSION = 0,
|
||||
};
|
||||
|
||||
// RFC 5245-defined errors.
|
||||
enum IceErrorCode {
|
||||
|
||||
@ -143,11 +143,15 @@ constexpr int64_t kMinExtraPingDelayMs = 100;
|
||||
// Default field trials.
|
||||
const cricket::IceFieldTrials kDefaultFieldTrials;
|
||||
|
||||
constexpr int kSupportGoogPingVersionIndex =
|
||||
static_cast<int>(cricket::IceGoogMiscInfoBindingResponseAttributeIndex::
|
||||
SUPPORT_GOOG_PING_VERSION);
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// A ConnectionRequest is a simple STUN ping used to determine writability.
|
||||
// A ConnectionRequest is a STUN binding used to determine writability.
|
||||
ConnectionRequest::ConnectionRequest(Connection* connection)
|
||||
: StunRequest(new IceMessage()), connection_(connection) {}
|
||||
|
||||
@ -220,10 +224,14 @@ void ConnectionRequest::Prepare(StunMessage* request) {
|
||||
request->AddAttribute(std::make_unique<StunUInt32Attribute>(
|
||||
STUN_ATTR_PRIORITY, prflx_priority));
|
||||
|
||||
// Adding Message Integrity attribute.
|
||||
request->AddMessageIntegrity(connection_->remote_candidate().password());
|
||||
// Adding Fingerprint.
|
||||
request->AddFingerprint();
|
||||
if (connection_->ShouldSendGoogPing(request)) {
|
||||
request->SetType(GOOG_PING_REQUEST);
|
||||
request->ClearAttributes();
|
||||
request->AddMessageIntegrity32(connection_->remote_candidate().password());
|
||||
} else {
|
||||
request->AddMessageIntegrity(connection_->remote_candidate().password());
|
||||
request->AddFingerprint();
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionRequest::OnResponse(StunMessage* response) {
|
||||
@ -451,11 +459,11 @@ void Connection::OnReadPacket(const char* data,
|
||||
rtc::LoggingSeverity sev = (!writable() ? rtc::LS_INFO : rtc::LS_VERBOSE);
|
||||
switch (msg->type()) {
|
||||
case STUN_BINDING_REQUEST:
|
||||
RTC_LOG_V(sev) << ToString() << ": Received STUN ping, id="
|
||||
<< rtc::hex_encode(msg->transaction_id());
|
||||
|
||||
RTC_LOG_V(sev) << ToString() << ": Received "
|
||||
<< StunMethodToString(msg->type())
|
||||
<< ", id=" << rtc::hex_encode(msg->transaction_id());
|
||||
if (remote_ufrag == remote_candidate_.username()) {
|
||||
HandleBindingRequest(msg.get());
|
||||
HandleStunBindingOrGoogPingRequest(msg.get());
|
||||
} else {
|
||||
// The packet had the right local username, but the remote username
|
||||
// was not the right one for the remote address.
|
||||
@ -487,7 +495,16 @@ void Connection::OnReadPacket(const char* data,
|
||||
case STUN_BINDING_INDICATION:
|
||||
ReceivedPing(msg->transaction_id());
|
||||
break;
|
||||
|
||||
case GOOG_PING_REQUEST:
|
||||
HandleStunBindingOrGoogPingRequest(msg.get());
|
||||
break;
|
||||
case GOOG_PING_RESPONSE:
|
||||
case GOOG_PING_ERROR_RESPONSE:
|
||||
if (msg->ValidateMessageIntegrity32(data, size,
|
||||
remote_candidate().password())) {
|
||||
requests_.CheckResponse(msg.get());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
RTC_NOTREACHED();
|
||||
break;
|
||||
@ -495,7 +512,7 @@ void Connection::OnReadPacket(const char* data,
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::HandleBindingRequest(IceMessage* msg) {
|
||||
void Connection::HandleStunBindingOrGoogPingRequest(IceMessage* msg) {
|
||||
// This connection should now be receiving.
|
||||
ReceivedPing(msg->transaction_id());
|
||||
if (webrtc::field_trial::IsEnabled("WebRTC-ExtraICEPing") &&
|
||||
@ -523,12 +540,14 @@ void Connection::HandleBindingRequest(IceMessage* msg) {
|
||||
}
|
||||
|
||||
const rtc::SocketAddress& remote_addr = remote_candidate_.address();
|
||||
const std::string& remote_ufrag = remote_candidate_.username();
|
||||
// Check for role conflicts.
|
||||
if (!port_->MaybeIceRoleConflict(remote_addr, msg, remote_ufrag)) {
|
||||
// Received conflicting role from the peer.
|
||||
RTC_LOG(LS_INFO) << "Received conflicting role from the peer.";
|
||||
return;
|
||||
if (msg->type() == STUN_BINDING_REQUEST) {
|
||||
// Check for role conflicts.
|
||||
const std::string& remote_ufrag = remote_candidate_.username();
|
||||
if (!port_->MaybeIceRoleConflict(remote_addr, msg, remote_ufrag)) {
|
||||
// Received conflicting role from the peer.
|
||||
RTC_LOG(LS_INFO) << "Received conflicting role from the peer.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
stats_.recv_ping_requests++;
|
||||
@ -536,7 +555,12 @@ void Connection::HandleBindingRequest(IceMessage* msg) {
|
||||
msg->reduced_transaction_id());
|
||||
|
||||
// This is a validated stun request from remote peer.
|
||||
SendBindingResponse(msg);
|
||||
if (msg->type() == STUN_BINDING_REQUEST) {
|
||||
SendStunBindingResponse(msg);
|
||||
} else {
|
||||
RTC_DCHECK(msg->type() == GOOG_PING_REQUEST);
|
||||
SendGoogPingResponse(msg);
|
||||
}
|
||||
|
||||
// If it timed out on writing check, start up again
|
||||
if (!pruned_ && write_state_ == STATE_WRITE_TIMEOUT) {
|
||||
@ -587,12 +611,9 @@ void Connection::HandleBindingRequest(IceMessage* msg) {
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::SendBindingResponse(const StunMessage* request) {
|
||||
void Connection::SendStunBindingResponse(const StunMessage* request) {
|
||||
RTC_DCHECK(request->type() == STUN_BINDING_REQUEST);
|
||||
|
||||
// Where I send the response.
|
||||
const rtc::SocketAddress& addr = remote_candidate_.address();
|
||||
|
||||
// Retrieve the username from the request.
|
||||
const StunByteStringAttribute* username_attr =
|
||||
request->GetByteString(STUN_ATTR_USERNAME);
|
||||
@ -623,10 +644,36 @@ void Connection::SendBindingResponse(const StunMessage* request) {
|
||||
}
|
||||
|
||||
response.AddAttribute(std::make_unique<StunXorAddressAttribute>(
|
||||
STUN_ATTR_XOR_MAPPED_ADDRESS, addr));
|
||||
STUN_ATTR_XOR_MAPPED_ADDRESS, remote_candidate_.address()));
|
||||
|
||||
if (field_trials_->announce_goog_ping) {
|
||||
auto list =
|
||||
StunAttribute::CreateUInt16ListAttribute(STUN_ATTR_GOOG_MISC_INFO);
|
||||
list->AddTypeAtIndex(kSupportGoogPingVersionIndex, kGoogPingVersion);
|
||||
response.AddAttribute(std::move(list));
|
||||
}
|
||||
|
||||
response.AddMessageIntegrity(local_candidate().password());
|
||||
response.AddFingerprint();
|
||||
|
||||
SendResponseMessage(response);
|
||||
}
|
||||
|
||||
void Connection::SendGoogPingResponse(const StunMessage* request) {
|
||||
RTC_DCHECK(request->type() == GOOG_PING_REQUEST);
|
||||
|
||||
// Fill in the response message.
|
||||
StunMessage response;
|
||||
response.SetType(GOOG_PING_RESPONSE);
|
||||
response.SetTransactionID(request->transaction_id());
|
||||
response.AddMessageIntegrity32(local_candidate().password());
|
||||
SendResponseMessage(response);
|
||||
}
|
||||
|
||||
void Connection::SendResponseMessage(const StunMessage& response) {
|
||||
// Where I send the response.
|
||||
const rtc::SocketAddress& addr = remote_candidate_.address();
|
||||
|
||||
// Send the response message.
|
||||
rtc::ByteBufferWriter buf;
|
||||
response.Write(&buf);
|
||||
@ -635,21 +682,22 @@ void Connection::SendBindingResponse(const StunMessage* request) {
|
||||
rtc::PacketType::kIceConnectivityCheckResponse;
|
||||
auto err = port_->SendTo(buf.Data(), buf.Length(), addr, options, false);
|
||||
if (err < 0) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Failed to send STUN ping response, to="
|
||||
<< addr.ToSensitiveString() << ", err=" << err
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Failed to send "
|
||||
<< StunMethodToString(response.type())
|
||||
<< ", to=" << addr.ToSensitiveString() << ", err=" << err
|
||||
<< ", id=" << rtc::hex_encode(response.transaction_id());
|
||||
} else {
|
||||
// Log at LS_INFO if we send a stun ping response on an unwritable
|
||||
// connection.
|
||||
rtc::LoggingSeverity sev = (!writable()) ? rtc::LS_INFO : rtc::LS_VERBOSE;
|
||||
RTC_LOG_V(sev) << ToString() << ": Sent STUN ping response, to="
|
||||
<< addr.ToSensitiveString()
|
||||
RTC_LOG_V(sev) << ToString() << ": Sent "
|
||||
<< StunMethodToString(response.type())
|
||||
<< ", to=" << addr.ToSensitiveString()
|
||||
<< ", id=" << rtc::hex_encode(response.transaction_id());
|
||||
|
||||
stats_.sent_ping_responses++;
|
||||
LogCandidatePairEvent(webrtc::IceCandidatePairEventType::kCheckResponseSent,
|
||||
request->reduced_transaction_id());
|
||||
response.reduced_transaction_id());
|
||||
}
|
||||
}
|
||||
|
||||
@ -786,7 +834,8 @@ void Connection::ReceivedPing(const absl::optional<std::string>& request_id) {
|
||||
}
|
||||
|
||||
void Connection::HandlePiggybackCheckAcknowledgementIfAny(StunMessage* msg) {
|
||||
RTC_DCHECK(msg->type() == STUN_BINDING_REQUEST);
|
||||
RTC_DCHECK(msg->type() == STUN_BINDING_REQUEST ||
|
||||
msg->type() == GOOG_PING_REQUEST);
|
||||
const StunByteStringAttribute* last_ice_check_received_attr =
|
||||
msg->GetByteString(STUN_ATTR_LAST_ICE_CHECK_RECEIVED);
|
||||
if (last_ice_check_received_attr) {
|
||||
@ -981,8 +1030,9 @@ void Connection::OnConnectionRequestResponse(ConnectionRequest* request,
|
||||
if (RTC_LOG_CHECK_LEVEL_V(sev)) {
|
||||
std::string pings;
|
||||
PrintPingsSinceLastResponse(&pings, 5);
|
||||
RTC_LOG_V(sev) << ToString() << ": Received STUN ping response, id="
|
||||
<< rtc::hex_encode(request->id())
|
||||
RTC_LOG_V(sev) << ToString() << ": Received "
|
||||
<< StunMethodToString(response->type())
|
||||
<< ", id=" << rtc::hex_encode(request->id())
|
||||
<< ", code=0" // Makes logging easier to parse.
|
||||
", rtt="
|
||||
<< rtt << ", pings_since_last_response=" << pings;
|
||||
@ -1002,17 +1052,33 @@ void Connection::OnConnectionRequestResponse(ConnectionRequest* request,
|
||||
webrtc::IceCandidatePairEventType::kCheckResponseReceived,
|
||||
response->reduced_transaction_id());
|
||||
|
||||
MaybeUpdateLocalCandidate(request, response);
|
||||
if (request->msg()->type() == STUN_BINDING_REQUEST) {
|
||||
auto goog_misc = response->GetUInt16List(STUN_ATTR_GOOG_MISC_INFO);
|
||||
if (goog_misc != nullptr &&
|
||||
goog_misc->Size() >= kSupportGoogPingVersionIndex &&
|
||||
goog_misc->GetType(kSupportGoogPingVersionIndex) >= kGoogPingVersion) {
|
||||
// The remote peer has indicated that it supports GOOG_PING.
|
||||
remote_support_goog_ping_ = true;
|
||||
}
|
||||
|
||||
MaybeUpdateLocalCandidate(request, response);
|
||||
|
||||
if (field_trials_->enable_goog_ping && remote_support_goog_ping_) {
|
||||
cached_stun_binding_ = request->msg()->Clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::OnConnectionRequestErrorResponse(ConnectionRequest* request,
|
||||
StunMessage* response) {
|
||||
int error_code = response->GetErrorCodeValue();
|
||||
RTC_LOG(LS_WARNING) << ToString() << ": Received STUN error response id="
|
||||
<< rtc::hex_encode(request->id())
|
||||
RTC_LOG(LS_WARNING) << ToString() << ": Received "
|
||||
<< StunMethodToString(response->type())
|
||||
<< " id=" << rtc::hex_encode(request->id())
|
||||
<< " code=" << error_code
|
||||
<< " rtt=" << request->Elapsed();
|
||||
|
||||
cached_stun_binding_.reset();
|
||||
if (error_code == STUN_ERROR_UNKNOWN_ATTRIBUTE ||
|
||||
error_code == STUN_ERROR_SERVER_ERROR ||
|
||||
error_code == STUN_ERROR_UNAUTHORIZED) {
|
||||
@ -1021,6 +1087,8 @@ void Connection::OnConnectionRequestErrorResponse(ConnectionRequest* request,
|
||||
// Race failure, retry
|
||||
} else if (error_code == STUN_ERROR_ROLE_CONFLICT) {
|
||||
HandleRoleConflictFromPeer();
|
||||
} else if (request->msg()->type() == GOOG_PING_REQUEST) {
|
||||
// Race, retry.
|
||||
} else {
|
||||
// This is not a valid connection.
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
@ -1041,8 +1109,9 @@ void Connection::OnConnectionRequestTimeout(ConnectionRequest* request) {
|
||||
void Connection::OnConnectionRequestSent(ConnectionRequest* request) {
|
||||
// Log at LS_INFO if we send a ping on an unwritable connection.
|
||||
rtc::LoggingSeverity sev = !writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
|
||||
RTC_LOG_V(sev) << ToString()
|
||||
<< ": Sent STUN ping, id=" << rtc::hex_encode(request->id())
|
||||
RTC_LOG_V(sev) << ToString() << ": Sent "
|
||||
<< StunMethodToString(request->msg()->type())
|
||||
<< ", id=" << rtc::hex_encode(request->id())
|
||||
<< ", use_candidate=" << use_candidate_attr()
|
||||
<< ", nomination=" << nomination();
|
||||
stats_.sent_ping_requests_total++;
|
||||
@ -1219,6 +1288,19 @@ bool Connection::TooManyOutstandingPings(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Connection::ShouldSendGoogPing(const StunMessage* message) {
|
||||
if (remote_support_goog_ping_ && cached_stun_binding_ &&
|
||||
cached_stun_binding_->EqualAttributes(message, [](int type) {
|
||||
// Ignore these attributes.
|
||||
return type != STUN_ATTR_FINGERPRINT &&
|
||||
type != STUN_ATTR_MESSAGE_INTEGRITY &&
|
||||
type != STUN_ATTR_RETRANSMIT_COUNT;
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ProxyConnection::ProxyConnection(Port* port,
|
||||
size_t index,
|
||||
const Candidate& remote_candidate)
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#ifndef P2P_BASE_CONNECTION_H_
|
||||
#define P2P_BASE_CONNECTION_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -31,6 +32,10 @@
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// Version number for GOOG_PING, this is added to have the option of
|
||||
// adding other flavors in the future.
|
||||
constexpr int kGoogPingVersion = 1;
|
||||
|
||||
// Connection and Port has circular dependencies.
|
||||
// So we use forward declaration rather than include.
|
||||
class Port;
|
||||
@ -227,7 +232,7 @@ class Connection : public CandidatePairInterface,
|
||||
void ReceivedPing(
|
||||
const absl::optional<std::string>& request_id = absl::nullopt);
|
||||
// Handles the binding request; sends a response if this is a valid request.
|
||||
void HandleBindingRequest(IceMessage* msg);
|
||||
void HandleStunBindingOrGoogPingRequest(IceMessage* msg);
|
||||
// Handles the piggyback acknowledgement of the lastest connectivity check
|
||||
// that the remote peer has received, if it is indicated in the incoming
|
||||
// connectivity check from the peer.
|
||||
@ -298,7 +303,9 @@ class Connection : public CandidatePairInterface,
|
||||
return rtt_estimate_;
|
||||
}
|
||||
|
||||
void SendBindingResponse(const StunMessage* request);
|
||||
void SendStunBindingResponse(const StunMessage* request);
|
||||
void SendGoogPingResponse(const StunMessage* request);
|
||||
void SendResponseMessage(const StunMessage& response);
|
||||
|
||||
// An accessor for unit tests.
|
||||
Port* PortForTest() { return port_; }
|
||||
@ -368,6 +375,10 @@ class Connection : public CandidatePairInterface,
|
||||
void LogCandidatePairEvent(webrtc::IceCandidatePairEventType type,
|
||||
uint32_t transaction_id);
|
||||
|
||||
// Check if this IceMessage is identical
|
||||
// to last message ack:ed STUN_BINDING_REQUEST.
|
||||
bool ShouldSendGoogPing(const StunMessage* message);
|
||||
|
||||
WriteState write_state_;
|
||||
bool receiving_;
|
||||
bool connected_;
|
||||
@ -424,6 +435,13 @@ class Connection : public CandidatePairInterface,
|
||||
absl::optional<webrtc::IceCandidatePairDescription> log_description_;
|
||||
webrtc::IceEventLog* ice_event_log_ = nullptr;
|
||||
|
||||
// GOOG_PING_REQUEST is sent in place of STUN_BINDING_REQUEST
|
||||
// if configured via field trial, the remote peer supports it (signaled
|
||||
// in STUN_BINDING) and if the last STUN BINDING is identical to the one
|
||||
// that is about to be sent.
|
||||
absl::optional<bool> remote_support_goog_ping_;
|
||||
std::unique_ptr<StunMessage> cached_stun_binding_;
|
||||
|
||||
const IceFieldTrials* field_trials_;
|
||||
rtc::EventBasedExponentialMovingAverage rtt_estimate_;
|
||||
|
||||
|
||||
@ -641,12 +641,21 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) {
|
||||
}
|
||||
|
||||
webrtc::StructParametersParser::Create(
|
||||
// go/skylift-light
|
||||
"skip_relay_to_non_relay_connections",
|
||||
&field_trials_.skip_relay_to_non_relay_connections,
|
||||
// Limiting pings sent.
|
||||
"max_outstanding_pings", &field_trials_.max_outstanding_pings,
|
||||
// Delay initial selection of connection.
|
||||
"initial_select_dampening", &field_trials_.initial_select_dampening,
|
||||
// Delay initial selection of connections, that are receiving.
|
||||
"initial_select_dampening_ping_received",
|
||||
&field_trials_.initial_select_dampening_ping_received,
|
||||
// Reply that we support goog ping.
|
||||
"announce_goog_ping", &field_trials_.announce_goog_ping,
|
||||
// Use goog ping if remote support it.
|
||||
"enable_goog_ping", &field_trials_.enable_goog_ping,
|
||||
// How fast does a RTT sample decay.
|
||||
"rtt_estimate_halftime_ms", &field_trials_.rtt_estimate_halftime_ms)
|
||||
->Parse(webrtc::field_trial::FindFullName("WebRTC-IceFieldTrials"));
|
||||
|
||||
@ -1028,7 +1037,7 @@ void P2PTransportChannel::OnUnknownAddress(PortInterface* port,
|
||||
: "resurrected")
|
||||
<< " candidate: " << remote_candidate.ToSensitiveString();
|
||||
AddConnection(connection);
|
||||
connection->HandleBindingRequest(stun_msg);
|
||||
connection->HandleStunBindingOrGoogPingRequest(stun_msg);
|
||||
|
||||
// Update the list of connections since we just added another. We do this
|
||||
// after sending the response since it could (in principle) delete the
|
||||
|
||||
@ -32,6 +32,12 @@ struct IceFieldTrials {
|
||||
// give us chance to find a better connection before starting.
|
||||
absl::optional<int> initial_select_dampening_ping_received;
|
||||
|
||||
// Announce GOOG_PING support in STUN_BINDING_RESPONSE.
|
||||
bool announce_goog_ping = true;
|
||||
|
||||
// Enable sending GOOG_PING if remote announce it.
|
||||
bool enable_goog_ping = false;
|
||||
|
||||
// Decay rate for RTT estimate using EventBasedExponentialMovingAverage
|
||||
// expressed as halving time.
|
||||
int rtt_estimate_halftime_ms = 500;
|
||||
|
||||
@ -609,7 +609,7 @@ class P2PTransportChannelTestBase : public ::testing::Test,
|
||||
return CheckConnected(ch1, ch2) && CheckCandidatePair(ch1, ch2, from, to);
|
||||
}
|
||||
|
||||
void Test(const Result& expected) {
|
||||
virtual void Test(const Result& expected) {
|
||||
rtc::ScopedFakeClock clock;
|
||||
int64_t connect_start = rtc::TimeMillis();
|
||||
int64_t connect_time;
|
||||
@ -1195,16 +1195,26 @@ const P2PTransportChannelTest::Result*
|
||||
LTRT},
|
||||
};
|
||||
|
||||
class P2PTransportChannelTestWithFieldTrials
|
||||
: public P2PTransportChannelTest,
|
||||
public ::testing::WithParamInterface<std::string> {
|
||||
public:
|
||||
void Test(const Result& expected) override {
|
||||
webrtc::test::ScopedFieldTrials field_trials(GetParam());
|
||||
P2PTransportChannelTest::Test(expected);
|
||||
}
|
||||
};
|
||||
|
||||
// The actual tests that exercise all the various configurations.
|
||||
// Test names are of the form P2PTransportChannelTest_TestOPENToNAT_FULL_CONE
|
||||
#define P2P_TEST_DECLARATION(x, y, z) \
|
||||
TEST_F(P2PTransportChannelTest, z##Test##x##To##y) { \
|
||||
ConfigureEndpoints(x, y, PORTALLOCATOR_ENABLE_SHARED_SOCKET, \
|
||||
PORTALLOCATOR_ENABLE_SHARED_SOCKET); \
|
||||
if (kMatrix[x][y] != NULL) \
|
||||
Test(*kMatrix[x][y]); \
|
||||
else \
|
||||
RTC_LOG(LS_WARNING) << "Not yet implemented"; \
|
||||
#define P2P_TEST_DECLARATION(x, y, z) \
|
||||
TEST_P(P2PTransportChannelTestWithFieldTrials, z##Test##x##To##y) { \
|
||||
ConfigureEndpoints(x, y, PORTALLOCATOR_ENABLE_SHARED_SOCKET, \
|
||||
PORTALLOCATOR_ENABLE_SHARED_SOCKET); \
|
||||
if (kMatrix[x][y] != NULL) \
|
||||
Test(*kMatrix[x][y]); \
|
||||
else \
|
||||
RTC_LOG(LS_WARNING) << "Not yet implemented"; \
|
||||
}
|
||||
|
||||
#define P2P_TEST(x, y) P2P_TEST_DECLARATION(x, y, /* empty argument */)
|
||||
@ -1236,6 +1246,12 @@ P2P_TEST_SET(BLOCK_ALL_BUT_OUTGOING_HTTP)
|
||||
P2P_TEST_SET(PROXY_HTTPS)
|
||||
P2P_TEST_SET(PROXY_SOCKS)
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
P2PTransportChannelTestWithFieldTrials,
|
||||
P2PTransportChannelTestWithFieldTrials,
|
||||
// Each field-trial is ~144 tests (some return not-yet-implemented).
|
||||
testing::Values("", "WebRTC-IceFieldTrials/enable_goog_ping:true/"));
|
||||
|
||||
// Test that we restart candidate allocation when local ufrag&pwd changed.
|
||||
// Standard Ice protocol is used.
|
||||
TEST_F(P2PTransportChannelTest, HandleUfragPwdChange) {
|
||||
|
||||
108
p2p/base/port.cc
108
p2p/base/port.cc
@ -392,8 +392,8 @@ void Port::OnReadPacket(const char* data,
|
||||
} else if (!msg) {
|
||||
// STUN message handled already
|
||||
} else if (msg->type() == STUN_BINDING_REQUEST) {
|
||||
RTC_LOG(LS_INFO) << "Received STUN ping id="
|
||||
<< rtc::hex_encode(msg->transaction_id())
|
||||
RTC_LOG(LS_INFO) << "Received " << StunMethodToString(msg->type())
|
||||
<< " id=" << rtc::hex_encode(msg->transaction_id())
|
||||
<< " from unknown address " << addr.ToSensitiveString();
|
||||
// We need to signal an unknown address before we handle any role conflict
|
||||
// below. Otherwise there would be no candidate pair and TURN entry created
|
||||
@ -404,12 +404,20 @@ void Port::OnReadPacket(const char* data,
|
||||
RTC_LOG(LS_INFO) << "Received conflicting role from the peer.";
|
||||
return;
|
||||
}
|
||||
} else if (msg->type() == GOOG_PING_REQUEST) {
|
||||
// This is a PING sent to a connection that was destroyed.
|
||||
// Send back that this is the case and a authenticated BINDING
|
||||
// is needed.
|
||||
SendBindingErrorResponse(msg.get(), addr, STUN_ERROR_BAD_REQUEST,
|
||||
STUN_ERROR_REASON_BAD_REQUEST);
|
||||
} else {
|
||||
// NOTE(tschmelcher): STUN_BINDING_RESPONSE is benign. It occurs if we
|
||||
// pruned a connection for this port while it had STUN requests in flight,
|
||||
// because we then get back responses for them, which this code correctly
|
||||
// does not handle.
|
||||
if (msg->type() != STUN_BINDING_RESPONSE) {
|
||||
if (msg->type() != STUN_BINDING_RESPONSE &&
|
||||
msg->type() != GOOG_PING_RESPONSE &&
|
||||
msg->type() != GOOG_PING_ERROR_RESPONSE) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Received unexpected STUN message type: "
|
||||
<< msg->type() << " from unknown address: "
|
||||
@ -444,7 +452,11 @@ bool Port::GetStunMessage(const char* data,
|
||||
|
||||
// Don't bother parsing the packet if we can tell it's not STUN.
|
||||
// In ICE mode, all STUN packets will have a valid fingerprint.
|
||||
if (!StunMessage::ValidateFingerprint(data, size)) {
|
||||
// Except GOOG_PING_REQUEST/RESPONSE that does not send fingerprint.
|
||||
int types[] = {GOOG_PING_REQUEST, GOOG_PING_RESPONSE,
|
||||
GOOG_PING_ERROR_RESPONSE};
|
||||
if (!StunMessage::IsStunMethod(types, data, size) &&
|
||||
!StunMessage::ValidateFingerprint(data, size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -461,8 +473,9 @@ bool Port::GetStunMessage(const char* data,
|
||||
// If not present, fail with a 400 Bad Request.
|
||||
if (!stun_msg->GetByteString(STUN_ATTR_USERNAME) ||
|
||||
!stun_msg->GetByteString(STUN_ATTR_MESSAGE_INTEGRITY)) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Received STUN request without username/M-I from: "
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type())
|
||||
<< " without username/M-I from: "
|
||||
<< addr.ToSensitiveString();
|
||||
SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_BAD_REQUEST,
|
||||
STUN_ERROR_REASON_BAD_REQUEST);
|
||||
@ -474,9 +487,10 @@ bool Port::GetStunMessage(const char* data,
|
||||
std::string remote_ufrag;
|
||||
if (!ParseStunUsername(stun_msg.get(), &local_ufrag, &remote_ufrag) ||
|
||||
local_ufrag != username_fragment()) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Received STUN request with bad local username "
|
||||
<< local_ufrag << " from " << addr.ToSensitiveString();
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type())
|
||||
<< " with bad local username " << local_ufrag
|
||||
<< " from " << addr.ToSensitiveString();
|
||||
SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_UNAUTHORIZED,
|
||||
STUN_ERROR_REASON_UNAUTHORIZED);
|
||||
return true;
|
||||
@ -484,9 +498,9 @@ bool Port::GetStunMessage(const char* data,
|
||||
|
||||
// If ICE, and the MESSAGE-INTEGRITY is bad, fail with a 401 Unauthorized
|
||||
if (!stun_msg->ValidateMessageIntegrity(data, size, password_)) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Received STUN request with bad M-I from "
|
||||
<< addr.ToSensitiveString()
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type())
|
||||
<< " with bad M-I from " << addr.ToSensitiveString()
|
||||
<< ", password_=" << password_;
|
||||
SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_UNAUTHORIZED,
|
||||
STUN_ERROR_REASON_UNAUTHORIZED);
|
||||
@ -497,30 +511,51 @@ bool Port::GetStunMessage(const char* data,
|
||||
(stun_msg->type() == STUN_BINDING_ERROR_RESPONSE)) {
|
||||
if (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE) {
|
||||
if (const StunErrorCodeAttribute* error_code = stun_msg->GetErrorCode()) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Received STUN binding error: class="
|
||||
<< error_code->eclass()
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type())
|
||||
<< ": class=" << error_code->eclass()
|
||||
<< " number=" << error_code->number() << " reason='"
|
||||
<< error_code->reason() << "' from "
|
||||
<< addr.ToSensitiveString();
|
||||
// Return message to allow error-specific processing
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR)
|
||||
<< ToString()
|
||||
<< ": Received STUN binding error without a error code from "
|
||||
<< addr.ToSensitiveString();
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type())
|
||||
<< " without a error code from "
|
||||
<< addr.ToSensitiveString();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// NOTE: Username should not be used in verifying response messages.
|
||||
out_username->clear();
|
||||
} else if (stun_msg->type() == STUN_BINDING_INDICATION) {
|
||||
RTC_LOG(LS_VERBOSE) << ToString()
|
||||
<< ": Received STUN binding indication: from "
|
||||
RTC_LOG(LS_VERBOSE) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type()) << ": from "
|
||||
<< addr.ToSensitiveString();
|
||||
out_username->clear();
|
||||
// No stun attributes will be verified, if it's stun indication message.
|
||||
// Returning from end of the this method.
|
||||
} else if (stun_msg->type() == GOOG_PING_REQUEST) {
|
||||
if (!stun_msg->ValidateMessageIntegrity32(data, size, password_)) {
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type())
|
||||
<< " with bad M-I from " << addr.ToSensitiveString()
|
||||
<< ", password_=" << password_;
|
||||
SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_UNAUTHORIZED,
|
||||
STUN_ERROR_REASON_UNAUTHORIZED);
|
||||
return true;
|
||||
}
|
||||
RTC_LOG(LS_VERBOSE) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type()) << " from "
|
||||
<< addr.ToSensitiveString();
|
||||
out_username->clear();
|
||||
} else if (stun_msg->type() == GOOG_PING_RESPONSE ||
|
||||
stun_msg->type() == GOOG_PING_ERROR_RESPONSE) {
|
||||
// note: the MessageIntegrity32 will be verified in Connection.cc
|
||||
RTC_LOG(LS_VERBOSE) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type()) << " from "
|
||||
<< addr.ToSensitiveString();
|
||||
out_username->clear();
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Received STUN packet with invalid type ("
|
||||
@ -665,11 +700,16 @@ void Port::SendBindingErrorResponse(StunMessage* request,
|
||||
const rtc::SocketAddress& addr,
|
||||
int error_code,
|
||||
const std::string& reason) {
|
||||
RTC_DCHECK(request->type() == STUN_BINDING_REQUEST);
|
||||
RTC_DCHECK(request->type() == STUN_BINDING_REQUEST ||
|
||||
request->type() == GOOG_PING_REQUEST);
|
||||
|
||||
// Fill in the response message.
|
||||
StunMessage response;
|
||||
response.SetType(STUN_BINDING_ERROR_RESPONSE);
|
||||
if (request->type() == STUN_BINDING_REQUEST) {
|
||||
response.SetType(STUN_BINDING_ERROR_RESPONSE);
|
||||
} else {
|
||||
response.SetType(GOOG_PING_ERROR_RESPONSE);
|
||||
}
|
||||
response.SetTransactionID(request->transaction_id());
|
||||
|
||||
// When doing GICE, we need to write out the error code incorrectly to
|
||||
@ -682,9 +722,18 @@ void Port::SendBindingErrorResponse(StunMessage* request,
|
||||
// Per Section 10.1.2, certain error cases don't get a MESSAGE-INTEGRITY,
|
||||
// because we don't have enough information to determine the shared secret.
|
||||
if (error_code != STUN_ERROR_BAD_REQUEST &&
|
||||
error_code != STUN_ERROR_UNAUTHORIZED)
|
||||
response.AddMessageIntegrity(password_);
|
||||
response.AddFingerprint();
|
||||
error_code != STUN_ERROR_UNAUTHORIZED &&
|
||||
request->type() != GOOG_PING_REQUEST) {
|
||||
if (request->type() == STUN_BINDING_REQUEST) {
|
||||
response.AddMessageIntegrity(password_);
|
||||
} else {
|
||||
response.AddMessageIntegrity32(password_);
|
||||
}
|
||||
}
|
||||
|
||||
if (request->type() == STUN_BINDING_REQUEST) {
|
||||
response.AddFingerprint();
|
||||
}
|
||||
|
||||
// Send the response message.
|
||||
rtc::ByteBufferWriter buf;
|
||||
@ -693,9 +742,10 @@ void Port::SendBindingErrorResponse(StunMessage* request,
|
||||
options.info_signaled_after_sent.packet_type =
|
||||
rtc::PacketType::kIceConnectivityCheckResponse;
|
||||
SendTo(buf.Data(), buf.Length(), addr, options, false);
|
||||
RTC_LOG(LS_INFO) << ToString()
|
||||
<< ": Sending STUN binding error: reason=" << reason
|
||||
<< " to " << addr.ToSensitiveString();
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Sending STUN "
|
||||
<< StunMethodToString(response.type())
|
||||
<< ": reason=" << reason << " to "
|
||||
<< addr.ToSensitiveString();
|
||||
}
|
||||
|
||||
void Port::KeepAliveUntilPruned() {
|
||||
|
||||
@ -302,7 +302,7 @@ class TestChannel : public sigslot::has_slots<> {
|
||||
c.set_address(remote_address_);
|
||||
conn_ = port_->CreateConnection(c, Port::ORIGIN_MESSAGE);
|
||||
conn_->SignalDestroyed.connect(this, &TestChannel::OnDestroyed);
|
||||
conn_->SendBindingResponse(remote_request_.get());
|
||||
conn_->SendStunBindingResponse(remote_request_.get());
|
||||
remote_request_.reset();
|
||||
}
|
||||
void Ping() { Ping(0); }
|
||||
@ -406,6 +406,8 @@ class PortTest : public ::testing::Test, public sigslot::has_slots<> {
|
||||
ports_destroyed_(0) {}
|
||||
|
||||
protected:
|
||||
std::string password() { return password_; }
|
||||
|
||||
void TestLocalToLocal() {
|
||||
auto port1 = CreateUdpPort(kLocalAddr1);
|
||||
port1->SetIceRole(cricket::ICEROLE_CONTROLLING);
|
||||
@ -2621,7 +2623,7 @@ TEST_F(PortTest, TestIceLiteConnectivity) {
|
||||
auto* con = ice_lite_port->CreateConnection(
|
||||
ice_full_port_ptr->Candidates()[0], cricket::Port::ORIGIN_MESSAGE);
|
||||
std::unique_ptr<IceMessage> request = CopyStunMessage(*msg);
|
||||
con->SendBindingResponse(request.get());
|
||||
con->SendStunBindingResponse(request.get());
|
||||
|
||||
// Feeding the respone message from litemode to the full mode connection.
|
||||
ch1.conn()->OnReadPacket(ice_lite_port->last_stun_buf()->data<char>(),
|
||||
@ -2643,6 +2645,305 @@ TEST_F(PortTest, TestIceLiteConnectivity) {
|
||||
ch1.Stop();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Utility function for testing goog ping.
|
||||
absl::optional<int> GetSupportedGoogPingVersion(const StunMessage* response) {
|
||||
auto goog_misc = response->GetUInt16List(STUN_ATTR_GOOG_MISC_INFO);
|
||||
if (goog_misc == nullptr) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
if (goog_misc->Size() <
|
||||
static_cast<int>(cricket::IceGoogMiscInfoBindingResponseAttributeIndex::
|
||||
SUPPORT_GOOG_PING_VERSION)) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
return goog_misc->GetType(
|
||||
static_cast<int>(cricket::IceGoogMiscInfoBindingResponseAttributeIndex::
|
||||
SUPPORT_GOOG_PING_VERSION));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class GoogPingTest
|
||||
: public PortTest,
|
||||
public ::testing::WithParamInterface<std::pair<bool, bool>> {};
|
||||
|
||||
// This test verifies the announce/enable on/off behavior
|
||||
TEST_P(GoogPingTest, TestGoogPingAnnounceEnable) {
|
||||
IceFieldTrials trials;
|
||||
trials.announce_goog_ping = GetParam().first;
|
||||
trials.enable_goog_ping = GetParam().second;
|
||||
RTC_LOG(LS_INFO) << "Testing combination: "
|
||||
<< " announce: " << trials.announce_goog_ping
|
||||
<< " enable:" << trials.enable_goog_ping;
|
||||
|
||||
auto port1_unique =
|
||||
CreateTestPort(kLocalAddr1, "lfrag", "lpass",
|
||||
cricket::ICEROLE_CONTROLLING, kTiebreaker1);
|
||||
auto* port1 = port1_unique.get();
|
||||
auto port2 = CreateTestPort(kLocalAddr2, "rfrag", "rpass",
|
||||
cricket::ICEROLE_CONTROLLED, kTiebreaker2);
|
||||
|
||||
TestChannel ch1(std::move(port1_unique));
|
||||
// Block usage of STUN_ATTR_USE_CANDIDATE so that
|
||||
// ch1.conn() will sent GOOG_PING_REQUEST directly.
|
||||
// This only makes test a bit shorter...
|
||||
ch1.SetIceMode(ICEMODE_LITE);
|
||||
// Start gathering candidates.
|
||||
ch1.Start();
|
||||
port2->PrepareAddress();
|
||||
|
||||
ASSERT_EQ_WAIT(1, ch1.complete_count(), kDefaultTimeout);
|
||||
ASSERT_FALSE(port2->Candidates().empty());
|
||||
|
||||
ch1.CreateConnection(GetCandidate(port2.get()));
|
||||
ASSERT_TRUE(ch1.conn() != NULL);
|
||||
EXPECT_EQ(Connection::STATE_WRITE_INIT, ch1.conn()->write_state());
|
||||
ch1.conn()->SetIceFieldTrials(&trials);
|
||||
|
||||
// Send ping.
|
||||
ch1.Ping();
|
||||
|
||||
ASSERT_TRUE_WAIT(port1->last_stun_msg() != NULL, kDefaultTimeout);
|
||||
const IceMessage* request1 = port1->last_stun_msg();
|
||||
auto* con = port2->CreateConnection(port1->Candidates()[0],
|
||||
cricket::Port::ORIGIN_MESSAGE);
|
||||
con->SetIceFieldTrials(&trials);
|
||||
|
||||
con->SendStunBindingResponse(request1);
|
||||
|
||||
// Then check the response matches the settings.
|
||||
const auto* response = port2->last_stun_msg();
|
||||
ASSERT_EQ(response->type(), STUN_BINDING_RESPONSE);
|
||||
ASSERT_EQ(trials.announce_goog_ping,
|
||||
GetSupportedGoogPingVersion(response) &&
|
||||
GetSupportedGoogPingVersion(response) >= kGoogPingVersion);
|
||||
|
||||
// Feeding the respone message back.
|
||||
ch1.conn()->OnReadPacket(port2->last_stun_buf()->data<char>(),
|
||||
port2->last_stun_buf()->size(),
|
||||
/* packet_time_us */ -1);
|
||||
|
||||
port1->Reset();
|
||||
port2->Reset();
|
||||
|
||||
ch1.Ping();
|
||||
ASSERT_TRUE_WAIT(port1->last_stun_msg() != NULL, kDefaultTimeout);
|
||||
const IceMessage* request2 = port1->last_stun_msg();
|
||||
|
||||
// It should be a GOOG_PING if both of these are TRUE
|
||||
if (trials.announce_goog_ping && trials.enable_goog_ping) {
|
||||
ASSERT_EQ(request2->type(), GOOG_PING_REQUEST);
|
||||
con->SendGoogPingResponse(request2);
|
||||
} else {
|
||||
ASSERT_EQ(request2->type(), STUN_BINDING_REQUEST);
|
||||
con->SendStunBindingResponse(request2);
|
||||
}
|
||||
|
||||
const auto* response2 = port2->last_stun_msg();
|
||||
ASSERT_TRUE(response2 != nullptr);
|
||||
|
||||
// It should be a GOOG_PING_RESPONSE if both of these are TRUE
|
||||
if (trials.announce_goog_ping && trials.enable_goog_ping) {
|
||||
ASSERT_EQ(response2->type(), GOOG_PING_RESPONSE);
|
||||
} else {
|
||||
ASSERT_EQ(response2->type(), STUN_BINDING_RESPONSE);
|
||||
}
|
||||
|
||||
ch1.Stop();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(GoogPingTest,
|
||||
GoogPingTest,
|
||||
// test all combinations of <announce, enable> pairs.
|
||||
::testing::Values(std::make_pair(false, false),
|
||||
std::make_pair(true, false),
|
||||
std::make_pair(false, true),
|
||||
std::make_pair(true, true)));
|
||||
|
||||
// This test checks that a change in attributes falls back to STUN_BINDING
|
||||
TEST_F(PortTest, TestChangeInAttributeMakesGoogPingFallsbackToStunBinding) {
|
||||
IceFieldTrials trials;
|
||||
trials.announce_goog_ping = true;
|
||||
trials.enable_goog_ping = true;
|
||||
|
||||
auto port1_unique =
|
||||
CreateTestPort(kLocalAddr1, "lfrag", "lpass",
|
||||
cricket::ICEROLE_CONTROLLING, kTiebreaker1);
|
||||
auto* port1 = port1_unique.get();
|
||||
auto port2 = CreateTestPort(kLocalAddr2, "rfrag", "rpass",
|
||||
cricket::ICEROLE_CONTROLLED, kTiebreaker2);
|
||||
|
||||
TestChannel ch1(std::move(port1_unique));
|
||||
// Block usage of STUN_ATTR_USE_CANDIDATE so that
|
||||
// ch1.conn() will sent GOOG_PING_REQUEST directly.
|
||||
// This only makes test a bit shorter...
|
||||
ch1.SetIceMode(ICEMODE_LITE);
|
||||
// Start gathering candidates.
|
||||
ch1.Start();
|
||||
port2->PrepareAddress();
|
||||
|
||||
ASSERT_EQ_WAIT(1, ch1.complete_count(), kDefaultTimeout);
|
||||
ASSERT_FALSE(port2->Candidates().empty());
|
||||
|
||||
ch1.CreateConnection(GetCandidate(port2.get()));
|
||||
ASSERT_TRUE(ch1.conn() != nullptr);
|
||||
EXPECT_EQ(Connection::STATE_WRITE_INIT, ch1.conn()->write_state());
|
||||
ch1.conn()->SetIceFieldTrials(&trials);
|
||||
|
||||
// Send ping.
|
||||
ch1.Ping();
|
||||
|
||||
ASSERT_TRUE_WAIT(port1->last_stun_msg() != NULL, kDefaultTimeout);
|
||||
const IceMessage* msg = port1->last_stun_msg();
|
||||
auto* con = port2->CreateConnection(port1->Candidates()[0],
|
||||
cricket::Port::ORIGIN_MESSAGE);
|
||||
con->SetIceFieldTrials(&trials);
|
||||
|
||||
// Feed the message into the connection.
|
||||
con->SendStunBindingResponse(msg);
|
||||
|
||||
// The check reply wrt to settings.
|
||||
const auto* response = port2->last_stun_msg();
|
||||
ASSERT_EQ(response->type(), STUN_BINDING_RESPONSE);
|
||||
ASSERT_TRUE(GetSupportedGoogPingVersion(response) >= kGoogPingVersion);
|
||||
|
||||
// Feeding the respone message back.
|
||||
ch1.conn()->OnReadPacket(port2->last_stun_buf()->data<char>(),
|
||||
port2->last_stun_buf()->size(),
|
||||
/* packet_time_us */ -1);
|
||||
|
||||
port1->Reset();
|
||||
port2->Reset();
|
||||
|
||||
ch1.Ping();
|
||||
ASSERT_TRUE_WAIT(port1->last_stun_msg() != NULL, kDefaultTimeout);
|
||||
const IceMessage* msg2 = port1->last_stun_msg();
|
||||
|
||||
// It should be a GOOG_PING if both of these are TRUE
|
||||
ASSERT_EQ(msg2->type(), GOOG_PING_REQUEST);
|
||||
con->SendGoogPingResponse(msg2);
|
||||
|
||||
const auto* response2 = port2->last_stun_msg();
|
||||
ASSERT_TRUE(response2 != nullptr);
|
||||
|
||||
// It should be a GOOG_PING_RESPONSE.
|
||||
ASSERT_EQ(response2->type(), GOOG_PING_RESPONSE);
|
||||
|
||||
// And now the third ping.
|
||||
port1->Reset();
|
||||
port2->Reset();
|
||||
|
||||
// Modify the message to be sent.
|
||||
ch1.conn()->set_use_candidate_attr(!ch1.conn()->use_candidate_attr());
|
||||
|
||||
ch1.Ping();
|
||||
ASSERT_TRUE_WAIT(port1->last_stun_msg() != NULL, kDefaultTimeout);
|
||||
const IceMessage* msg3 = port1->last_stun_msg();
|
||||
|
||||
// It should be a STUN_BINDING_REQUEST
|
||||
ASSERT_EQ(msg3->type(), STUN_BINDING_REQUEST);
|
||||
|
||||
ch1.Stop();
|
||||
}
|
||||
|
||||
// This test that an error response fall back to STUN_BINDING.
|
||||
TEST_F(PortTest, TestErrorResponseMakesGoogPingFallBackToStunBinding) {
|
||||
IceFieldTrials trials;
|
||||
trials.announce_goog_ping = true;
|
||||
trials.enable_goog_ping = true;
|
||||
|
||||
auto port1_unique =
|
||||
CreateTestPort(kLocalAddr1, "lfrag", "lpass",
|
||||
cricket::ICEROLE_CONTROLLING, kTiebreaker1);
|
||||
auto* port1 = port1_unique.get();
|
||||
auto port2 = CreateTestPort(kLocalAddr2, "rfrag", "rpass",
|
||||
cricket::ICEROLE_CONTROLLED, kTiebreaker2);
|
||||
|
||||
TestChannel ch1(std::move(port1_unique));
|
||||
// Block usage of STUN_ATTR_USE_CANDIDATE so that
|
||||
// ch1.conn() will sent GOOG_PING_REQUEST directly.
|
||||
// This only makes test a bit shorter...
|
||||
ch1.SetIceMode(ICEMODE_LITE);
|
||||
// Start gathering candidates.
|
||||
ch1.Start();
|
||||
port2->PrepareAddress();
|
||||
|
||||
ASSERT_EQ_WAIT(1, ch1.complete_count(), kDefaultTimeout);
|
||||
ASSERT_FALSE(port2->Candidates().empty());
|
||||
|
||||
ch1.CreateConnection(GetCandidate(port2.get()));
|
||||
ASSERT_TRUE(ch1.conn() != NULL);
|
||||
EXPECT_EQ(Connection::STATE_WRITE_INIT, ch1.conn()->write_state());
|
||||
ch1.conn()->SetIceFieldTrials(&trials);
|
||||
|
||||
// Send ping.
|
||||
ch1.Ping();
|
||||
|
||||
ASSERT_TRUE_WAIT(port1->last_stun_msg() != NULL, kDefaultTimeout);
|
||||
const IceMessage* msg = port1->last_stun_msg();
|
||||
auto* con = port2->CreateConnection(port1->Candidates()[0],
|
||||
cricket::Port::ORIGIN_MESSAGE);
|
||||
con->SetIceFieldTrials(&trials);
|
||||
|
||||
// Feed the message into the connection.
|
||||
con->SendStunBindingResponse(msg);
|
||||
|
||||
// The check reply wrt to settings.
|
||||
const auto* response = port2->last_stun_msg();
|
||||
ASSERT_EQ(response->type(), STUN_BINDING_RESPONSE);
|
||||
ASSERT_TRUE(GetSupportedGoogPingVersion(response) >= kGoogPingVersion);
|
||||
|
||||
// Feeding the respone message back.
|
||||
ch1.conn()->OnReadPacket(port2->last_stun_buf()->data<char>(),
|
||||
port2->last_stun_buf()->size(),
|
||||
/* packet_time_us */ -1);
|
||||
|
||||
port1->Reset();
|
||||
port2->Reset();
|
||||
|
||||
ch1.Ping();
|
||||
ASSERT_TRUE_WAIT(port1->last_stun_msg() != NULL, kDefaultTimeout);
|
||||
const IceMessage* msg2 = port1->last_stun_msg();
|
||||
|
||||
// It should be a GOOG_PING.
|
||||
ASSERT_EQ(msg2->type(), GOOG_PING_REQUEST);
|
||||
con->SendGoogPingResponse(msg2);
|
||||
|
||||
const auto* response2 = port2->last_stun_msg();
|
||||
ASSERT_TRUE(response2 != nullptr);
|
||||
|
||||
// It should be a GOOG_PING_RESPONSE.
|
||||
ASSERT_EQ(response2->type(), GOOG_PING_RESPONSE);
|
||||
|
||||
// But rather than the RESPONSE...feedback an error.
|
||||
StunMessage error_response;
|
||||
error_response.SetType(GOOG_PING_ERROR_RESPONSE);
|
||||
error_response.SetTransactionID(response2->transaction_id());
|
||||
error_response.AddMessageIntegrity32("rpass");
|
||||
rtc::ByteBufferWriter buf;
|
||||
error_response.Write(&buf);
|
||||
|
||||
ch1.conn()->OnReadPacket(buf.Data(), buf.Length(),
|
||||
/* packet_time_us */ -1);
|
||||
|
||||
// And now the third ping...this should be a binding.
|
||||
port1->Reset();
|
||||
port2->Reset();
|
||||
|
||||
ch1.Ping();
|
||||
ASSERT_TRUE_WAIT(port1->last_stun_msg() != NULL, kDefaultTimeout);
|
||||
const IceMessage* msg3 = port1->last_stun_msg();
|
||||
|
||||
// It should be a STUN_BINDING_REQUEST
|
||||
ASSERT_EQ(msg3->type(), STUN_BINDING_REQUEST);
|
||||
|
||||
ch1.Stop();
|
||||
}
|
||||
|
||||
// This test case verifies that both the controlling port and the controlled
|
||||
// port will time out after connectivity is lost, if they are not marked as
|
||||
// "keep alive until pruned."
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user