blob: 9065d1f5b73dd830dc3e27b4ce9b26e2412108fe [file] [log] [blame]
/*
* Copyright 2009 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/p2p/base/p2ptransportchannel.h"
#include "webrtc/p2p/base/testrelayserver.h"
#include "webrtc/p2p/base/teststunserver.h"
#include "webrtc/p2p/base/testturnserver.h"
#include "webrtc/p2p/client/basicportallocator.h"
#include "webrtc/p2p/client/fakeportallocator.h"
#include "webrtc/base/dscp.h"
#include "webrtc/base/fakenetwork.h"
#include "webrtc/base/firewallsocketserver.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/natserver.h"
#include "webrtc/base/natsocketfactory.h"
#include "webrtc/base/physicalsocketserver.h"
#include "webrtc/base/proxyserver.h"
#include "webrtc/base/socketaddress.h"
#include "webrtc/base/ssladapter.h"
#include "webrtc/base/thread.h"
#include "webrtc/base/virtualsocketserver.h"
using cricket::kDefaultPortAllocatorFlags;
using cricket::kMinimumStepDelay;
using cricket::kDefaultStepDelay;
using cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG;
using cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET;
using cricket::ServerAddresses;
using rtc::SocketAddress;
static const int kDefaultTimeout = 1000;
static const int kOnlyLocalPorts = cricket::PORTALLOCATOR_DISABLE_STUN |
cricket::PORTALLOCATOR_DISABLE_RELAY |
cricket::PORTALLOCATOR_DISABLE_TCP;
// Addresses on the public internet.
static const SocketAddress kPublicAddrs[2] =
{ SocketAddress("11.11.11.11", 0), SocketAddress("22.22.22.22", 0) };
// IPv6 Addresses on the public internet.
static const SocketAddress kIPv6PublicAddrs[2] = {
SocketAddress("2400:4030:1:2c00:be30:abcd:efab:cdef", 0),
SocketAddress("2620:0:1000:1b03:2e41:38ff:fea6:f2a4", 0)
};
// For configuring multihomed clients.
static const SocketAddress kAlternateAddrs[2] =
{ SocketAddress("11.11.11.101", 0), SocketAddress("22.22.22.202", 0) };
// Addresses for HTTP proxy servers.
static const SocketAddress kHttpsProxyAddrs[2] =
{ SocketAddress("11.11.11.1", 443), SocketAddress("22.22.22.1", 443) };
// Addresses for SOCKS proxy servers.
static const SocketAddress kSocksProxyAddrs[2] =
{ SocketAddress("11.11.11.1", 1080), SocketAddress("22.22.22.1", 1080) };
// Internal addresses for NAT boxes.
static const SocketAddress kNatAddrs[2] =
{ SocketAddress("192.168.1.1", 0), SocketAddress("192.168.2.1", 0) };
// Private addresses inside the NAT private networks.
static const SocketAddress kPrivateAddrs[2] =
{ SocketAddress("192.168.1.11", 0), SocketAddress("192.168.2.22", 0) };
// For cascaded NATs, the internal addresses of the inner NAT boxes.
static const SocketAddress kCascadedNatAddrs[2] =
{ SocketAddress("192.168.10.1", 0), SocketAddress("192.168.20.1", 0) };
// For cascaded NATs, private addresses inside the inner private networks.
static const SocketAddress kCascadedPrivateAddrs[2] =
{ SocketAddress("192.168.10.11", 0), SocketAddress("192.168.20.22", 0) };
// The address of the public STUN server.
static const SocketAddress kStunAddr("99.99.99.1", cricket::STUN_SERVER_PORT);
// The addresses for the public relay server.
static const SocketAddress kRelayUdpIntAddr("99.99.99.2", 5000);
static const SocketAddress kRelayUdpExtAddr("99.99.99.3", 5001);
static const SocketAddress kRelayTcpIntAddr("99.99.99.2", 5002);
static const SocketAddress kRelayTcpExtAddr("99.99.99.3", 5003);
static const SocketAddress kRelaySslTcpIntAddr("99.99.99.2", 5004);
static const SocketAddress kRelaySslTcpExtAddr("99.99.99.3", 5005);
// The addresses for the public turn server.
static const SocketAddress kTurnUdpIntAddr("99.99.99.4",
cricket::STUN_SERVER_PORT);
static const SocketAddress kTurnUdpExtAddr("99.99.99.5", 0);
static const cricket::RelayCredentials kRelayCredentials("test", "test");
// Based on ICE_UFRAG_LENGTH
static const char* kIceUfrag[4] = {"TESTICEUFRAG0000", "TESTICEUFRAG0001",
"TESTICEUFRAG0002", "TESTICEUFRAG0003"};
// Based on ICE_PWD_LENGTH
static const char* kIcePwd[4] = {"TESTICEPWD00000000000000",
"TESTICEPWD00000000000001",
"TESTICEPWD00000000000002",
"TESTICEPWD00000000000003"};
static const uint64 kTiebreaker1 = 11111;
static const uint64 kTiebreaker2 = 22222;
enum {
MSG_CANDIDATE
};
// This test simulates 2 P2P endpoints that want to establish connectivity
// with each other over various network topologies and conditions, which can be
// specified in each individial test.
// A virtual network (via VirtualSocketServer) along with virtual firewalls and
// NATs (via Firewall/NATSocketServer) are used to simulate the various network
// conditions. We can configure the IP addresses of the endpoints,
// block various types of connectivity, or add arbitrary levels of NAT.
// We also run a STUN server and a relay server on the virtual network to allow
// our typical P2P mechanisms to do their thing.
// For each case, we expect the P2P stack to eventually settle on a specific
// form of connectivity to the other side. The test checks that the P2P
// negotiation successfully establishes connectivity within a certain time,
// and that the result is what we expect.
// Note that this class is a base class for use by other tests, who will provide
// specialized test behavior.
class P2PTransportChannelTestBase : public testing::Test,
public rtc::MessageHandler,
public sigslot::has_slots<> {
public:
P2PTransportChannelTestBase()
: main_(rtc::Thread::Current()),
pss_(new rtc::PhysicalSocketServer),
vss_(new rtc::VirtualSocketServer(pss_.get())),
nss_(new rtc::NATSocketServer(vss_.get())),
ss_(new rtc::FirewallSocketServer(nss_.get())),
ss_scope_(ss_.get()),
stun_server_(cricket::TestStunServer::Create(main_, kStunAddr)),
turn_server_(main_, kTurnUdpIntAddr, kTurnUdpExtAddr),
relay_server_(main_, kRelayUdpIntAddr, kRelayUdpExtAddr,
kRelayTcpIntAddr, kRelayTcpExtAddr,
kRelaySslTcpIntAddr, kRelaySslTcpExtAddr),
socks_server1_(ss_.get(), kSocksProxyAddrs[0],
ss_.get(), kSocksProxyAddrs[0]),
socks_server2_(ss_.get(), kSocksProxyAddrs[1],
ss_.get(), kSocksProxyAddrs[1]),
clear_remote_candidates_ufrag_pwd_(false),
force_relay_(false) {
ep1_.role_ = cricket::ICEROLE_CONTROLLING;
ep2_.role_ = cricket::ICEROLE_CONTROLLED;
ServerAddresses stun_servers;
stun_servers.insert(kStunAddr);
ep1_.allocator_.reset(new cricket::BasicPortAllocator(
&ep1_.network_manager_,
stun_servers, kRelayUdpIntAddr, kRelayTcpIntAddr, kRelaySslTcpIntAddr));
ep2_.allocator_.reset(new cricket::BasicPortAllocator(
&ep2_.network_manager_,
stun_servers, kRelayUdpIntAddr, kRelayTcpIntAddr, kRelaySslTcpIntAddr));
}
protected:
enum Config {
OPEN, // Open to the Internet
NAT_FULL_CONE, // NAT, no filtering
NAT_ADDR_RESTRICTED, // NAT, must send to an addr to recv
NAT_PORT_RESTRICTED, // NAT, must send to an addr+port to recv
NAT_SYMMETRIC, // NAT, endpoint-dependent bindings
NAT_DOUBLE_CONE, // Double NAT, both cone
NAT_SYMMETRIC_THEN_CONE, // Double NAT, symmetric outer, cone inner
BLOCK_UDP, // Firewall, UDP in/out blocked
BLOCK_UDP_AND_INCOMING_TCP, // Firewall, UDP in/out and TCP in blocked
BLOCK_ALL_BUT_OUTGOING_HTTP, // Firewall, only TCP out on 80/443
PROXY_HTTPS, // All traffic through HTTPS proxy
PROXY_SOCKS, // All traffic through SOCKS proxy
NUM_CONFIGS
};
struct Result {
Result(const std::string& lt, const std::string& lp,
const std::string& rt, const std::string& rp,
const std::string& lt2, const std::string& lp2,
const std::string& rt2, const std::string& rp2, int wait)
: local_type(lt), local_proto(lp), remote_type(rt), remote_proto(rp),
local_type2(lt2), local_proto2(lp2), remote_type2(rt2),
remote_proto2(rp2), connect_wait(wait) {
}
std::string local_type;
std::string local_proto;
std::string remote_type;
std::string remote_proto;
std::string local_type2;
std::string local_proto2;
std::string remote_type2;
std::string remote_proto2;
int connect_wait;
};
struct ChannelData {
bool CheckData(const char* data, int len) {
bool ret = false;
if (!ch_packets_.empty()) {
std::string packet = ch_packets_.front();
ret = (packet == std::string(data, len));
ch_packets_.pop_front();
}
return ret;
}
std::string name_; // TODO - Currently not used.
std::list<std::string> ch_packets_;
rtc::scoped_ptr<cricket::P2PTransportChannel> ch_;
};
struct CandidateData : public rtc::MessageData {
CandidateData(cricket::TransportChannel* ch, const cricket::Candidate& c)
: channel(ch), candidate(c) {
}
cricket::TransportChannel* channel;
cricket::Candidate candidate;
};
struct Endpoint {
Endpoint()
: role_(cricket::ICEROLE_UNKNOWN),
tiebreaker_(0),
role_conflict_(false),
save_candidates_(false),
protocol_type_(cricket::ICEPROTO_GOOGLE) {}
bool HasChannel(cricket::TransportChannel* ch) {
return (ch == cd1_.ch_.get() || ch == cd2_.ch_.get());
}
ChannelData* GetChannelData(cricket::TransportChannel* ch) {
if (!HasChannel(ch)) return NULL;
if (cd1_.ch_.get() == ch)
return &cd1_;
else
return &cd2_;
}
void SetIceRole(cricket::IceRole role) { role_ = role; }
cricket::IceRole ice_role() { return role_; }
void SetIceProtocolType(cricket::IceProtocolType type) {
protocol_type_ = type;
}
cricket::IceProtocolType protocol_type() { return protocol_type_; }
void SetIceTiebreaker(uint64 tiebreaker) { tiebreaker_ = tiebreaker; }
uint64 GetIceTiebreaker() { return tiebreaker_; }
void OnRoleConflict(bool role_conflict) { role_conflict_ = role_conflict; }
bool role_conflict() { return role_conflict_; }
void SetAllocationStepDelay(uint32 delay) {
allocator_->set_step_delay(delay);
}
void SetAllowTcpListen(bool allow_tcp_listen) {
allocator_->set_allow_tcp_listen(allow_tcp_listen);
}
rtc::FakeNetworkManager network_manager_;
rtc::scoped_ptr<cricket::BasicPortAllocator> allocator_;
ChannelData cd1_;
ChannelData cd2_;
cricket::IceRole role_;
uint64 tiebreaker_;
bool role_conflict_;
bool save_candidates_;
cricket::IceProtocolType protocol_type_;
std::vector<CandidateData*> saved_candidates_;
};
ChannelData* GetChannelData(cricket::TransportChannel* channel) {
if (ep1_.HasChannel(channel))
return ep1_.GetChannelData(channel);
else
return ep2_.GetChannelData(channel);
}
void CreateChannels(int num) {
std::string ice_ufrag_ep1_cd1_ch = kIceUfrag[0];
std::string ice_pwd_ep1_cd1_ch = kIcePwd[0];
std::string ice_ufrag_ep2_cd1_ch = kIceUfrag[1];
std::string ice_pwd_ep2_cd1_ch = kIcePwd[1];
ep1_.cd1_.ch_.reset(CreateChannel(
0, cricket::ICE_CANDIDATE_COMPONENT_DEFAULT,
ice_ufrag_ep1_cd1_ch, ice_pwd_ep1_cd1_ch,
ice_ufrag_ep2_cd1_ch, ice_pwd_ep2_cd1_ch));
ep2_.cd1_.ch_.reset(CreateChannel(
1, cricket::ICE_CANDIDATE_COMPONENT_DEFAULT,
ice_ufrag_ep2_cd1_ch, ice_pwd_ep2_cd1_ch,
ice_ufrag_ep1_cd1_ch, ice_pwd_ep1_cd1_ch));
if (num == 2) {
std::string ice_ufrag_ep1_cd2_ch = kIceUfrag[2];
std::string ice_pwd_ep1_cd2_ch = kIcePwd[2];
std::string ice_ufrag_ep2_cd2_ch = kIceUfrag[3];
std::string ice_pwd_ep2_cd2_ch = kIcePwd[3];
ep1_.cd2_.ch_.reset(CreateChannel(
0, cricket::ICE_CANDIDATE_COMPONENT_DEFAULT,
ice_ufrag_ep1_cd2_ch, ice_pwd_ep1_cd2_ch,
ice_ufrag_ep2_cd2_ch, ice_pwd_ep2_cd2_ch));
ep2_.cd2_.ch_.reset(CreateChannel(
1, cricket::ICE_CANDIDATE_COMPONENT_DEFAULT,
ice_ufrag_ep2_cd2_ch, ice_pwd_ep2_cd2_ch,
ice_ufrag_ep1_cd2_ch, ice_pwd_ep1_cd2_ch));
}
}
cricket::P2PTransportChannel* CreateChannel(
int endpoint,
int component,
const std::string& local_ice_ufrag,
const std::string& local_ice_pwd,
const std::string& remote_ice_ufrag,
const std::string& remote_ice_pwd) {
cricket::P2PTransportChannel* channel = new cricket::P2PTransportChannel(
"test content name", component, NULL, GetAllocator(endpoint));
channel->SignalRequestSignaling.connect(
this, &P2PTransportChannelTestBase::OnChannelRequestSignaling);
channel->SignalCandidateReady.connect(this,
&P2PTransportChannelTestBase::OnCandidate);
channel->SignalReadPacket.connect(
this, &P2PTransportChannelTestBase::OnReadPacket);
channel->SignalRoleConflict.connect(
this, &P2PTransportChannelTestBase::OnRoleConflict);
channel->SetIceProtocolType(GetEndpoint(endpoint)->protocol_type());
channel->SetIceCredentials(local_ice_ufrag, local_ice_pwd);
if (clear_remote_candidates_ufrag_pwd_) {
// This only needs to be set if we're clearing them from the
// candidates. Some unit tests rely on this not being set.
channel->SetRemoteIceCredentials(remote_ice_ufrag, remote_ice_pwd);
}
channel->SetIceRole(GetEndpoint(endpoint)->ice_role());
channel->SetIceTiebreaker(GetEndpoint(endpoint)->GetIceTiebreaker());
channel->Connect();
return channel;
}
void DestroyChannels() {
ep1_.cd1_.ch_.reset();
ep2_.cd1_.ch_.reset();
ep1_.cd2_.ch_.reset();
ep2_.cd2_.ch_.reset();
}
cricket::P2PTransportChannel* ep1_ch1() { return ep1_.cd1_.ch_.get(); }
cricket::P2PTransportChannel* ep1_ch2() { return ep1_.cd2_.ch_.get(); }
cricket::P2PTransportChannel* ep2_ch1() { return ep2_.cd1_.ch_.get(); }
cricket::P2PTransportChannel* ep2_ch2() { return ep2_.cd2_.ch_.get(); }
// Common results.
static const Result kLocalUdpToLocalUdp;
static const Result kLocalUdpToStunUdp;
static const Result kLocalUdpToPrflxUdp;
static const Result kPrflxUdpToLocalUdp;
static const Result kStunUdpToLocalUdp;
static const Result kStunUdpToStunUdp;
static const Result kPrflxUdpToStunUdp;
static const Result kLocalUdpToRelayUdp;
static const Result kPrflxUdpToRelayUdp;
static const Result kLocalTcpToLocalTcp;
static const Result kLocalTcpToPrflxTcp;
static const Result kPrflxTcpToLocalTcp;
rtc::NATSocketServer* nat() { return nss_.get(); }
rtc::FirewallSocketServer* fw() { return ss_.get(); }
Endpoint* GetEndpoint(int endpoint) {
if (endpoint == 0) {
return &ep1_;
} else if (endpoint == 1) {
return &ep2_;
} else {
return NULL;
}
}
cricket::PortAllocator* GetAllocator(int endpoint) {
return GetEndpoint(endpoint)->allocator_.get();
}
void AddAddress(int endpoint, const SocketAddress& addr) {
GetEndpoint(endpoint)->network_manager_.AddInterface(addr);
}
void RemoveAddress(int endpoint, const SocketAddress& addr) {
GetEndpoint(endpoint)->network_manager_.RemoveInterface(addr);
}
void SetProxy(int endpoint, rtc::ProxyType type) {
rtc::ProxyInfo info;
info.type = type;
info.address = (type == rtc::PROXY_HTTPS) ?
kHttpsProxyAddrs[endpoint] : kSocksProxyAddrs[endpoint];
GetAllocator(endpoint)->set_proxy("unittest/1.0", info);
}
void SetAllocatorFlags(int endpoint, int flags) {
GetAllocator(endpoint)->set_flags(flags);
}
void SetIceProtocol(int endpoint, cricket::IceProtocolType type) {
GetEndpoint(endpoint)->SetIceProtocolType(type);
}
void SetIceRole(int endpoint, cricket::IceRole role) {
GetEndpoint(endpoint)->SetIceRole(role);
}
void SetIceTiebreaker(int endpoint, uint64 tiebreaker) {
GetEndpoint(endpoint)->SetIceTiebreaker(tiebreaker);
}
bool GetRoleConflict(int endpoint) {
return GetEndpoint(endpoint)->role_conflict();
}
void SetAllocationStepDelay(int endpoint, uint32 delay) {
return GetEndpoint(endpoint)->SetAllocationStepDelay(delay);
}
void SetAllowTcpListen(int endpoint, bool allow_tcp_listen) {
return GetEndpoint(endpoint)->SetAllowTcpListen(allow_tcp_listen);
}
void Test(const Result& expected) {
int32 connect_start = rtc::Time(), connect_time;
// Create the channels and wait for them to connect.
CreateChannels(1);
EXPECT_TRUE_WAIT_MARGIN(ep1_ch1() != NULL &&
ep2_ch1() != NULL &&
ep1_ch1()->readable() &&
ep1_ch1()->writable() &&
ep2_ch1()->readable() &&
ep2_ch1()->writable(),
expected.connect_wait,
1000);
connect_time = rtc::TimeSince(connect_start);
if (connect_time < expected.connect_wait) {
LOG(LS_INFO) << "Connect time: " << connect_time << " ms";
} else {
LOG(LS_INFO) << "Connect time: " << "TIMEOUT ("
<< expected.connect_wait << " ms)";
}
// Allow a few turns of the crank for the best connections to emerge.
// This may take up to 2 seconds.
if (ep1_ch1()->best_connection() &&
ep2_ch1()->best_connection()) {
int32 converge_start = rtc::Time(), converge_time;
int converge_wait = 2000;
EXPECT_TRUE_WAIT_MARGIN(
LocalCandidate(ep1_ch1())->type() == expected.local_type &&
LocalCandidate(ep1_ch1())->protocol() == expected.local_proto &&
RemoteCandidate(ep1_ch1())->type() == expected.remote_type &&
RemoteCandidate(ep1_ch1())->protocol() == expected.remote_proto,
converge_wait,
converge_wait);
// Also do EXPECT_EQ on each part so that failures are more verbose.
EXPECT_EQ(expected.local_type, LocalCandidate(ep1_ch1())->type());
EXPECT_EQ(expected.local_proto, LocalCandidate(ep1_ch1())->protocol());
EXPECT_EQ(expected.remote_type, RemoteCandidate(ep1_ch1())->type());
EXPECT_EQ(expected.remote_proto, RemoteCandidate(ep1_ch1())->protocol());
// Verifying remote channel best connection information. This is done
// only for the RFC 5245 as controlled agent will use USE-CANDIDATE
// from controlling (ep1) agent. We can easily predict from EP1 result
// matrix.
if (ep2_.protocol_type_ == cricket::ICEPROTO_RFC5245) {
// Checking for best connection candidates information at remote.
EXPECT_TRUE_WAIT(
LocalCandidate(ep2_ch1())->type() == expected.local_type2 &&
LocalCandidate(ep2_ch1())->protocol() == expected.local_proto2 &&
RemoteCandidate(ep2_ch1())->protocol() == expected.remote_proto2,
kDefaultTimeout);
// For verbose
EXPECT_EQ(expected.local_type2, LocalCandidate(ep2_ch1())->type());
EXPECT_EQ(expected.local_proto2, LocalCandidate(ep2_ch1())->protocol());
EXPECT_EQ(expected.remote_proto2,
RemoteCandidate(ep2_ch1())->protocol());
// Removed remote_type comparision aginst best connection remote
// candidate. This is done to handle remote type discrepancy from
// local to stun based on the test type.
// For example in case of Open -> NAT, ep2 channels will have LULU
// and in other cases like NAT -> NAT it will be LUSU. To avoid these
// mismatches and we are doing comparision in different way.
// i.e. when don't match its remote type is either local or stun.
// TODO(ronghuawu): Refine the test criteria.
// https://code.google.com/p/webrtc/issues/detail?id=1953
if (expected.remote_type2 != RemoteCandidate(ep2_ch1())->type()) {
EXPECT_TRUE(expected.remote_type2 == cricket::LOCAL_PORT_TYPE ||
expected.remote_type2 == cricket::STUN_PORT_TYPE);
EXPECT_TRUE(
RemoteCandidate(ep2_ch1())->type() == cricket::LOCAL_PORT_TYPE ||
RemoteCandidate(ep2_ch1())->type() == cricket::STUN_PORT_TYPE ||
RemoteCandidate(ep2_ch1())->type() == cricket::PRFLX_PORT_TYPE);
}
}
converge_time = rtc::TimeSince(converge_start);
if (converge_time < converge_wait) {
LOG(LS_INFO) << "Converge time: " << converge_time << " ms";
} else {
LOG(LS_INFO) << "Converge time: " << "TIMEOUT ("
<< converge_wait << " ms)";
}
}
// Try sending some data to other end.
TestSendRecv(1);
// Destroy the channels, and wait for them to be fully cleaned up.
DestroyChannels();
}
void TestSendRecv(int channels) {
for (int i = 0; i < 10; ++i) {
const char* data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
int len = static_cast<int>(strlen(data));
// local_channel1 <==> remote_channel1
EXPECT_EQ_WAIT(len, SendData(ep1_ch1(), data, len), 1000);
EXPECT_TRUE_WAIT(CheckDataOnChannel(ep2_ch1(), data, len), 1000);
EXPECT_EQ_WAIT(len, SendData(ep2_ch1(), data, len), 1000);
EXPECT_TRUE_WAIT(CheckDataOnChannel(ep1_ch1(), data, len), 1000);
if (channels == 2 && ep1_ch2() && ep2_ch2()) {
// local_channel2 <==> remote_channel2
EXPECT_EQ_WAIT(len, SendData(ep1_ch2(), data, len), 1000);
EXPECT_TRUE_WAIT(CheckDataOnChannel(ep2_ch2(), data, len), 1000);
EXPECT_EQ_WAIT(len, SendData(ep2_ch2(), data, len), 1000);
EXPECT_TRUE_WAIT(CheckDataOnChannel(ep1_ch2(), data, len), 1000);
}
}
}
// This test waits for the transport to become readable and writable on both
// end points. Once they are, the end points set new local ice credentials to
// restart the ice gathering. Finally it waits for the transport to select a
// new connection using the newly generated ice candidates.
// Before calling this function the end points must be configured.
void TestHandleIceUfragPasswordChanged() {
ep1_ch1()->SetRemoteIceCredentials(kIceUfrag[1], kIcePwd[1]);
ep2_ch1()->SetRemoteIceCredentials(kIceUfrag[0], kIcePwd[0]);
EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->readable() && ep1_ch1()->writable() &&
ep2_ch1()->readable() && ep2_ch1()->writable(),
1000, 1000);
const cricket::Candidate* old_local_candidate1 = LocalCandidate(ep1_ch1());
const cricket::Candidate* old_local_candidate2 = LocalCandidate(ep2_ch1());
const cricket::Candidate* old_remote_candidate1 =
RemoteCandidate(ep1_ch1());
const cricket::Candidate* old_remote_candidate2 =
RemoteCandidate(ep2_ch1());
ep1_ch1()->SetIceCredentials(kIceUfrag[2], kIcePwd[2]);
ep1_ch1()->SetRemoteIceCredentials(kIceUfrag[3], kIcePwd[3]);
ep2_ch1()->SetIceCredentials(kIceUfrag[3], kIcePwd[3]);
ep2_ch1()->SetRemoteIceCredentials(kIceUfrag[2], kIcePwd[2]);
EXPECT_TRUE_WAIT_MARGIN(LocalCandidate(ep1_ch1())->generation() !=
old_local_candidate1->generation(),
1000, 1000);
EXPECT_TRUE_WAIT_MARGIN(LocalCandidate(ep2_ch1())->generation() !=
old_local_candidate2->generation(),
1000, 1000);
EXPECT_TRUE_WAIT_MARGIN(RemoteCandidate(ep1_ch1())->generation() !=
old_remote_candidate1->generation(),
1000, 1000);
EXPECT_TRUE_WAIT_MARGIN(RemoteCandidate(ep2_ch1())->generation() !=
old_remote_candidate2->generation(),
1000, 1000);
EXPECT_EQ(1u, RemoteCandidate(ep2_ch1())->generation());
EXPECT_EQ(1u, RemoteCandidate(ep1_ch1())->generation());
}
void TestSignalRoleConflict() {
SetIceProtocol(0, cricket::ICEPROTO_RFC5245);
SetIceTiebreaker(0, kTiebreaker1); // Default EP1 is in controlling state.
SetIceProtocol(1, cricket::ICEPROTO_RFC5245);
SetIceRole(1, cricket::ICEROLE_CONTROLLING);
SetIceTiebreaker(1, kTiebreaker2);
// Creating channels with both channels role set to CONTROLLING.
CreateChannels(1);
// Since both the channels initiated with controlling state and channel2
// has higher tiebreaker value, channel1 should receive SignalRoleConflict.
EXPECT_TRUE_WAIT(GetRoleConflict(0), 1000);
EXPECT_FALSE(GetRoleConflict(1));
EXPECT_TRUE_WAIT(ep1_ch1()->readable() &&
ep1_ch1()->writable() &&
ep2_ch1()->readable() &&
ep2_ch1()->writable(),
1000);
EXPECT_TRUE(ep1_ch1()->best_connection() &&
ep2_ch1()->best_connection());
TestSendRecv(1);
}
void TestHybridConnectivity(cricket::IceProtocolType proto) {
AddAddress(0, kPublicAddrs[0]);
AddAddress(1, kPublicAddrs[1]);
SetAllocationStepDelay(0, kMinimumStepDelay);
SetAllocationStepDelay(1, kMinimumStepDelay);
SetIceRole(0, cricket::ICEROLE_CONTROLLING);
SetIceProtocol(0, cricket::ICEPROTO_HYBRID);
SetIceTiebreaker(0, kTiebreaker1);
SetIceRole(1, cricket::ICEROLE_CONTROLLED);
SetIceProtocol(1, proto);
SetIceTiebreaker(1, kTiebreaker2);
CreateChannels(1);
// When channel is in hybrid and it's controlling agent, channel will
// receive ping request from the remote. Hence connection is readable.
// Since channel is in hybrid, it will not send any pings, so no writable
// connection. Since channel2 is in controlled state, it will not have
// any connections which are readable or writable, as it didn't received
// pings (or none) with USE-CANDIDATE attribute.
EXPECT_TRUE_WAIT(ep1_ch1()->readable(), 1000);
// Set real protocol type.
ep1_ch1()->SetIceProtocolType(proto);
// Channel should able to send ping requests and connections become writable
// in both directions.
EXPECT_TRUE_WAIT(ep1_ch1()->readable() && ep1_ch1()->writable() &&
ep2_ch1()->readable() && ep2_ch1()->writable(),
1000);
EXPECT_TRUE(
ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
TestSendRecv(1);
DestroyChannels();
}
void OnChannelRequestSignaling(cricket::TransportChannelImpl* channel) {
channel->OnSignalingReady();
}
// We pass the candidates directly to the other side.
void OnCandidate(cricket::TransportChannelImpl* ch,
const cricket::Candidate& c) {
if (force_relay_ && c.type() != cricket::RELAY_PORT_TYPE)
return;
if (GetEndpoint(ch)->save_candidates_) {
GetEndpoint(ch)->saved_candidates_.push_back(new CandidateData(ch, c));
} else {
main_->Post(this, MSG_CANDIDATE, new CandidateData(ch, c));
}
}
void PauseCandidates(int endpoint) {
GetEndpoint(endpoint)->save_candidates_ = true;
}
void ResumeCandidates(int endpoint) {
Endpoint* ed = GetEndpoint(endpoint);
std::vector<CandidateData*>::iterator it = ed->saved_candidates_.begin();
for (; it != ed->saved_candidates_.end(); ++it) {
main_->Post(this, MSG_CANDIDATE, *it);
}
ed->saved_candidates_.clear();
ed->save_candidates_ = false;
}
void OnMessage(rtc::Message* msg) {
switch (msg->message_id) {
case MSG_CANDIDATE: {
rtc::scoped_ptr<CandidateData> data(
static_cast<CandidateData*>(msg->pdata));
cricket::P2PTransportChannel* rch = GetRemoteChannel(data->channel);
cricket::Candidate c = data->candidate;
if (clear_remote_candidates_ufrag_pwd_) {
c.set_username("");
c.set_password("");
}
LOG(LS_INFO) << "Candidate(" << data->channel->component() << "->"
<< rch->component() << "): " << c.ToString();
rch->OnCandidate(c);
break;
}
}
}
void OnReadPacket(cricket::TransportChannel* channel, const char* data,
size_t len, const rtc::PacketTime& packet_time,
int flags) {
std::list<std::string>& packets = GetPacketList(channel);
packets.push_front(std::string(data, len));
}
void OnRoleConflict(cricket::TransportChannelImpl* channel) {
GetEndpoint(channel)->OnRoleConflict(true);
cricket::IceRole new_role =
GetEndpoint(channel)->ice_role() == cricket::ICEROLE_CONTROLLING ?
cricket::ICEROLE_CONTROLLED : cricket::ICEROLE_CONTROLLING;
channel->SetIceRole(new_role);
}
int SendData(cricket::TransportChannel* channel,
const char* data, size_t len) {
rtc::PacketOptions options;
return channel->SendPacket(data, len, options, 0);
}
bool CheckDataOnChannel(cricket::TransportChannel* channel,
const char* data, int len) {
return GetChannelData(channel)->CheckData(data, len);
}
static const cricket::Candidate* LocalCandidate(
cricket::P2PTransportChannel* ch) {
return (ch && ch->best_connection()) ?
&ch->best_connection()->local_candidate() : NULL;
}
static const cricket::Candidate* RemoteCandidate(
cricket::P2PTransportChannel* ch) {
return (ch && ch->best_connection()) ?
&ch->best_connection()->remote_candidate() : NULL;
}
Endpoint* GetEndpoint(cricket::TransportChannel* ch) {
if (ep1_.HasChannel(ch)) {
return &ep1_;
} else if (ep2_.HasChannel(ch)) {
return &ep2_;
} else {
return NULL;
}
}
cricket::P2PTransportChannel* GetRemoteChannel(
cricket::TransportChannel* ch) {
if (ch == ep1_ch1())
return ep2_ch1();
else if (ch == ep1_ch2())
return ep2_ch2();
else if (ch == ep2_ch1())
return ep1_ch1();
else if (ch == ep2_ch2())
return ep1_ch2();
else
return NULL;
}
std::list<std::string>& GetPacketList(cricket::TransportChannel* ch) {
return GetChannelData(ch)->ch_packets_;
}
void set_clear_remote_candidates_ufrag_pwd(bool clear) {
clear_remote_candidates_ufrag_pwd_ = clear;
}
void set_force_relay(bool relay) {
force_relay_ = relay;
}
private:
rtc::Thread* main_;
rtc::scoped_ptr<rtc::PhysicalSocketServer> pss_;
rtc::scoped_ptr<rtc::VirtualSocketServer> vss_;
rtc::scoped_ptr<rtc::NATSocketServer> nss_;
rtc::scoped_ptr<rtc::FirewallSocketServer> ss_;
rtc::SocketServerScope ss_scope_;
rtc::scoped_ptr<cricket::TestStunServer> stun_server_;
cricket::TestTurnServer turn_server_;
cricket::TestRelayServer relay_server_;
rtc::SocksProxyServer socks_server1_;
rtc::SocksProxyServer socks_server2_;
Endpoint ep1_;
Endpoint ep2_;
bool clear_remote_candidates_ufrag_pwd_;
bool force_relay_;
};
// The tests have only a few outcomes, which we predefine.
const P2PTransportChannelTestBase::Result P2PTransportChannelTestBase::
kLocalUdpToLocalUdp("local", "udp", "local", "udp",
"local", "udp", "local", "udp", 1000);
const P2PTransportChannelTestBase::Result P2PTransportChannelTestBase::
kLocalUdpToStunUdp("local", "udp", "stun", "udp",
"local", "udp", "stun", "udp", 1000);
const P2PTransportChannelTestBase::Result P2PTransportChannelTestBase::
kLocalUdpToPrflxUdp("local", "udp", "prflx", "udp",
"prflx", "udp", "local", "udp", 1000);
const P2PTransportChannelTestBase::Result P2PTransportChannelTestBase::
kPrflxUdpToLocalUdp("prflx", "udp", "local", "udp",
"local", "udp", "prflx", "udp", 1000);
const P2PTransportChannelTestBase::Result P2PTransportChannelTestBase::
kStunUdpToLocalUdp("stun", "udp", "local", "udp",
"local", "udp", "stun", "udp", 1000);
const P2PTransportChannelTestBase::Result P2PTransportChannelTestBase::
kStunUdpToStunUdp("stun", "udp", "stun", "udp",
"stun", "udp", "stun", "udp", 1000);
const P2PTransportChannelTestBase::Result P2PTransportChannelTestBase::
kPrflxUdpToStunUdp("prflx", "udp", "stun", "udp",
"local", "udp", "prflx", "udp", 1000);
const P2PTransportChannelTestBase::Result P2PTransportChannelTestBase::
kLocalUdpToRelayUdp("local", "udp", "relay", "udp",
"relay", "udp", "local", "udp", 2000);
const P2PTransportChannelTestBase::Result P2PTransportChannelTestBase::
kPrflxUdpToRelayUdp("prflx", "udp", "relay", "udp",
"relay", "udp", "prflx", "udp", 2000);
const P2PTransportChannelTestBase::Result P2PTransportChannelTestBase::
kLocalTcpToLocalTcp("local", "tcp", "local", "tcp",
"local", "tcp", "local", "tcp", 3000);
const P2PTransportChannelTestBase::Result P2PTransportChannelTestBase::
kLocalTcpToPrflxTcp("local", "tcp", "prflx", "tcp",
"prflx", "tcp", "local", "tcp", 3000);
const P2PTransportChannelTestBase::Result P2PTransportChannelTestBase::
kPrflxTcpToLocalTcp("prflx", "tcp", "local", "tcp",
"local", "tcp", "prflx", "tcp", 3000);
// Test the matrix of all the connectivity types we expect to see in the wild.
// Just test every combination of the configs in the Config enum.
class P2PTransportChannelTest : public P2PTransportChannelTestBase {
protected:
static const Result* kMatrix[NUM_CONFIGS][NUM_CONFIGS];
static const Result* kMatrixSharedUfrag[NUM_CONFIGS][NUM_CONFIGS];
static const Result* kMatrixSharedSocketAsGice[NUM_CONFIGS][NUM_CONFIGS];
static const Result* kMatrixSharedSocketAsIce[NUM_CONFIGS][NUM_CONFIGS];
void ConfigureEndpoints(Config config1, Config config2,
int allocator_flags1, int allocator_flags2,
int delay1, int delay2,
cricket::IceProtocolType type) {
// Ideally we want to use TURN server for both GICE and ICE, but in case
// of GICE, TURN server usage is not producing results reliabally.
// TODO(mallinath): Remove Relay and use TURN server for all tests.
ServerAddresses stun_servers;
stun_servers.insert(kStunAddr);
GetEndpoint(0)->allocator_.reset(
new cricket::BasicPortAllocator(&(GetEndpoint(0)->network_manager_),
stun_servers,
rtc::SocketAddress(), rtc::SocketAddress(),
rtc::SocketAddress()));
GetEndpoint(1)->allocator_.reset(
new cricket::BasicPortAllocator(&(GetEndpoint(1)->network_manager_),
stun_servers,
rtc::SocketAddress(), rtc::SocketAddress(),
rtc::SocketAddress()));
cricket::RelayServerConfig relay_server(cricket::RELAY_GTURN);
if (type == cricket::ICEPROTO_RFC5245) {
relay_server.type = cricket::RELAY_TURN;
relay_server.credentials = kRelayCredentials;
relay_server.ports.push_back(cricket::ProtocolAddress(
kTurnUdpIntAddr, cricket::PROTO_UDP, false));
} else {
relay_server.ports.push_back(cricket::ProtocolAddress(
kRelayUdpIntAddr, cricket::PROTO_UDP, false));
relay_server.ports.push_back(cricket::ProtocolAddress(
kRelayTcpIntAddr, cricket::PROTO_TCP, false));
relay_server.ports.push_back(cricket::ProtocolAddress(
kRelaySslTcpIntAddr, cricket::PROTO_SSLTCP, false));
}
GetEndpoint(0)->allocator_->AddRelay(relay_server);
GetEndpoint(1)->allocator_->AddRelay(relay_server);
ConfigureEndpoint(0, config1);
SetIceProtocol(0, type);
SetAllocatorFlags(0, allocator_flags1);
SetAllocationStepDelay(0, delay1);
ConfigureEndpoint(1, config2);
SetIceProtocol(1, type);
SetAllocatorFlags(1, allocator_flags2);
SetAllocationStepDelay(1, delay2);
if (type == cricket::ICEPROTO_RFC5245) {
set_clear_remote_candidates_ufrag_pwd(true);
}
}
void ConfigureEndpoint(int endpoint, Config config) {
switch (config) {
case OPEN:
AddAddress(endpoint, kPublicAddrs[endpoint]);
break;
case NAT_FULL_CONE:
case NAT_ADDR_RESTRICTED:
case NAT_PORT_RESTRICTED:
case NAT_SYMMETRIC:
AddAddress(endpoint, kPrivateAddrs[endpoint]);
// Add a single NAT of the desired type
nat()->AddTranslator(kPublicAddrs[endpoint], kNatAddrs[endpoint],
static_cast<rtc::NATType>(config - NAT_FULL_CONE))->
AddClient(kPrivateAddrs[endpoint]);
break;
case NAT_DOUBLE_CONE:
case NAT_SYMMETRIC_THEN_CONE:
AddAddress(endpoint, kCascadedPrivateAddrs[endpoint]);
// Add a two cascaded NATs of the desired types
nat()->AddTranslator(kPublicAddrs[endpoint], kNatAddrs[endpoint],
(config == NAT_DOUBLE_CONE) ?
rtc::NAT_OPEN_CONE : rtc::NAT_SYMMETRIC)->
AddTranslator(kPrivateAddrs[endpoint], kCascadedNatAddrs[endpoint],
rtc::NAT_OPEN_CONE)->
AddClient(kCascadedPrivateAddrs[endpoint]);
break;
case BLOCK_UDP:
case BLOCK_UDP_AND_INCOMING_TCP:
case BLOCK_ALL_BUT_OUTGOING_HTTP:
case PROXY_HTTPS:
case PROXY_SOCKS:
AddAddress(endpoint, kPublicAddrs[endpoint]);
// Block all UDP
fw()->AddRule(false, rtc::FP_UDP, rtc::FD_ANY,
kPublicAddrs[endpoint]);
if (config == BLOCK_UDP_AND_INCOMING_TCP) {
// Block TCP inbound to the endpoint
fw()->AddRule(false, rtc::FP_TCP, SocketAddress(),
kPublicAddrs[endpoint]);
} else if (config == BLOCK_ALL_BUT_OUTGOING_HTTP) {
// Block all TCP to/from the endpoint except 80/443 out
fw()->AddRule(true, rtc::FP_TCP, kPublicAddrs[endpoint],
SocketAddress(rtc::IPAddress(INADDR_ANY), 80));
fw()->AddRule(true, rtc::FP_TCP, kPublicAddrs[endpoint],
SocketAddress(rtc::IPAddress(INADDR_ANY), 443));
fw()->AddRule(false, rtc::FP_TCP, rtc::FD_ANY,
kPublicAddrs[endpoint]);
} else if (config == PROXY_HTTPS) {
// Block all TCP to/from the endpoint except to the proxy server
fw()->AddRule(true, rtc::FP_TCP, kPublicAddrs[endpoint],
kHttpsProxyAddrs[endpoint]);
fw()->AddRule(false, rtc::FP_TCP, rtc::FD_ANY,
kPublicAddrs[endpoint]);
SetProxy(endpoint, rtc::PROXY_HTTPS);
} else if (config == PROXY_SOCKS) {
// Block all TCP to/from the endpoint except to the proxy server
fw()->AddRule(true, rtc::FP_TCP, kPublicAddrs[endpoint],
kSocksProxyAddrs[endpoint]);
fw()->AddRule(false, rtc::FP_TCP, rtc::FD_ANY,
kPublicAddrs[endpoint]);
SetProxy(endpoint, rtc::PROXY_SOCKS5);
}
break;
default:
break;
}
}
};
// Shorthands for use in the test matrix.
#define LULU &kLocalUdpToLocalUdp
#define LUSU &kLocalUdpToStunUdp
#define LUPU &kLocalUdpToPrflxUdp
#define PULU &kPrflxUdpToLocalUdp
#define SULU &kStunUdpToLocalUdp
#define SUSU &kStunUdpToStunUdp
#define PUSU &kPrflxUdpToStunUdp
#define LURU &kLocalUdpToRelayUdp
#define PURU &kPrflxUdpToRelayUdp
#define LTLT &kLocalTcpToLocalTcp
#define LTPT &kLocalTcpToPrflxTcp
#define PTLT &kPrflxTcpToLocalTcp
// TODO: Enable these once TestRelayServer can accept external TCP.
#define LTRT NULL
#define LSRS NULL
// Test matrix. Originator behavior defined by rows, receiever by columns.
// Currently the p2ptransportchannel.cc (specifically the
// P2PTransportChannel::OnUnknownAddress) operates in 2 modes depend on the
// remote candidates - ufrag per port or shared ufrag.
// For example, if the remote candidates have the shared ufrag, for the unknown
// address reaches the OnUnknownAddress, we will try to find the matched
// remote candidate based on the address and protocol, if not found, a new
// remote candidate will be created for this address. But if the remote
// candidates have different ufrags, we will try to find the matched remote
// candidate by comparing the ufrag. If not found, an error will be returned.
// Because currently the shared ufrag feature is under the experiment and will
// be rolled out gradually. We want to test the different combinations of peers
// with/without the shared ufrag enabled. And those different combinations have
// different expectation of the best connection. For example in the OpenToCONE
// case, an unknown address will be updated to a "host" remote candidate if the
// remote peer uses different ufrag per port. But in the shared ufrag case,
// a "stun" (should be peer-reflexive eventually) candidate will be created for
// that. So the expected best candidate will be LUSU instead of LULU.
// With all these, we have to keep 2 test matrixes for the tests:
// kMatrix - for the tests that the remote peer uses different ufrag per port.
// kMatrixSharedUfrag - for the tests that remote peer uses shared ufrag.
// The different between the two matrixes are on:
// OPToCONE, OPTo2CON,
// COToCONE, COToADDR, COToPORT, COToSYMM, COTo2CON, COToSCON,
// ADToCONE, ADToADDR, ADTo2CON,
// POToADDR,
// SYToADDR,
// 2CToCONE, 2CToADDR, 2CToPORT, 2CToSYMM, 2CTo2CON, 2CToSCON,
// SCToADDR,
// TODO: Fix NULLs caused by lack of TCP support in NATSocket.
// TODO: Fix NULLs caused by no HTTP proxy support.
// TODO: Rearrange rows/columns from best to worst.
// TODO(ronghuawu): Keep only one test matrix once the shared ufrag is enabled.
const P2PTransportChannelTest::Result*
P2PTransportChannelTest::kMatrix[NUM_CONFIGS][NUM_CONFIGS] = {
// OPEN CONE ADDR PORT SYMM 2CON SCON !UDP !TCP HTTP PRXH PRXS
/*OP*/ {LULU, LULU, LULU, LULU, LULU, LULU, LULU, LTLT, LTLT, LSRS, NULL, LTLT},
/*CO*/ {LULU, LULU, LULU, SULU, SULU, LULU, SULU, NULL, NULL, LSRS, NULL, LTRT},
/*AD*/ {LULU, LULU, LULU, SUSU, SUSU, LULU, SUSU, NULL, NULL, LSRS, NULL, LTRT},
/*PO*/ {LULU, LUSU, LUSU, SUSU, LURU, LUSU, LURU, NULL, NULL, LSRS, NULL, LTRT},
/*SY*/ {LULU, LUSU, LUSU, LURU, LURU, LUSU, LURU, NULL, NULL, LSRS, NULL, LTRT},
/*2C*/ {LULU, LULU, LULU, SULU, SULU, LULU, SULU, NULL, NULL, LSRS, NULL, LTRT},
/*SC*/ {LULU, LUSU, LUSU, LURU, LURU, LUSU, LURU, NULL, NULL, LSRS, NULL, LTRT},
/*!U*/ {LTLT, NULL, NULL, NULL, NULL, NULL, NULL, LTLT, LTLT, LSRS, NULL, LTRT},
/*!T*/ {LTRT, NULL, NULL, NULL, NULL, NULL, NULL, LTLT, LTRT, LSRS, NULL, LTRT},
/*HT*/ {LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, NULL, LSRS},
/*PR*/ {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
/*PR*/ {LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LSRS, NULL, LTRT},
};
const P2PTransportChannelTest::Result*
P2PTransportChannelTest::kMatrixSharedUfrag[NUM_CONFIGS][NUM_CONFIGS] = {
// OPEN CONE ADDR PORT SYMM 2CON SCON !UDP !TCP HTTP PRXH PRXS
/*OP*/ {LULU, LUSU, LULU, LULU, LULU, LUSU, LULU, LTLT, LTLT, LSRS, NULL, LTLT},
/*CO*/ {LULU, LUSU, LUSU, SUSU, SUSU, LUSU, SUSU, NULL, NULL, LSRS, NULL, LTRT},
/*AD*/ {LULU, LUSU, LUSU, SUSU, SUSU, LUSU, SUSU, NULL, NULL, LSRS, NULL, LTRT},
/*PO*/ {LULU, LUSU, LUSU, SUSU, LURU, LUSU, LURU, NULL, NULL, LSRS, NULL, LTRT},
/*SY*/ {LULU, LUSU, LUSU, LURU, LURU, LUSU, LURU, NULL, NULL, LSRS, NULL, LTRT},
/*2C*/ {LULU, LUSU, LUSU, SUSU, SUSU, LUSU, SUSU, NULL, NULL, LSRS, NULL, LTRT},
/*SC*/ {LULU, LUSU, LUSU, LURU, LURU, LUSU, LURU, NULL, NULL, LSRS, NULL, LTRT},
/*!U*/ {LTLT, NULL, NULL, NULL, NULL, NULL, NULL, LTLT, LTLT, LSRS, NULL, LTRT},
/*!T*/ {LTRT, NULL, NULL, NULL, NULL, NULL, NULL, LTLT, LTRT, LSRS, NULL, LTRT},
/*HT*/ {LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, NULL, LSRS},
/*PR*/ {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
/*PR*/ {LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LSRS, NULL, LTRT},
};
const P2PTransportChannelTest::Result*
P2PTransportChannelTest::kMatrixSharedSocketAsGice
[NUM_CONFIGS][NUM_CONFIGS] = {
// OPEN CONE ADDR PORT SYMM 2CON SCON !UDP !TCP HTTP PRXH PRXS
/*OP*/ {LULU, LUSU, LUSU, LUSU, LUSU, LUSU, LUSU, LTLT, LTLT, LSRS, NULL, LTLT},
/*CO*/ {LULU, LUSU, LUSU, LUSU, LUSU, LUSU, LUSU, NULL, NULL, LSRS, NULL, LTRT},
/*AD*/ {LULU, LUSU, LUSU, LUSU, LUSU, LUSU, LUSU, NULL, NULL, LSRS, NULL, LTRT},
/*PO*/ {LULU, LUSU, LUSU, LUSU, LURU, LUSU, LURU, NULL, NULL, LSRS, NULL, LTRT},
/*SY*/ {LULU, LUSU, LUSU, LURU, LURU, LUSU, LURU, NULL, NULL, LSRS, NULL, LTRT},
/*2C*/ {LULU, LUSU, LUSU, LUSU, LUSU, LUSU, LUSU, NULL, NULL, LSRS, NULL, LTRT},
/*SC*/ {LULU, LUSU, LUSU, LURU, LURU, LUSU, LURU, NULL, NULL, LSRS, NULL, LTRT},
/*!U*/ {LTLT, NULL, NULL, NULL, NULL, NULL, NULL, LTLT, LTLT, LSRS, NULL, LTRT},
/*!T*/ {LTRT, NULL, NULL, NULL, NULL, NULL, NULL, LTLT, LTRT, LSRS, NULL, LTRT},
/*HT*/ {LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, NULL, LSRS},
/*PR*/ {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
/*PR*/ {LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LSRS, NULL, LTRT},
};
const P2PTransportChannelTest::Result*
P2PTransportChannelTest::kMatrixSharedSocketAsIce
[NUM_CONFIGS][NUM_CONFIGS] = {
// OPEN CONE ADDR PORT SYMM 2CON SCON !UDP !TCP HTTP PRXH PRXS
/*OP*/ {LULU, LUSU, LUSU, LUSU, LUPU, LUSU, LUPU, PTLT, LTPT, LSRS, NULL, LTPT},
/*CO*/ {LULU, LUSU, LUSU, LUSU, LUPU, LUSU, LUPU, NULL, NULL, LSRS, NULL, LTRT},
/*AD*/ {LULU, LUSU, LUSU, LUSU, LUPU, LUSU, LUPU, NULL, NULL, LSRS, NULL, LTRT},
/*PO*/ {LULU, LUSU, LUSU, LUSU, LURU, LUSU, LURU, NULL, NULL, LSRS, NULL, LTRT},
/*SY*/ {PULU, PUSU, PUSU, PURU, PURU, PUSU, PURU, NULL, NULL, LSRS, NULL, LTRT},
/*2C*/ {LULU, LUSU, LUSU, LUSU, LUPU, LUSU, LUPU, NULL, NULL, LSRS, NULL, LTRT},
/*SC*/ {PULU, PUSU, PUSU, PURU, PURU, PUSU, PURU, NULL, NULL, LSRS, NULL, LTRT},
/*!U*/ {PTLT, NULL, NULL, NULL, NULL, NULL, NULL, PTLT, LTPT, LSRS, NULL, LTRT},
/*!T*/ {LTRT, NULL, NULL, NULL, NULL, NULL, NULL, PTLT, LTRT, LSRS, NULL, LTRT},
/*HT*/ {LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, LSRS, NULL, LSRS},
/*PR*/ {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
/*PR*/ {LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LTRT, LSRS, NULL, LTRT},
};
// The actual tests that exercise all the various configurations.
// Test names are of the form P2PTransportChannelTest_TestOPENToNAT_FULL_CONE
// Same test case is run in both GICE and ICE mode.
// kDefaultStepDelay - is used for all Gice cases.
// kMinimumStepDelay - is used when both end points have
// PORTALLOCATOR_ENABLE_SHARED_UFRAG flag enabled.
// Technically we should be able to use kMinimumStepDelay irrespective of
// protocol type. But which might need modifications to current result matrices
// for tests in this file.
#define P2P_TEST_DECLARATION(x, y, z) \
TEST_F(P2PTransportChannelTest, z##Test##x##To##y##AsGiceNoneSharedUfrag) { \
ConfigureEndpoints(x, y, kDefaultPortAllocatorFlags, \
kDefaultPortAllocatorFlags, \
kDefaultStepDelay, kDefaultStepDelay, \
cricket::ICEPROTO_GOOGLE); \
if (kMatrix[x][y] != NULL) \
Test(*kMatrix[x][y]); \
else \
LOG(LS_WARNING) << "Not yet implemented"; \
} \
TEST_F(P2PTransportChannelTest, z##Test##x##To##y##AsGiceP0SharedUfrag) { \
ConfigureEndpoints(x, y, PORTALLOCATOR_ENABLE_SHARED_UFRAG, \
kDefaultPortAllocatorFlags, \
kDefaultStepDelay, kDefaultStepDelay, \
cricket::ICEPROTO_GOOGLE); \
if (kMatrix[x][y] != NULL) \
Test(*kMatrix[x][y]); \
else \
LOG(LS_WARNING) << "Not yet implemented"; \
} \
TEST_F(P2PTransportChannelTest, z##Test##x##To##y##AsGiceP1SharedUfrag) { \
ConfigureEndpoints(x, y, kDefaultPortAllocatorFlags, \
PORTALLOCATOR_ENABLE_SHARED_UFRAG, \
kDefaultStepDelay, kDefaultStepDelay, \
cricket::ICEPROTO_GOOGLE); \
if (kMatrixSharedUfrag[x][y] != NULL) \
Test(*kMatrixSharedUfrag[x][y]); \
else \
LOG(LS_WARNING) << "Not yet implemented"; \
} \
TEST_F(P2PTransportChannelTest, z##Test##x##To##y##AsGiceBothSharedUfrag) { \
ConfigureEndpoints(x, y, PORTALLOCATOR_ENABLE_SHARED_UFRAG, \
PORTALLOCATOR_ENABLE_SHARED_UFRAG, \
kDefaultStepDelay, kDefaultStepDelay, \
cricket::ICEPROTO_GOOGLE); \
if (kMatrixSharedUfrag[x][y] != NULL) \
Test(*kMatrixSharedUfrag[x][y]); \
else \
LOG(LS_WARNING) << "Not yet implemented"; \
} \
TEST_F(P2PTransportChannelTest, \
z##Test##x##To##y##AsGiceBothSharedUfragWithMinimumStepDelay) { \
ConfigureEndpoints(x, y, PORTALLOCATOR_ENABLE_SHARED_UFRAG, \
PORTALLOCATOR_ENABLE_SHARED_UFRAG, \
kMinimumStepDelay, kMinimumStepDelay, \
cricket::ICEPROTO_GOOGLE); \
if (kMatrixSharedUfrag[x][y] != NULL) \
Test(*kMatrixSharedUfrag[x][y]); \
else \
LOG(LS_WARNING) << "Not yet implemented"; \
} \
TEST_F(P2PTransportChannelTest, \
z##Test##x##To##y##AsGiceBothSharedUfragSocket) { \
ConfigureEndpoints(x, y, PORTALLOCATOR_ENABLE_SHARED_UFRAG | \
PORTALLOCATOR_ENABLE_SHARED_SOCKET, \
PORTALLOCATOR_ENABLE_SHARED_UFRAG | \
PORTALLOCATOR_ENABLE_SHARED_SOCKET, \
kMinimumStepDelay, kMinimumStepDelay, \
cricket::ICEPROTO_GOOGLE); \
if (kMatrixSharedSocketAsGice[x][y] != NULL) \
Test(*kMatrixSharedSocketAsGice[x][y]); \
else \
LOG(LS_WARNING) << "Not yet implemented"; \
} \
TEST_F(P2PTransportChannelTest, z##Test##x##To##y##AsIce) { \
ConfigureEndpoints(x, y, PORTALLOCATOR_ENABLE_SHARED_UFRAG | \
PORTALLOCATOR_ENABLE_SHARED_SOCKET, \
PORTALLOCATOR_ENABLE_SHARED_UFRAG | \
PORTALLOCATOR_ENABLE_SHARED_SOCKET, \
kMinimumStepDelay, kMinimumStepDelay, \
cricket::ICEPROTO_RFC5245); \
if (kMatrixSharedSocketAsIce[x][y] != NULL) \
Test(*kMatrixSharedSocketAsIce[x][y]); \
else \
LOG(LS_WARNING) << "Not yet implemented"; \
}
#define P2P_TEST(x, y) \
P2P_TEST_DECLARATION(x, y,)
#define FLAKY_P2P_TEST(x, y) \
P2P_TEST_DECLARATION(x, y, DISABLED_)
// TODO(holmer): Disabled due to randomly failing on webrtc buildbots.
// Issue: webrtc/2383
#define P2P_TEST_SET(x) \
P2P_TEST(x, OPEN) \
FLAKY_P2P_TEST(x, NAT_FULL_CONE) \
FLAKY_P2P_TEST(x, NAT_ADDR_RESTRICTED) \
FLAKY_P2P_TEST(x, NAT_PORT_RESTRICTED) \
P2P_TEST(x, NAT_SYMMETRIC) \
FLAKY_P2P_TEST(x, NAT_DOUBLE_CONE) \
P2P_TEST(x, NAT_SYMMETRIC_THEN_CONE) \
P2P_TEST(x, BLOCK_UDP) \
P2P_TEST(x, BLOCK_UDP_AND_INCOMING_TCP) \
P2P_TEST(x, BLOCK_ALL_BUT_OUTGOING_HTTP) \
P2P_TEST(x, PROXY_HTTPS) \
P2P_TEST(x, PROXY_SOCKS)
#define FLAKY_P2P_TEST_SET(x) \
P2P_TEST(x, OPEN) \
P2P_TEST(x, NAT_FULL_CONE) \
P2P_TEST(x, NAT_ADDR_RESTRICTED) \
P2P_TEST(x, NAT_PORT_RESTRICTED) \
P2P_TEST(x, NAT_SYMMETRIC) \
P2P_TEST(x, NAT_DOUBLE_CONE) \
P2P_TEST(x, NAT_SYMMETRIC_THEN_CONE) \
P2P_TEST(x, BLOCK_UDP) \
P2P_TEST(x, BLOCK_UDP_AND_INCOMING_TCP) \
P2P_TEST(x, BLOCK_ALL_BUT_OUTGOING_HTTP) \
P2P_TEST(x, PROXY_HTTPS) \
P2P_TEST(x, PROXY_SOCKS)
P2P_TEST_SET(OPEN)
P2P_TEST_SET(NAT_FULL_CONE)
P2P_TEST_SET(NAT_ADDR_RESTRICTED)
P2P_TEST_SET(NAT_PORT_RESTRICTED)
P2P_TEST_SET(NAT_SYMMETRIC)
P2P_TEST_SET(NAT_DOUBLE_CONE)
P2P_TEST_SET(NAT_SYMMETRIC_THEN_CONE)
P2P_TEST_SET(BLOCK_UDP)
P2P_TEST_SET(BLOCK_UDP_AND_INCOMING_TCP)
P2P_TEST_SET(BLOCK_ALL_BUT_OUTGOING_HTTP)
P2P_TEST_SET(PROXY_HTTPS)
P2P_TEST_SET(PROXY_SOCKS)
// Test that we restart candidate allocation when local ufrag&pwd changed.
// Standard Ice protocol is used.
TEST_F(P2PTransportChannelTest, HandleUfragPwdChangeAsIce) {
ConfigureEndpoints(OPEN, OPEN,
PORTALLOCATOR_ENABLE_SHARED_UFRAG,
PORTALLOCATOR_ENABLE_SHARED_UFRAG,
kMinimumStepDelay, kMinimumStepDelay,
cricket::ICEPROTO_RFC5245);
CreateChannels(1);
TestHandleIceUfragPasswordChanged();
DestroyChannels();
}
// Test that we restart candidate allocation when local ufrag&pwd changed.
// Google Ice protocol is used.
TEST_F(P2PTransportChannelTest, HandleUfragPwdChangeAsGice) {
ConfigureEndpoints(OPEN, OPEN,
PORTALLOCATOR_ENABLE_SHARED_UFRAG,
PORTALLOCATOR_ENABLE_SHARED_UFRAG,
kDefaultStepDelay, kDefaultStepDelay,
cricket::ICEPROTO_GOOGLE);
CreateChannels(1);
TestHandleIceUfragPasswordChanged();
DestroyChannels();
}
// Test the operation of GetStats.
TEST_F(P2PTransportChannelTest, GetStats) {
ConfigureEndpoints(OPEN, OPEN,
kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags,
kDefaultStepDelay, kDefaultStepDelay,
cricket::ICEPROTO_GOOGLE);
CreateChannels(1);
EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->readable() && ep1_ch1()->writable() &&
ep2_ch1()->readable() && ep2_ch1()->writable(),
1000, 1000);
TestSendRecv(1);
cricket::ConnectionInfos infos;
ASSERT_TRUE(ep1_ch1()->GetStats(&infos));
ASSERT_EQ(1U, infos.size());
EXPECT_TRUE(infos[0].new_connection);
EXPECT_TRUE(infos[0].best_connection);
EXPECT_TRUE(infos[0].readable);
EXPECT_TRUE(infos[0].writable);
EXPECT_FALSE(infos[0].timeout);
EXPECT_EQ(10U, infos[0].sent_total_packets);
EXPECT_EQ(0U, infos[0].sent_discarded_packets);
EXPECT_EQ(10 * 36U, infos[0].sent_total_bytes);
EXPECT_EQ(10 * 36U, infos[0].recv_total_bytes);
EXPECT_GT(infos[0].rtt, 0U);
DestroyChannels();
}
// Test that we properly create a connection on a STUN ping from unknown address
// when the signaling is slow.
TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignaling) {
ConfigureEndpoints(OPEN, OPEN,
PORTALLOCATOR_ENABLE_SHARED_UFRAG,
PORTALLOCATOR_ENABLE_SHARED_UFRAG,
kDefaultStepDelay, kDefaultStepDelay,
cricket::ICEPROTO_RFC5245);
// Emulate no remote credentials coming in.
set_clear_remote_candidates_ufrag_pwd(false);
CreateChannels(1);
// Only have remote credentials come in for ep2, not ep1.
ep2_ch1()->SetRemoteIceCredentials(kIceUfrag[3], kIcePwd[3]);
// Pause sending ep2's candidates to ep1 until ep1 receives the peer reflexive
// candidate.
PauseCandidates(1);
// The caller should have the best connection connected to the peer reflexive
// candidate.
const cricket::Connection* best_connection = NULL;
WAIT((best_connection = ep1_ch1()->best_connection()) != NULL, 2000);
EXPECT_EQ("prflx", ep1_ch1()->best_connection()->remote_candidate().type());
// Because we don't have a remote pwd, we don't ping yet.
EXPECT_EQ(kIceUfrag[1],
ep1_ch1()->best_connection()->remote_candidate().username());
EXPECT_EQ("", ep1_ch1()->best_connection()->remote_candidate().password());
EXPECT_TRUE(nullptr == ep1_ch1()->FindNextPingableConnection());
ep1_ch1()->SetRemoteIceCredentials(kIceUfrag[1], kIcePwd[1]);
ResumeCandidates(1);
EXPECT_EQ(kIcePwd[1],
ep1_ch1()->best_connection()->remote_candidate().password());
EXPECT_TRUE(nullptr != ep1_ch1()->FindNextPingableConnection());
WAIT(ep2_ch1()->best_connection() != NULL, 2000);
// Verify ep1's best connection is updated to use the 'local' candidate.
EXPECT_EQ_WAIT(
"local",
ep1_ch1()->best_connection()->remote_candidate().type(),
2000);
EXPECT_EQ(best_connection, ep1_ch1()->best_connection());
DestroyChannels();
}
// Test that we properly create a connection on a STUN ping from unknown address
// when the signaling is slow and the end points are behind NAT.
TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignalingWithNAT) {
ConfigureEndpoints(OPEN, NAT_SYMMETRIC,
PORTALLOCATOR_ENABLE_SHARED_UFRAG,
PORTALLOCATOR_ENABLE_SHARED_UFRAG,
kDefaultStepDelay, kDefaultStepDelay,
cricket::ICEPROTO_RFC5245);
// Emulate no remote credentials coming in.
set_clear_remote_candidates_ufrag_pwd(false);
CreateChannels(1);
// Only have remote credentials come in for ep2, not ep1.
ep2_ch1()->SetRemoteIceCredentials(kIceUfrag[3], kIcePwd[3]);
// Pause sending ep2's candidates to ep1 until ep1 receives the peer reflexive
// candidate.
PauseCandidates(1);
// The caller should have the best connection connected to the peer reflexive
// candidate.
WAIT(ep1_ch1()->best_connection() != NULL, 2000);
EXPECT_EQ("prflx", ep1_ch1()->best_connection()->remote_candidate().type());
// Because we don't have a remote pwd, we don't ping yet.
EXPECT_EQ(kIceUfrag[1],
ep1_ch1()->best_connection()->remote_candidate().username());
EXPECT_EQ("", ep1_ch1()->best_connection()->remote_candidate().password());
EXPECT_TRUE(nullptr == ep1_ch1()->FindNextPingableConnection());
ep1_ch1()->SetRemoteIceCredentials(kIceUfrag[1], kIcePwd[1]);
ResumeCandidates(1);
EXPECT_EQ(kIcePwd[1],
ep1_ch1()->best_connection()->remote_candidate().password());
EXPECT_TRUE(nullptr != ep1_ch1()->FindNextPingableConnection());
const cricket::Connection* best_connection = NULL;
WAIT((best_connection = ep2_ch1()->best_connection()) != NULL, 2000);
// Wait to verify the connection is not culled.
WAIT(ep1_ch1()->writable(), 2000);
EXPECT_EQ(ep2_ch1()->best_connection(), best_connection);
EXPECT_EQ("prflx", ep1_ch1()->best_connection()->remote_candidate().type());
DestroyChannels();
}
// Test that if remote candidates don't have ufrag and pwd, we still work.
TEST_F(P2PTransportChannelTest, RemoteCandidatesWithoutUfragPwd) {
set_clear_remote_candidates_ufrag_pwd(true);
ConfigureEndpoints(OPEN, OPEN,
PORTALLOCATOR_ENABLE_SHARED_UFRAG,
PORTALLOCATOR_ENABLE_SHARED_UFRAG,
kMinimumStepDelay, kMinimumStepDelay,
cricket::ICEPROTO_GOOGLE);
CreateChannels(1);
const cricket::Connection* best_connection = NULL;
// Wait until the callee's connections are created.
WAIT((best_connection = ep2_ch1()->best_connection()) != NULL, 1000);
// Wait to see if they get culled; they shouldn't.
WAIT(ep2_ch1()->best_connection() != best_connection, 1000);
EXPECT_TRUE(ep2_ch1()->best_connection() == best_connection);
DestroyChannels();
}
// Test that a host behind NAT cannot be reached when incoming_only
// is set to true.
TEST_F(P2PTransportChannelTest, IncomingOnlyBlocked) {
ConfigureEndpoints(NAT_FULL_CONE, OPEN,
kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags,
kDefaultStepDelay, kDefaultStepDelay,
cricket::ICEPROTO_GOOGLE);
SetAllocatorFlags(0, kOnlyLocalPorts);
CreateChannels(1);
ep1_ch1()->set_incoming_only(true);
// Pump for 1 second and verify that the channels are not connected.
rtc::Thread::Current()->ProcessMessages(1000);
EXPECT_FALSE(ep1_ch1()->readable());
EXPECT_FALSE(ep1_ch1()->writable());
EXPECT_FALSE(ep2_ch1()->readable());
EXPECT_FALSE(ep2_ch1()->writable());
DestroyChannels();
}
// Test that a peer behind NAT can connect to a peer that has
// incoming_only flag set.
TEST_F(P2PTransportChannelTest, IncomingOnlyOpen) {
ConfigureEndpoints(OPEN, NAT_FULL_CONE,
kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags,
kDefaultStepDelay, kDefaultStepDelay,
cricket::ICEPROTO_GOOGLE);
SetAllocatorFlags(0, kOnlyLocalPorts);
CreateChannels(1);
ep1_ch1()->set_incoming_only(true);
EXPECT_TRUE_WAIT_MARGIN(ep1_ch1() != NULL && ep2_ch1() != NULL &&
ep1_ch1()->readable() && ep1_ch1()->writable() &&
ep2_ch1()->readable() && ep2_ch1()->writable(),
1000, 1000);
DestroyChannels();
}
TEST_F(P2PTransportChannelTest, TestTcpConnectionsFromActiveToPassive) {
AddAddress(0, kPublicAddrs[0]);
AddAddress(1, kPublicAddrs[1]);
SetAllocationStepDelay(0, kMinimumStepDelay);
SetAllocationStepDelay(1, kMinimumStepDelay);
int kOnlyLocalTcpPorts = cricket::PORTALLOCATOR_DISABLE_UDP |
cricket::PORTALLOCATOR_DISABLE_STUN |
cricket::PORTALLOCATOR_DISABLE_RELAY |
cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG;
// Disable all protocols except TCP.
SetAllocatorFlags(0, kOnlyLocalTcpPorts);
SetAllocatorFlags(1, kOnlyLocalTcpPorts);
SetAllowTcpListen(0, true); // actpass.
SetAllowTcpListen(1, false); // active.
CreateChannels(1);
EXPECT_TRUE_WAIT(ep1_ch1()->readable() && ep1_ch1()->writable() &&
ep2_ch1()->readable() && ep2_ch1()->writable(),
1000);
EXPECT_TRUE(
ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
std::string kTcpProtocol = "tcp";
EXPECT_EQ(kTcpProtocol, RemoteCandidate(ep1_ch1())->protocol());
EXPECT_EQ(kTcpProtocol, LocalCandidate(ep1_ch1())->protocol());
EXPECT_EQ(kTcpProtocol, RemoteCandidate(ep2_ch1())->protocol());
EXPECT_EQ(kTcpProtocol, LocalCandidate(ep2_ch1())->protocol());
TestSendRecv(1);
DestroyChannels();
}
TEST_F(P2PTransportChannelTest, TestIceRoleConflict) {
AddAddress(0, kPublicAddrs[0]);
AddAddress(1, kPublicAddrs[1]);
TestSignalRoleConflict();
}
// Tests that the ice configs (protocol, tiebreaker and role) can be passed
// down to ports.
TEST_F(P2PTransportChannelTest, TestIceConfigWillPassDownToPort) {
AddAddress(0, kPublicAddrs[0]);
AddAddress(1, kPublicAddrs[1]);
SetIceRole(0, cricket::ICEROLE_CONTROLLING);
SetIceProtocol(0, cricket::ICEPROTO_GOOGLE);
SetIceTiebreaker(0, kTiebreaker1);
SetIceRole(1, cricket::ICEROLE_CONTROLLING);
SetIceProtocol(1, cricket::ICEPROTO_RFC5245);
SetIceTiebreaker(1, kTiebreaker2);
CreateChannels(1);
EXPECT_EQ_WAIT(2u, ep1_ch1()->ports().size(), 1000);
const std::vector<cricket::PortInterface *> ports_before = ep1_ch1()->ports();
for (size_t i = 0; i < ports_before.size(); ++i) {
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, ports_before[i]->GetIceRole());
EXPECT_EQ(cricket::ICEPROTO_GOOGLE, ports_before[i]->IceProtocol());
EXPECT_EQ(kTiebreaker1, ports_before[i]->IceTiebreaker());
}
ep1_ch1()->SetIceRole(cricket::ICEROLE_CONTROLLED);
ep1_ch1()->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
ep1_ch1()->SetIceTiebreaker(kTiebreaker2);
const std::vector<cricket::PortInterface *> ports_after = ep1_ch1()->ports();
for (size_t i = 0; i < ports_after.size(); ++i) {
EXPECT_EQ(cricket::ICEROLE_CONTROLLED, ports_before[i]->GetIceRole());
EXPECT_EQ(cricket::ICEPROTO_RFC5245, ports_before[i]->IceProtocol());
// SetIceTiebreaker after Connect() has been called will fail. So expect the
// original value.
EXPECT_EQ(kTiebreaker1, ports_before[i]->IceTiebreaker());
}
EXPECT_TRUE_WAIT(ep1_ch1()->readable() &&
ep1_ch1()->writable() &&
ep2_ch1()->readable() &&
ep2_ch1()->writable(),
1000);
EXPECT_TRUE(ep1_ch1()->best_connection() &&
ep2_ch1()->best_connection());
TestSendRecv(1);
DestroyChannels();
}
// This test verifies channel can handle ice messages when channel is in
// hybrid mode.
TEST_F(P2PTransportChannelTest, TestConnectivityBetweenHybridandIce) {
TestHybridConnectivity(cricket::ICEPROTO_RFC5245);
}
// This test verifies channel can handle Gice messages when channel is in
// hybrid mode.
TEST_F(P2PTransportChannelTest, TestConnectivityBetweenHybridandGice) {
TestHybridConnectivity(cricket::ICEPROTO_GOOGLE);
}
// Verify that we can set DSCP value and retrieve properly from P2PTC.
TEST_F(P2PTransportChannelTest, TestDefaultDscpValue) {
AddAddress(0, kPublicAddrs[0]);
AddAddress(1, kPublicAddrs[1]);
CreateChannels(1);
EXPECT_EQ(rtc::DSCP_NO_CHANGE,
GetEndpoint(0)->cd1_.ch_->DefaultDscpValue());
EXPECT_EQ(rtc::DSCP_NO_CHANGE,
GetEndpoint(1)->cd1_.ch_->DefaultDscpValue());
GetEndpoint(0)->cd1_.ch_->SetOption(
rtc::Socket::OPT_DSCP, rtc::DSCP_CS6);
GetEndpoint(1)->cd1_.ch_->SetOption(
rtc::Socket::OPT_DSCP, rtc::DSCP_CS6);
EXPECT_EQ(rtc::DSCP_CS6,
GetEndpoint(0)->cd1_.ch_->DefaultDscpValue());
EXPECT_EQ(rtc::DSCP_CS6,
GetEndpoint(1)->cd1_.ch_->DefaultDscpValue());
GetEndpoint(0)->cd1_.ch_->SetOption(
rtc::Socket::OPT_DSCP, rtc::DSCP_AF41);
GetEndpoint(1)->cd1_.ch_->SetOption(
rtc::Socket::OPT_DSCP, rtc::DSCP_AF41);
EXPECT_EQ(rtc::DSCP_AF41,
GetEndpoint(0)->cd1_.ch_->DefaultDscpValue());
EXPECT_EQ(rtc::DSCP_AF41,
GetEndpoint(1)->cd1_.ch_->DefaultDscpValue());
}
// Verify IPv6 connection is preferred over IPv4.
TEST_F(P2PTransportChannelTest, TestIPv6Connections) {
AddAddress(0, kIPv6PublicAddrs[0]);
AddAddress(0, kPublicAddrs[0]);
AddAddress(1, kIPv6PublicAddrs[1]);
AddAddress(1, kPublicAddrs[1]);
SetAllocationStepDelay(0, kMinimumStepDelay);
SetAllocationStepDelay(1, kMinimumStepDelay);
// Enable IPv6
SetAllocatorFlags(0, cricket::PORTALLOCATOR_ENABLE_IPV6);
SetAllocatorFlags(1, cricket::PORTALLOCATOR_ENABLE_IPV6);
CreateChannels(1);
EXPECT_TRUE_WAIT(ep1_ch1()->readable() && ep1_ch1()->writable() &&
ep2_ch1()->readable() && ep2_ch1()->writable(),
1000);
EXPECT_TRUE(
ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(kIPv6PublicAddrs[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(kIPv6PublicAddrs[1]));
TestSendRecv(1);
DestroyChannels();
}
// Testing forceful TURN connections.
TEST_F(P2PTransportChannelTest, TestForceTurn) {
ConfigureEndpoints(NAT_PORT_RESTRICTED, NAT_SYMMETRIC,
kDefaultPortAllocatorFlags |
cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET |
cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG,
kDefaultPortAllocatorFlags |
cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET |
cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG,
kDefaultStepDelay, kDefaultStepDelay,
cricket::ICEPROTO_RFC5245);
set_force_relay(true);
SetAllocationStepDelay(0, kMinimumStepDelay);
SetAllocationStepDelay(1, kMinimumStepDelay);
CreateChannels(1);
EXPECT_TRUE_WAIT(ep1_ch1()->readable() &&
ep1_ch1()->writable() &&
ep2_ch1()->readable() &&
ep2_ch1()->writable(),
1000);
EXPECT_TRUE(ep1_ch1()->best_connection() &&
ep2_ch1()->best_connection());
EXPECT_EQ("relay", RemoteCandidate(ep1_ch1())->type());
EXPECT_EQ("relay", LocalCandidate(ep1_ch1())->type());
EXPECT_EQ("relay", RemoteCandidate(ep2_ch1())->type());
EXPECT_EQ("relay", LocalCandidate(ep2_ch1())->type());
TestSendRecv(1);
DestroyChannels();
}
// Test what happens when we have 2 users behind the same NAT. This can lead
// to interesting behavior because the STUN server will only give out the
// address of the outermost NAT.
class P2PTransportChannelSameNatTest : public P2PTransportChannelTestBase {
protected:
void ConfigureEndpoints(Config nat_type, Config config1, Config config2) {
ASSERT(nat_type >= NAT_FULL_CONE && nat_type <= NAT_SYMMETRIC);
rtc::NATSocketServer::Translator* outer_nat =
nat()->AddTranslator(kPublicAddrs[0], kNatAddrs[0],
static_cast<rtc::NATType>(nat_type - NAT_FULL_CONE));
ConfigureEndpoint(outer_nat, 0, config1);
ConfigureEndpoint(outer_nat, 1, config2);
}
void ConfigureEndpoint(rtc::NATSocketServer::Translator* nat,
int endpoint, Config config) {
ASSERT(config <= NAT_SYMMETRIC);
if (config == OPEN) {
AddAddress(endpoint, kPrivateAddrs[endpoint]);
nat->AddClient(kPrivateAddrs[endpoint]);
} else {
AddAddress(endpoint, kCascadedPrivateAddrs[endpoint]);
nat->AddTranslator(kPrivateAddrs[endpoint], kCascadedNatAddrs[endpoint],
static_cast<rtc::NATType>(config - NAT_FULL_CONE))->AddClient(
kCascadedPrivateAddrs[endpoint]);
}
}
};
TEST_F(P2PTransportChannelSameNatTest, TestConesBehindSameCone) {
ConfigureEndpoints(NAT_FULL_CONE, NAT_FULL_CONE, NAT_FULL_CONE);
Test(kLocalUdpToStunUdp);
}
// Test what happens when we have multiple available pathways.
// In the future we will try different RTTs and configs for the different
// interfaces, so that we can simulate a user with Ethernet and VPN networks.
class P2PTransportChannelMultihomedTest : public P2PTransportChannelTestBase {
};
// Test that we can establish connectivity when both peers are multihomed.
TEST_F(P2PTransportChannelMultihomedTest, DISABLED_TestBasic) {
AddAddress(0, kPublicAddrs[0]);
AddAddress(0, kAlternateAddrs[0]);
AddAddress(1, kPublicAddrs[1]);
AddAddress(1, kAlternateAddrs[1]);
Test(kLocalUdpToLocalUdp);
}
// Test that we can quickly switch links if an interface goes down.
TEST_F(P2PTransportChannelMultihomedTest, TestFailover) {
AddAddress(0, kPublicAddrs[0]);
// Adding alternate address will make sure |kPublicAddrs| has the higher
// priority than others. This is due to FakeNetwork::AddInterface method.
AddAddress(1, kAlternateAddrs[1]);
AddAddress(1, kPublicAddrs[1]);
// Use only local ports for simplicity.
SetAllocatorFlags(0, kOnlyLocalPorts);
SetAllocatorFlags(1, kOnlyLocalPorts);
// Create channels and let them go writable, as usual.
CreateChannels(1);
EXPECT_TRUE_WAIT(ep1_ch1()->readable() && ep1_ch1()->writable() &&
ep2_ch1()->readable() && ep2_ch1()->writable(),
1000);
EXPECT_TRUE(
ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
// Blackhole any traffic to or from the public addrs.
LOG(LS_INFO) << "Failing over...";
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY,
kPublicAddrs[1]);
// We should detect loss of connectivity within 5 seconds or so.
EXPECT_TRUE_WAIT(!ep1_ch1()->writable(), 7000);
// We should switch over to use the alternate addr immediately
// when we lose writability.
EXPECT_TRUE_WAIT(
ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(kAlternateAddrs[1]),
3000);
DestroyChannels();
}
// Test that we can switch links in a coordinated fashion.
TEST_F(P2PTransportChannelMultihomedTest, TestDrain) {
AddAddress(0, kPublicAddrs[0]);
AddAddress(1, kPublicAddrs[1]);
// Use only local ports for simplicity.
SetAllocatorFlags(0, kOnlyLocalPorts);
SetAllocatorFlags(1, kOnlyLocalPorts);
// Create channels and let them go writable, as usual.
CreateChannels(1);
EXPECT_TRUE_WAIT(ep1_ch1()->readable() && ep1_ch1()->writable() &&
ep2_ch1()->readable() && ep2_ch1()->writable(),
1000);
EXPECT_TRUE(
ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
// Remove the public interface, add the alternate interface, and allocate
// a new generation of candidates for the new interface (via Connect()).
LOG(LS_INFO) << "Draining...";
AddAddress(1, kAlternateAddrs[1]);
RemoveAddress(1, kPublicAddrs[1]);
ep2_ch1()->Connect();
// We should switch over to use the alternate address after
// an exchange of pings.
EXPECT_TRUE_WAIT(
ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(kAlternateAddrs[1]),
3000);
DestroyChannels();
}
// A collection of tests which tests a single P2PTransportChannel by sending
// pings.
class P2PTransportChannelPingTest : public testing::Test,
public sigslot::has_slots<> {
public:
P2PTransportChannelPingTest()
: pss_(new rtc::PhysicalSocketServer),
vss_(new rtc::VirtualSocketServer(pss_.get())),
ss_scope_(vss_.get()) {}
protected:
void PrepareChannel(cricket::P2PTransportChannel* ch) {
ch->SignalRequestSignaling.connect(
this, &P2PTransportChannelPingTest::OnChannelRequestSignaling);
ch->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
ch->SetIceRole(cricket::ICEROLE_CONTROLLING);
ch->SetIceCredentials(kIceUfrag[0], kIcePwd[0]);
ch->SetRemoteIceCredentials(kIceUfrag[1], kIcePwd[1]);
}
void OnChannelRequestSignaling(cricket::TransportChannelImpl* channel) {
channel->OnSignalingReady();
}
cricket::Candidate CreateCandidate(const std::string& ip,
int port,
int priority) {
cricket::Candidate c;
c.set_address(rtc::SocketAddress(ip, port));
c.set_component(1);
c.set_protocol(cricket::UDP_PROTOCOL_NAME);
c.set_priority(priority);
return c;
}
cricket::Connection* WaitForConnectionTo(cricket::P2PTransportChannel* ch,
const std::string& ip,
int port_num) {
EXPECT_TRUE_WAIT(GetConnectionTo(ch, ip, port_num) != nullptr, 3000);
return GetConnectionTo(ch, ip, port_num);
}
cricket::Port* GetPort(cricket::P2PTransportChannel* ch) {
if (ch->ports().empty()) {
return nullptr;
}
return static_cast<cricket::Port*>(ch->ports()[0]);
}
cricket::Connection* GetConnectionTo(cricket::P2PTransportChannel* ch,
const std::string& ip,
int port_num) {
cricket::Port* port = GetPort(ch);
if (!port) {
return nullptr;
}
return port->GetConnection(rtc::SocketAddress(ip, port_num));
}
private:
rtc::scoped_ptr<rtc::PhysicalSocketServer> pss_;
rtc::scoped_ptr<rtc::VirtualSocketServer> vss_;
rtc::SocketServerScope ss_scope_;
};
TEST_F(P2PTransportChannelPingTest, TestTriggeredChecks) {
cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
cricket::P2PTransportChannel ch("trigger checks", 1, nullptr, &pa);
PrepareChannel(&ch);
ch.Connect();
ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2));
cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
ASSERT_TRUE(conn1 != nullptr);
ASSERT_TRUE(conn2 != nullptr);
// Before a triggered check, the first connection to ping is the
// highest priority one.
EXPECT_EQ(conn2, ch.FindNextPingableConnection());
// Receiving a ping causes a triggered check which should make conn1
// be pinged first instead of conn2, even though conn2 has a higher
// priority.
conn1->ReceivedPing();
EXPECT_EQ(conn1, ch.FindNextPingableConnection());
}
TEST_F(P2PTransportChannelPingTest, TestNoTriggeredChecksWhenWritable) {
cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
cricket::P2PTransportChannel ch("trigger checks", 1, nullptr, &pa);
PrepareChannel(&ch);
ch.Connect();
ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2));
cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
ASSERT_TRUE(conn1 != nullptr);
ASSERT_TRUE(conn2 != nullptr);
EXPECT_EQ(conn2, ch.FindNextPingableConnection());
conn1->ReceivedPingResponse();
ASSERT_TRUE(conn1->writable());
conn1->ReceivedPing();
// Ping received, but the connection is already writable, so no
// "triggered check" and conn2 is pinged before conn1 because it has
// a higher priority.
EXPECT_EQ(conn2, ch.FindNextPingableConnection());
}
TEST_F(P2PTransportChannelPingTest, ConnectionResurrection) {
cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
cricket::P2PTransportChannel ch("connection resurrection", 1, nullptr, &pa);
PrepareChannel(&ch);
ch.Connect();
// Create conn1 and keep track of original candidate priority.
ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
ASSERT_TRUE(conn1 != nullptr);
uint32 remote_priority = conn1->remote_candidate().priority();
// Create a higher priority candidate and make the connection
// readable/writable. This will prune conn1.
ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2));
cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
ASSERT_TRUE(conn2 != nullptr);
conn2->ReceivedPing();
conn2->ReceivedPingResponse();
// Wait for conn1 being destroyed.
EXPECT_TRUE_WAIT(GetConnectionTo(&ch, "1.1.1.1", 1) == nullptr, 3000);
cricket::Port* port = GetPort(&ch);
// Create a minimal STUN message with prflx priority.
cricket::IceMessage request;
request.SetType(cricket::STUN_BINDING_REQUEST);
request.AddAttribute(new cricket::StunByteStringAttribute(
cricket::STUN_ATTR_USERNAME, kIceUfrag[1]));
uint32 prflx_priority = cricket::ICE_TYPE_PREFERENCE_PRFLX << 24;
request.AddAttribute(new cricket::StunUInt32Attribute(
cricket::STUN_ATTR_PRIORITY, prflx_priority));
EXPECT_NE(prflx_priority, remote_priority);
// conn1 should be resurrected with original priority.
port->SignalUnknownAddress(port, rtc::SocketAddress("1.1.1.1", 1),
cricket::PROTO_UDP, &request, kIceUfrag[1], false);
conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
ASSERT_TRUE(conn1 != nullptr);
EXPECT_EQ(conn1->remote_candidate().priority(), remote_priority);
// conn3, a real prflx connection, should have prflx priority.
port->SignalUnknownAddress(port, rtc::SocketAddress("3.3.3.3", 1),
cricket::PROTO_UDP, &request, kIceUfrag[1], false);
cricket::Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 1);
ASSERT_TRUE(conn3 != nullptr);
EXPECT_EQ(conn3->remote_candidate().priority(), prflx_priority);
}
TEST_F(P2PTransportChannelPingTest, TestReceivingStateChange) {
cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
cricket::P2PTransportChannel ch("receiving state change", 1, nullptr, &pa);
PrepareChannel(&ch);
// Default receiving timeout and checking receiving delay should not be too
// small.
EXPECT_LE(1000, ch.receiving_timeout());
EXPECT_LE(200, ch.check_receiving_delay());
ch.SetReceivingTimeout(500);
EXPECT_EQ(500, ch.receiving_timeout());
EXPECT_EQ(50, ch.check_receiving_delay());
ch.Connect();
ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
ASSERT_TRUE(conn1 != nullptr);
conn1->ReceivedPing();
conn1->OnReadPacket("ABC", 3, rtc::CreatePacketTime(0));
EXPECT_TRUE_WAIT(ch.best_connection() != nullptr, 1000)
EXPECT_TRUE_WAIT(ch.receiving(), 1000);
EXPECT_TRUE_WAIT(!ch.receiving(), 1000);
}
// The controlled side will select a connection as the "best connection" based
// on priority until the controlling side nominates a connection, at which
// point the controlled side will select that connection as the
// "best connection".
TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) {
cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
cricket::P2PTransportChannel ch("receiving state change", 1, nullptr, &pa);
PrepareChannel(&ch);
ch.SetIceRole(cricket::ICEROLE_CONTROLLED);
ch.Connect();
ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
ASSERT_TRUE(conn1 != nullptr);
EXPECT_EQ(conn1, ch.best_connection());
// When a higher priority candidate comes in, the new connection is chosen
// as the best connection.
ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 10));
cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
ASSERT_TRUE(conn1 != nullptr);
EXPECT_EQ(conn2, ch.best_connection());
// If a stun request with use-candidate attribute arrives, the receiving
// connection will be set as the best connection, even though
// its priority is lower.
ch.OnCandidate(CreateCandidate("3.3.3.3", 3, 1));
cricket::Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3);
ASSERT_TRUE(conn3 != nullptr);
// Because it has a lower priority, the best connection is still conn2.
EXPECT_EQ(conn2, ch.best_connection());
conn3->ReceivedPingResponse(); // Become writable.
// But if it is nominated via use_candidate, it is chosen as the best
// connection.
conn3->set_nominated(true);
conn3->SignalNominated(conn3);
EXPECT_EQ(conn3, ch.best_connection());
// Even if another higher priority candidate arrives,
// it will not be set as the best connection because the best connection
// is nominated by the controlling side.
ch.OnCandidate(CreateCandidate("4.4.4.4", 4, 100));
cricket::Connection* conn4 = WaitForConnectionTo(&ch, "4.4.4.4", 4);
ASSERT_TRUE(conn4 != nullptr);
EXPECT_EQ(conn3, ch.best_connection());
// But if it is nominated via use_candidate and writable, it will be set as
// the best connection.
conn4->set_nominated(true);
conn4->SignalNominated(conn4);
// Not switched yet because conn4 is not writable.
EXPECT_EQ(conn3, ch.best_connection());
// The best connection switches after conn4 becomes writable.
conn4->ReceivedPingResponse();
EXPECT_EQ(conn4, ch.best_connection());
}
// The controlled side will select a connection as the "best connection" based
// on requests from an unknown address before the controlling side nominates
// a connection, and will nominate a connection from an unknown address if the
// request contains the use_candidate attribute.
TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) {
cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
cricket::P2PTransportChannel ch("receiving state change", 1, nullptr, &pa);
PrepareChannel(&ch);
ch.SetIceRole(cricket::ICEROLE_CONTROLLED);
ch.Connect();
// A minimal STUN message with prflx priority.
cricket::IceMessage request;
request.SetType(cricket::STUN_BINDING_REQUEST);
request.AddAttribute(new cricket::StunByteStringAttribute(
cricket::STUN_ATTR_USERNAME, kIceUfrag[1]));
uint32 prflx_priority = cricket::ICE_TYPE_PREFERENCE_PRFLX << 24;
request.AddAttribute(new cricket::StunUInt32Attribute(
cricket::STUN_ATTR_PRIORITY, prflx_priority));
cricket::Port* port = GetPort(&ch);
port->SignalUnknownAddress(port, rtc::SocketAddress("1.1.1.1", 1),
cricket::PROTO_UDP, &request, kIceUfrag[1], false);
cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
ASSERT_TRUE(conn1 != nullptr);
EXPECT_EQ(conn1, ch.best_connection());
conn1->ReceivedPingResponse();
EXPECT_EQ(conn1, ch.best_connection());
// Another connection is nominated via use_candidate.
ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 1));
cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
ASSERT_TRUE(conn2 != nullptr);
// Because it has a lower priority, the best connection is still conn1.
EXPECT_EQ(conn1, ch.best_connection());
// When it is nominated via use_candidate and writable, it is chosen as the
// best connection.
conn2->ReceivedPingResponse(); // Become writable.
conn2->set_nominated(true);
conn2->SignalNominated(conn2);
EXPECT_EQ(conn2, ch.best_connection());
// Another request with unknown address, it will not be set as the best
// connection because the best connection was nominated by the controlling
// side.
port->SignalUnknownAddress(port, rtc::SocketAddress("3.3.3.3", 3),
cricket::PROTO_UDP, &request, kIceUfrag[1], false);
cricket::Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3);
ASSERT_TRUE(conn3 != nullptr);
conn3->ReceivedPingResponse(); // Become writable.
EXPECT_EQ(conn2, ch.best_connection());
// However if the request contains use_candidate attribute, it will be
// selected as the best connection.
request.AddAttribute(
new cricket::StunByteStringAttribute(cricket::STUN_ATTR_USE_CANDIDATE));
port->SignalUnknownAddress(port, rtc::SocketAddress("4.4.4.4", 4),
cricket::PROTO_UDP, &request, kIceUfrag[1], false);
cricket::Connection* conn4 = WaitForConnectionTo(&ch, "4.4.4.4", 4);
ASSERT_TRUE(conn4 != nullptr);
// conn4 is not the best connection yet because it is not writable.
EXPECT_EQ(conn2, ch.best_connection());
conn4->ReceivedPingResponse(); // Become writable.
EXPECT_EQ(conn4, ch.best_connection());
}
// The controlled side will select a connection as the "best connection"
// based on media received until the controlling side nominates a connection,
// at which point the controlled side will select that connection as
// the "best connection".
TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBasedOnMediaReceived) {
cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
cricket::P2PTransportChannel ch("receiving state change", 1, nullptr, &pa);
PrepareChannel(&ch);
ch.SetIceRole(cricket::ICEROLE_CONTROLLED);
ch.Connect();
ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 10));
cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
ASSERT_TRUE(conn1 != nullptr);
EXPECT_EQ(conn1, ch.best_connection());
// If a data packet is received on conn2, the best connection should
// switch to conn2 because the controlled side must mirror the media path
// chosen by the controlling side.
ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 1));
cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
ASSERT_TRUE(conn2 != nullptr);
conn2->ReceivedPing(); // Become readable.
// Do not switch because it is not writable.
conn2->OnReadPacket("ABC", 3, rtc::CreatePacketTime(0));
EXPECT_EQ(conn1, ch.best_connection());
conn2->ReceivedPingResponse(); // Become writable.
// Switch because it is writable.
conn2->OnReadPacket("DEF", 3, rtc::CreatePacketTime(0));
EXPECT_EQ(conn2, ch.best_connection());
// Now another STUN message with an unknown address and use_candidate will
// nominate the best connection.
cricket::IceMessage request;
request.SetType(cricket::STUN_BINDING_REQUEST);
request.AddAttribute(new cricket::StunByteStringAttribute(
cricket::STUN_ATTR_USERNAME, kIceUfrag[1]));
uint32 prflx_priority = cricket::ICE_TYPE_PREFERENCE_PRFLX << 24;
request.AddAttribute(new cricket::StunUInt32Attribute(
cricket::STUN_ATTR_PRIORITY, prflx_priority));
request.AddAttribute(
new cricket::StunByteStringAttribute(cricket::STUN_ATTR_USE_CANDIDATE));
cricket::Port* port = GetPort(&ch);
port->SignalUnknownAddress(port, rtc::SocketAddress("3.3.3.3", 3),
cricket::PROTO_UDP, &request, kIceUfrag[1], false);
cricket::Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3);
ASSERT_TRUE(conn3 != nullptr);
EXPECT_EQ(conn2, ch.best_connection()); // Not writable yet.
conn3->ReceivedPingResponse(); // Become writable.
EXPECT_EQ(conn3, ch.best_connection());
// Now another data packet will not switch the best connection because the
// best connection was nominated by the controlling side.
conn2->ReceivedPing();
conn2->ReceivedPingResponse();
conn2->OnReadPacket("XYZ", 3, rtc::CreatePacketTime(0));
EXPECT_EQ(conn3, ch.best_connection());
}