blob: 62fddb2b581bbe57ae56380c2afa9f1b1bf36af3 [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 <algorithm>
#include <memory>
#include "webrtc/api/fakemetricsobserver.h"
#include "webrtc/p2p/base/fakeportallocator.h"
#include "webrtc/p2p/base/icetransportinternal.h"
#include "webrtc/p2p/base/p2ptransportchannel.h"
#include "webrtc/p2p/base/packettransportinternal.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/rtc_base/checks.h"
#include "webrtc/rtc_base/dscp.h"
#include "webrtc/rtc_base/fakeclock.h"
#include "webrtc/rtc_base/fakenetwork.h"
#include "webrtc/rtc_base/firewallsocketserver.h"
#include "webrtc/rtc_base/gunit.h"
#include "webrtc/rtc_base/helpers.h"
#include "webrtc/rtc_base/logging.h"
#include "webrtc/rtc_base/natserver.h"
#include "webrtc/rtc_base/natsocketfactory.h"
#include "webrtc/rtc_base/proxyserver.h"
#include "webrtc/rtc_base/ptr_util.h"
#include "webrtc/rtc_base/socketaddress.h"
#include "webrtc/rtc_base/ssladapter.h"
#include "webrtc/rtc_base/thread.h"
#include "webrtc/rtc_base/virtualsocketserver.h"
namespace {
using rtc::SocketAddress;
// Default timeout for tests in this file.
// Should be large enough for slow buildbots to run the tests reliably.
static const int kDefaultTimeout = 10000;
static const int kMediumTimeout = 3000;
static const int kShortTimeout = 1000;
static const int kOnlyLocalPorts = cricket::PORTALLOCATOR_DISABLE_STUN |
cricket::PORTALLOCATOR_DISABLE_RELAY |
cricket::PORTALLOCATOR_DISABLE_TCP;
static const int LOW_RTT = 20;
// 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("2600:0:1000:1b03:2e41:38ff:fea6:f2a4", 0)};
// For configuring multihomed clients.
static const SocketAddress kAlternateAddrs[2] = {
SocketAddress("101.101.101.101", 0), SocketAddress("202.202.202.202", 0)};
static const SocketAddress kIPv6AlternateAddrs[2] = {
SocketAddress("2401:4030:1:2c00:be30:abcd:efab:cdef", 0),
SocketAddress("2601:0:1000:1b03:2e41:38ff:fea6:f2a4", 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 turn server.
static const SocketAddress kTurnUdpIntAddr("99.99.99.3",
cricket::STUN_SERVER_PORT);
static const SocketAddress kTurnTcpIntAddr("99.99.99.4",
cricket::STUN_SERVER_PORT + 1);
static const SocketAddress kTurnUdpExtAddr("99.99.99.5", 0);
static const cricket::RelayCredentials kRelayCredentials("test", "test");
// Based on ICE_UFRAG_LENGTH
const char* kIceUfrag[4] = {"UF00", "UF01", "UF02", "UF03"};
// Based on ICE_PWD_LENGTH
const char* kIcePwd[4] = {
"TESTICEPWD00000000000000", "TESTICEPWD00000000000001",
"TESTICEPWD00000000000002", "TESTICEPWD00000000000003"};
const cricket::IceParameters kIceParams[4] = {
{kIceUfrag[0], kIcePwd[0], false},
{kIceUfrag[1], kIcePwd[1], false},
{kIceUfrag[2], kIcePwd[2], false},
{kIceUfrag[3], kIcePwd[3], false}};
const uint64_t kLowTiebreaker = 11111;
const uint64_t kHighTiebreaker = 22222;
enum { MSG_ADD_CANDIDATES, MSG_REMOVE_CANDIDATES };
cricket::IceConfig CreateIceConfig(
int receiving_timeout,
cricket::ContinualGatheringPolicy continual_gathering_policy,
int backup_ping_interval = -1) {
cricket::IceConfig config;
config.receiving_timeout = receiving_timeout;
config.continual_gathering_policy = continual_gathering_policy;
config.backup_connection_ping_interval = backup_ping_interval;
return config;
}
cricket::Candidate CreateUdpCandidate(const std::string& type,
const std::string& ip,
int port,
int priority,
const std::string& ufrag = "") {
cricket::Candidate c;
c.set_address(rtc::SocketAddress(ip, port));
c.set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);
c.set_protocol(cricket::UDP_PROTOCOL_NAME);
c.set_priority(priority);
c.set_username(ufrag);
c.set_type(type);
return c;
}
cricket::BasicPortAllocator* CreateBasicPortAllocator(
rtc::NetworkManager* network_manager,
const cricket::ServerAddresses& stun_servers,
const rtc::SocketAddress& turn_server_udp,
const rtc::SocketAddress& turn_server_tcp) {
cricket::RelayServerConfig turn_server(cricket::RELAY_TURN);
turn_server.credentials = kRelayCredentials;
if (!turn_server_udp.IsNil()) {
turn_server.ports.push_back(
cricket::ProtocolAddress(turn_server_udp, cricket::PROTO_UDP));
}
if (!turn_server_tcp.IsNil()) {
turn_server.ports.push_back(
cricket::ProtocolAddress(turn_server_tcp, cricket::PROTO_TCP));
}
std::vector<cricket::RelayServerConfig> turn_servers(1, turn_server);
cricket::BasicPortAllocator* allocator =
new cricket::BasicPortAllocator(network_manager);
allocator->SetConfiguration(stun_servers, turn_servers, 0, false);
return allocator;
}
} // namespace
namespace cricket {
// 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()
: vss_(new rtc::VirtualSocketServer()),
nss_(new rtc::NATSocketServer(vss_.get())),
ss_(new rtc::FirewallSocketServer(nss_.get())),
main_(ss_.get()),
stun_server_(TestStunServer::Create(&main_, kStunAddr)),
turn_server_(&main_, kTurnUdpIntAddr, kTurnUdpExtAddr),
socks_server1_(ss_.get(),
kSocksProxyAddrs[0],
ss_.get(),
kSocksProxyAddrs[0]),
socks_server2_(ss_.get(),
kSocksProxyAddrs[1],
ss_.get(),
kSocksProxyAddrs[1]),
force_relay_(false) {
ep1_.role_ = ICEROLE_CONTROLLING;
ep2_.role_ = ICEROLE_CONTROLLED;
ServerAddresses stun_servers;
stun_servers.insert(kStunAddr);
ep1_.allocator_.reset(
CreateBasicPortAllocator(&ep1_.network_manager_, stun_servers,
kTurnUdpIntAddr, rtc::SocketAddress()));
ep1_.metrics_observer_ =
new rtc::RefCountedObject<webrtc::FakeMetricsObserver>();
ep1_.allocator_->SetMetricsObserver(ep1_.metrics_observer_);
ep2_.allocator_.reset(
CreateBasicPortAllocator(&ep2_.network_manager_, stun_servers,
kTurnUdpIntAddr, rtc::SocketAddress()));
ep2_.metrics_observer_ =
new rtc::RefCountedObject<webrtc::FakeMetricsObserver>();
ep2_.allocator_->SetMetricsObserver(ep2_.metrics_observer_);
}
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& controlling_type,
const std::string& controlling_protocol,
const std::string& controlled_type,
const std::string& controlled_protocol,
int wait)
: controlling_type(controlling_type),
controlling_protocol(controlling_protocol),
controlled_type(controlled_type),
controlled_protocol(controlled_protocol),
connect_wait(wait) {}
// The expected candidate type and protocol of the controlling ICE agent.
std::string controlling_type;
std::string controlling_protocol;
// The expected candidate type and protocol of the controlled ICE agent.
std::string controlled_type;
std::string controlled_protocol;
// How long to wait before the correct candidate pair is selected.
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_;
std::unique_ptr<P2PTransportChannel> ch_;
};
struct CandidatesData : public rtc::MessageData {
CandidatesData(IceTransportInternal* ch, const Candidate& c)
: channel(ch), candidates(1, c) {}
CandidatesData(IceTransportInternal* ch, const std::vector<Candidate>& cc)
: channel(ch), candidates(cc) {}
IceTransportInternal* channel;
Candidates candidates;
};
struct Endpoint {
Endpoint()
: role_(ICEROLE_UNKNOWN),
tiebreaker_(0),
role_conflict_(false),
save_candidates_(false) {}
bool HasTransport(const rtc::PacketTransportInternal* transport) {
return (transport == cd1_.ch_.get() || transport == cd2_.ch_.get());
}
ChannelData* GetChannelData(rtc::PacketTransportInternal* transport) {
if (!HasTransport(transport))
return NULL;
if (cd1_.ch_.get() == transport)
return &cd1_;
else
return &cd2_;
}
void SetIceRole(IceRole role) { role_ = role; }
IceRole ice_role() { return role_; }
void SetIceTiebreaker(uint64_t tiebreaker) { tiebreaker_ = tiebreaker; }
uint64_t GetIceTiebreaker() { return tiebreaker_; }
void OnRoleConflict(bool role_conflict) { role_conflict_ = role_conflict; }
bool role_conflict() { return role_conflict_; }
void SetAllocationStepDelay(uint32_t delay) {
allocator_->set_step_delay(delay);
}
void SetAllowTcpListen(bool allow_tcp_listen) {
allocator_->set_allow_tcp_listen(allow_tcp_listen);
}
rtc::FakeNetworkManager network_manager_;
// |metrics_observer_| should outlive |allocator_| as the former may be
// used by the latter.
rtc::scoped_refptr<webrtc::FakeMetricsObserver> metrics_observer_;
std::unique_ptr<BasicPortAllocator> allocator_;
ChannelData cd1_;
ChannelData cd2_;
IceRole role_;
uint64_t tiebreaker_;
bool role_conflict_;
bool save_candidates_;
std::vector<std::unique_ptr<CandidatesData>> saved_candidates_;
bool ready_to_send_ = false;
};
ChannelData* GetChannelData(rtc::PacketTransportInternal* transport) {
if (ep1_.HasTransport(transport))
return ep1_.GetChannelData(transport);
else
return ep2_.GetChannelData(transport);
}
IceParameters IceParamsWithRenomination(const IceParameters& ice,
bool renomination) {
IceParameters new_ice = ice;
new_ice.renomination = renomination;
return new_ice;
}
void CreateChannels(const IceConfig& ep1_config,
const IceConfig& ep2_config,
bool renomination = false) {
IceParameters ice_ep1_cd1_ch =
IceParamsWithRenomination(kIceParams[0], renomination);
IceParameters ice_ep2_cd1_ch =
IceParamsWithRenomination(kIceParams[1], renomination);
ep1_.cd1_.ch_.reset(CreateChannel(0, ICE_CANDIDATE_COMPONENT_DEFAULT,
ice_ep1_cd1_ch, ice_ep2_cd1_ch));
ep2_.cd1_.ch_.reset(CreateChannel(1, ICE_CANDIDATE_COMPONENT_DEFAULT,
ice_ep2_cd1_ch, ice_ep1_cd1_ch));
ep1_.cd1_.ch_->SetMetricsObserver(ep1_.metrics_observer_);
ep2_.cd1_.ch_->SetMetricsObserver(ep2_.metrics_observer_);
ep1_.cd1_.ch_->SetIceConfig(ep1_config);
ep2_.cd1_.ch_->SetIceConfig(ep2_config);
ep1_.cd1_.ch_->MaybeStartGathering();
ep2_.cd1_.ch_->MaybeStartGathering();
}
void CreateChannels() {
IceConfig default_config;
CreateChannels(default_config, default_config, false);
}
P2PTransportChannel* CreateChannel(int endpoint,
int component,
const IceParameters& local_ice,
const IceParameters& remote_ice) {
P2PTransportChannel* channel = new P2PTransportChannel(
"test content name", component, GetAllocator(endpoint));
channel->SignalReadyToSend.connect(
this, &P2PTransportChannelTestBase::OnReadyToSend);
channel->SignalCandidateGathered.connect(
this, &P2PTransportChannelTestBase::OnCandidateGathered);
channel->SignalCandidatesRemoved.connect(
this, &P2PTransportChannelTestBase::OnCandidatesRemoved);
channel->SignalReadPacket.connect(
this, &P2PTransportChannelTestBase::OnReadPacket);
channel->SignalRoleConflict.connect(
this, &P2PTransportChannelTestBase::OnRoleConflict);
channel->SignalSelectedCandidatePairChanged.connect(
this, &P2PTransportChannelTestBase::OnSelectedCandidatePairChanged);
channel->SetIceParameters(local_ice);
if (remote_ice_parameter_source_ == FROM_SETICEPARAMETERS) {
channel->SetRemoteIceParameters(remote_ice);
}
channel->SetIceRole(GetEndpoint(endpoint)->ice_role());
channel->SetIceTiebreaker(GetEndpoint(endpoint)->GetIceTiebreaker());
return channel;
}
void DestroyChannels() {
ep1_.cd1_.ch_.reset();
ep2_.cd1_.ch_.reset();
ep1_.cd2_.ch_.reset();
ep2_.cd2_.ch_.reset();
}
P2PTransportChannel* ep1_ch1() { return ep1_.cd1_.ch_.get(); }
P2PTransportChannel* ep1_ch2() { return ep1_.cd2_.ch_.get(); }
P2PTransportChannel* ep2_ch1() { return ep2_.cd1_.ch_.get(); }
P2PTransportChannel* ep2_ch2() { return ep2_.cd2_.ch_.get(); }
TestTurnServer* test_turn_server() { return &turn_server_; }
rtc::VirtualSocketServer* virtual_socket_server() { return vss_.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 kStunUdpToPrflxUdp;
static const Result kPrflxUdpToStunUdp;
static const Result kLocalUdpToRelayUdp;
static const Result kPrflxUdpToRelayUdp;
static const Result kRelayUdpToPrflxUdp;
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;
}
}
BasicPortAllocator* GetAllocator(int endpoint) {
return GetEndpoint(endpoint)->allocator_.get();
}
webrtc::FakeMetricsObserver* GetMetricsObserver(int endpoint) {
return GetEndpoint(endpoint)->metrics_observer_;
}
void AddAddress(int endpoint, const SocketAddress& addr) {
GetEndpoint(endpoint)->network_manager_.AddInterface(addr);
}
void AddAddress(int endpoint,
const SocketAddress& addr,
const std::string& ifname,
rtc::AdapterType adapter_type) {
GetEndpoint(endpoint)->network_manager_.AddInterface(addr, ifname,
adapter_type);
}
void RemoveAddress(int endpoint, const SocketAddress& addr) {
GetEndpoint(endpoint)->network_manager_.RemoveInterface(addr);
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, 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 SetIceRole(int endpoint, IceRole role) {
GetEndpoint(endpoint)->SetIceRole(role);
}
void SetIceTiebreaker(int endpoint, uint64_t tiebreaker) {
GetEndpoint(endpoint)->SetIceTiebreaker(tiebreaker);
}
bool GetRoleConflict(int endpoint) {
return GetEndpoint(endpoint)->role_conflict();
}
void SetAllocationStepDelay(int endpoint, uint32_t delay) {
return GetEndpoint(endpoint)->SetAllocationStepDelay(delay);
}
void SetAllowTcpListen(int endpoint, bool allow_tcp_listen) {
return GetEndpoint(endpoint)->SetAllowTcpListen(allow_tcp_listen);
}
// Return true if the approprite parts of the expected Result, based
// on the local and remote candidate of ep1_ch1, match. This can be
// used in an EXPECT_TRUE_WAIT.
bool CheckCandidate1(const Result& expected) {
const std::string& local_type = LocalCandidate(ep1_ch1())->type();
const std::string& local_protocol = LocalCandidate(ep1_ch1())->protocol();
const std::string& remote_type = RemoteCandidate(ep1_ch1())->type();
const std::string& remote_protocol = RemoteCandidate(ep1_ch1())->protocol();
return (local_protocol == expected.controlling_protocol &&
remote_protocol == expected.controlled_protocol &&
local_type == expected.controlling_type &&
remote_type == expected.controlled_type);
}
// EXPECT_EQ on the approprite parts of the expected Result, based
// on the local and remote candidate of ep1_ch1. This is like
// CheckCandidate1, except that it will provide more detail about
// what didn't match.
void ExpectCandidate1(const Result& expected) {
if (CheckCandidate1(expected)) {
return;
}
const std::string& local_type = LocalCandidate(ep1_ch1())->type();
const std::string& local_protocol = LocalCandidate(ep1_ch1())->protocol();
const std::string& remote_type = RemoteCandidate(ep1_ch1())->type();
const std::string& remote_protocol = RemoteCandidate(ep1_ch1())->protocol();
EXPECT_EQ(expected.controlling_type, local_type);
EXPECT_EQ(expected.controlled_type, remote_type);
EXPECT_EQ(expected.controlling_protocol, local_protocol);
EXPECT_EQ(expected.controlled_protocol, remote_protocol);
}
// Return true if the approprite parts of the expected Result, based
// on the local and remote candidate of ep2_ch1, match. This can be
// used in an EXPECT_TRUE_WAIT.
bool CheckCandidate2(const Result& expected) {
const std::string& local_type = LocalCandidate(ep2_ch1())->type();
const std::string& local_protocol = LocalCandidate(ep2_ch1())->protocol();
const std::string& remote_type = RemoteCandidate(ep2_ch1())->type();
const std::string& remote_protocol = RemoteCandidate(ep2_ch1())->protocol();
return (local_protocol == expected.controlled_protocol &&
remote_protocol == expected.controlling_protocol &&
local_type == expected.controlled_type &&
remote_type == expected.controlling_type);
}
// EXPECT_EQ on the approprite parts of the expected Result, based
// on the local and remote candidate of ep2_ch1. This is like
// CheckCandidate2, except that it will provide more detail about
// what didn't match.
void ExpectCandidate2(const Result& expected) {
if (CheckCandidate2(expected)) {
return;
}
const std::string& local_type = LocalCandidate(ep2_ch1())->type();
const std::string& local_protocol = LocalCandidate(ep2_ch1())->protocol();
const std::string& remote_type = RemoteCandidate(ep2_ch1())->type();
const std::string& remote_protocol = RemoteCandidate(ep2_ch1())->protocol();
EXPECT_EQ(expected.controlled_type, local_type);
EXPECT_EQ(expected.controlling_type, remote_type);
EXPECT_EQ(expected.controlled_protocol, local_protocol);
EXPECT_EQ(expected.controlling_protocol, remote_protocol);
}
void Test(const Result& expected) {
rtc::ScopedFakeClock clock;
int64_t connect_start = rtc::TimeMillis();
int64_t connect_time;
// Create the channels and wait for them to connect.
CreateChannels();
EXPECT_TRUE_SIMULATED_WAIT(
ep1_ch1() != NULL && ep2_ch1() != NULL && ep1_ch1()->receiving() &&
ep1_ch1()->writable() && ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
expected.connect_wait + kShortTimeout, clock);
connect_time = rtc::TimeMillis() - 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 selected connections to emerge.
// This may take up to 2 seconds.
if (ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection()) {
int64_t converge_start = rtc::TimeMillis();
int64_t converge_time;
// Verifying local and remote channel selected 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.
EXPECT_TRUE_SIMULATED_WAIT(
CheckCandidate1(expected) && CheckCandidate2(expected),
kMediumTimeout, clock);
// Also do EXPECT_EQ on each part so that failures are more verbose.
ExpectCandidate1(expected);
ExpectCandidate2(expected);
converge_time = rtc::TimeMillis() - converge_start;
int64_t converge_wait = 2000;
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(clock);
// Destroy the channels, and wait for them to be fully cleaned up.
DestroyChannels();
}
void TestSendRecv(rtc::FakeClock& clock) {
for (int i = 0; i < 10; ++i) {
const char* data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
int len = static_cast<int>(strlen(data));
// local_channel1 <==> remote_channel1
EXPECT_EQ_SIMULATED_WAIT(len, SendData(ep1_ch1(), data, len),
kMediumTimeout, clock);
EXPECT_TRUE_SIMULATED_WAIT(CheckDataOnChannel(ep2_ch1(), data, len),
kMediumTimeout, clock);
EXPECT_EQ_SIMULATED_WAIT(len, SendData(ep2_ch1(), data, len),
kMediumTimeout, clock);
EXPECT_TRUE_SIMULATED_WAIT(CheckDataOnChannel(ep1_ch1(), data, len),
kMediumTimeout, clock);
}
}
// This test waits for the transport to become receiving and writable on both
// end points. Once they are, the end points set new local ice parameters and
// 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() {
rtc::ScopedFakeClock clock;
ep1_ch1()->SetRemoteIceParameters(kIceParams[1]);
ep2_ch1()->SetRemoteIceParameters(kIceParams[0]);
EXPECT_TRUE_SIMULATED_WAIT(
ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() && ep2_ch1()->writable(),
kMediumTimeout, clock);
const Candidate* old_local_candidate1 = LocalCandidate(ep1_ch1());
const Candidate* old_local_candidate2 = LocalCandidate(ep2_ch1());
const Candidate* old_remote_candidate1 = RemoteCandidate(ep1_ch1());
const Candidate* old_remote_candidate2 = RemoteCandidate(ep2_ch1());
ep1_ch1()->SetIceParameters(kIceParams[2]);
ep1_ch1()->SetRemoteIceParameters(kIceParams[3]);
ep1_ch1()->MaybeStartGathering();
ep2_ch1()->SetIceParameters(kIceParams[3]);
ep2_ch1()->SetRemoteIceParameters(kIceParams[2]);
ep2_ch1()->MaybeStartGathering();
EXPECT_TRUE_SIMULATED_WAIT(LocalCandidate(ep1_ch1())->generation() !=
old_local_candidate1->generation(),
kMediumTimeout, clock);
EXPECT_TRUE_SIMULATED_WAIT(LocalCandidate(ep2_ch1())->generation() !=
old_local_candidate2->generation(),
kMediumTimeout, clock);
EXPECT_TRUE_SIMULATED_WAIT(RemoteCandidate(ep1_ch1())->generation() !=
old_remote_candidate1->generation(),
kMediumTimeout, clock);
EXPECT_TRUE_SIMULATED_WAIT(RemoteCandidate(ep2_ch1())->generation() !=
old_remote_candidate2->generation(),
kMediumTimeout, clock);
EXPECT_EQ(1u, RemoteCandidate(ep2_ch1())->generation());
EXPECT_EQ(1u, RemoteCandidate(ep1_ch1())->generation());
}
void TestSignalRoleConflict() {
rtc::ScopedFakeClock clock;
// Default EP1 is in controlling state.
SetIceTiebreaker(0, kLowTiebreaker);
SetIceRole(1, ICEROLE_CONTROLLING);
SetIceTiebreaker(1, kHighTiebreaker);
// Creating channels with both channels role set to CONTROLLING.
CreateChannels();
// Since both the channels initiated with controlling state and channel2
// has higher tiebreaker value, channel1 should receive SignalRoleConflict.
EXPECT_TRUE_SIMULATED_WAIT(GetRoleConflict(0), kShortTimeout, clock);
EXPECT_FALSE(GetRoleConflict(1));
EXPECT_TRUE_SIMULATED_WAIT(
ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() && ep2_ch1()->writable(),
kShortTimeout, clock);
EXPECT_TRUE(ep1_ch1()->selected_connection() &&
ep2_ch1()->selected_connection());
TestSendRecv(clock);
}
void OnReadyToSend(rtc::PacketTransportInternal* transport) {
GetEndpoint(transport)->ready_to_send_ = true;
}
// We pass the candidates directly to the other side.
void OnCandidateGathered(IceTransportInternal* ch, const Candidate& c) {
if (force_relay_ && c.type() != RELAY_PORT_TYPE)
return;
if (GetEndpoint(ch)->save_candidates_) {
GetEndpoint(ch)->saved_candidates_.push_back(
std::unique_ptr<CandidatesData>(new CandidatesData(ch, c)));
} else {
main_.Post(RTC_FROM_HERE, this, MSG_ADD_CANDIDATES,
new CandidatesData(ch, c));
}
}
void OnSelectedCandidatePairChanged(
IceTransportInternal* transport_channel,
CandidatePairInterface* selected_candidate_pair,
int last_sent_packet_id,
bool ready_to_send) {
// Do not count if it switches to nullptr. This may happen if all
// connections timed out.
if (selected_candidate_pair != nullptr) {
++selected_candidate_pair_switches_;
}
}
int reset_selected_candidate_pair_switches() {
int switches = selected_candidate_pair_switches_;
selected_candidate_pair_switches_ = 0;
return switches;
}
void PauseCandidates(int endpoint) {
GetEndpoint(endpoint)->save_candidates_ = true;
}
void OnCandidatesRemoved(IceTransportInternal* ch,
const std::vector<Candidate>& candidates) {
// Candidate removals are not paused.
CandidatesData* candidates_data = new CandidatesData(ch, candidates);
main_.Post(RTC_FROM_HERE, this, MSG_REMOVE_CANDIDATES, candidates_data);
}
// Tcp candidate verification has to be done when they are generated.
void VerifySavedTcpCandidates(int endpoint, const std::string& tcptype) {
for (auto& data : GetEndpoint(endpoint)->saved_candidates_) {
for (auto& candidate : data->candidates) {
EXPECT_EQ(candidate.protocol(), TCP_PROTOCOL_NAME);
EXPECT_EQ(candidate.tcptype(), tcptype);
if (candidate.tcptype() == TCPTYPE_ACTIVE_STR) {
EXPECT_EQ(candidate.address().port(), DISCARD_PORT);
} else if (candidate.tcptype() == TCPTYPE_PASSIVE_STR) {
EXPECT_NE(candidate.address().port(), DISCARD_PORT);
} else {
FAIL() << "Unknown tcptype: " << candidate.tcptype();
}
}
}
}
void ResumeCandidates(int endpoint) {
Endpoint* ed = GetEndpoint(endpoint);
for (auto& candidate : ed->saved_candidates_) {
main_.Post(RTC_FROM_HERE, this, MSG_ADD_CANDIDATES, candidate.release());
}
ed->saved_candidates_.clear();
ed->save_candidates_ = false;
}
void OnMessage(rtc::Message* msg) {
switch (msg->message_id) {
case MSG_ADD_CANDIDATES: {
std::unique_ptr<CandidatesData> data(
static_cast<CandidatesData*>(msg->pdata));
P2PTransportChannel* rch = GetRemoteChannel(data->channel);
if (!rch) {
return;
}
for (auto& c : data->candidates) {
if (remote_ice_parameter_source_ != FROM_CANDIDATE) {
c.set_username("");
c.set_password("");
}
LOG(LS_INFO) << "Candidate(" << data->channel->component() << "->"
<< rch->component() << "): " << c.ToString();
rch->AddRemoteCandidate(c);
}
break;
}
case MSG_REMOVE_CANDIDATES: {
std::unique_ptr<CandidatesData> data(
static_cast<CandidatesData*>(msg->pdata));
P2PTransportChannel* rch = GetRemoteChannel(data->channel);
if (!rch) {
return;
}
for (Candidate& c : data->candidates) {
LOG(LS_INFO) << "Removed remote candidate " << c.ToString();
rch->RemoveRemoteCandidate(c);
}
break;
}
}
}
void OnReadPacket(rtc::PacketTransportInternal* transport,
const char* data,
size_t len,
const rtc::PacketTime& packet_time,
int flags) {
std::list<std::string>& packets = GetPacketList(transport);
packets.push_front(std::string(data, len));
}
void OnRoleConflict(IceTransportInternal* channel) {
GetEndpoint(channel)->OnRoleConflict(true);
IceRole new_role = GetEndpoint(channel)->ice_role() == ICEROLE_CONTROLLING
? ICEROLE_CONTROLLED
: ICEROLE_CONTROLLING;
channel->SetIceRole(new_role);
}
int SendData(IceTransportInternal* channel, const char* data, size_t len) {
rtc::PacketOptions options;
return channel->SendPacket(data, len, options, 0);
}
bool CheckDataOnChannel(IceTransportInternal* channel,
const char* data,
int len) {
return GetChannelData(channel)->CheckData(data, len);
}
static const Candidate* LocalCandidate(P2PTransportChannel* ch) {
return (ch && ch->selected_connection())
? &ch->selected_connection()->local_candidate()
: NULL;
}
static const Candidate* RemoteCandidate(P2PTransportChannel* ch) {
return (ch && ch->selected_connection())
? &ch->selected_connection()->remote_candidate()
: NULL;
}
Endpoint* GetEndpoint(rtc::PacketTransportInternal* transport) {
if (ep1_.HasTransport(transport)) {
return &ep1_;
} else if (ep2_.HasTransport(transport)) {
return &ep2_;
} else {
return NULL;
}
}
P2PTransportChannel* GetRemoteChannel(IceTransportInternal* 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(
rtc::PacketTransportInternal* transport) {
return GetChannelData(transport)->ch_packets_;
}
enum RemoteIceParameterSource { FROM_CANDIDATE, FROM_SETICEPARAMETERS };
// How does the test pass ICE parameters to the P2PTransportChannel?
// On the candidate itself, or through SetRemoteIceParameters?
// Goes through the candidate itself by default.
void set_remote_ice_parameter_source(RemoteIceParameterSource source) {
remote_ice_parameter_source_ = source;
}
void set_force_relay(bool relay) {
force_relay_ = relay;
}
void ConnectSignalNominated(Connection* conn) {
conn->SignalNominated.connect(this,
&P2PTransportChannelTestBase::OnNominated);
}
void OnNominated(Connection* conn) { nominated_ = true; }
bool nominated() { return nominated_; }
private:
std::unique_ptr<rtc::VirtualSocketServer> vss_;
std::unique_ptr<rtc::NATSocketServer> nss_;
std::unique_ptr<rtc::FirewallSocketServer> ss_;
rtc::AutoSocketServerThread main_;
std::unique_ptr<TestStunServer> stun_server_;
TestTurnServer turn_server_;
rtc::SocksProxyServer socks_server1_;
rtc::SocksProxyServer socks_server2_;
Endpoint ep1_;
Endpoint ep2_;
RemoteIceParameterSource remote_ice_parameter_source_ = FROM_CANDIDATE;
bool force_relay_;
int selected_candidate_pair_switches_ = 0;
bool nominated_ = false;
};
// The tests have only a few outcomes, which we predefine.
const P2PTransportChannelTestBase::Result
P2PTransportChannelTestBase::kLocalUdpToLocalUdp("local",
"udp",
"local",
"udp",
1000);
const P2PTransportChannelTestBase::Result
P2PTransportChannelTestBase::kLocalUdpToStunUdp("local",
"udp",
"stun",
"udp",
1000);
const P2PTransportChannelTestBase::Result
P2PTransportChannelTestBase::kLocalUdpToPrflxUdp("local",
"udp",
"prflx",
"udp",
1000);
const P2PTransportChannelTestBase::Result
P2PTransportChannelTestBase::kPrflxUdpToLocalUdp("prflx",
"udp",
"local",
"udp",
1000);
const P2PTransportChannelTestBase::Result
P2PTransportChannelTestBase::kStunUdpToLocalUdp("stun",
"udp",
"local",
"udp",
1000);
const P2PTransportChannelTestBase::Result
P2PTransportChannelTestBase::kStunUdpToStunUdp("stun",
"udp",
"stun",
"udp",
1000);
const P2PTransportChannelTestBase::Result
P2PTransportChannelTestBase::kStunUdpToPrflxUdp("stun",
"udp",
"prflx",
"udp",
1000);
const P2PTransportChannelTestBase::Result
P2PTransportChannelTestBase::kPrflxUdpToStunUdp("prflx",
"udp",
"stun",
"udp",
1000);
const P2PTransportChannelTestBase::Result
P2PTransportChannelTestBase::kLocalUdpToRelayUdp("local",
"udp",
"relay",
"udp",
2000);
const P2PTransportChannelTestBase::Result
P2PTransportChannelTestBase::kPrflxUdpToRelayUdp("prflx",
"udp",
"relay",
"udp",
2000);
const P2PTransportChannelTestBase::Result
P2PTransportChannelTestBase::kRelayUdpToPrflxUdp("relay",
"udp",
"prflx",
"udp",
2000);
const P2PTransportChannelTestBase::Result
P2PTransportChannelTestBase::kLocalTcpToLocalTcp("local",
"tcp",
"local",
"tcp",
3000);
const P2PTransportChannelTestBase::Result
P2PTransportChannelTestBase::kLocalTcpToPrflxTcp("local",
"tcp",
"prflx",
"tcp",
3000);
const P2PTransportChannelTestBase::Result
P2PTransportChannelTestBase::kPrflxTcpToLocalTcp("prflx",
"tcp",
"local",
"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];
void ConfigureEndpoints(Config config1,
Config config2,
int allocator_flags1,
int allocator_flags2) {
ConfigureEndpoint(0, config1);
SetAllocatorFlags(0, allocator_flags1);
SetAllocationStepDelay(0, kMinimumStepDelay);
ConfigureEndpoint(1, config2);
SetAllocatorFlags(1, allocator_flags2);
SetAllocationStepDelay(1, kMinimumStepDelay);
set_remote_ice_parameter_source(FROM_SETICEPARAMETERS);
}
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:
RTC_NOTREACHED();
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 SUPU &kStunUdpToPrflxUdp
#define PUSU &kPrflxUdpToStunUdp
#define LURU &kLocalUdpToRelayUdp
#define PURU &kPrflxUdpToRelayUdp
#define RUPU &kRelayUdpToPrflxUdp
#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.
// 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.
const P2PTransportChannelTest::Result*
P2PTransportChannelTest::kMatrix[NUM_CONFIGS][NUM_CONFIGS] = {
// OPEN CONE ADDR PORT SYMM 2CON SCON !UDP !TCP HTTP PRXH PRXS
/*OP*/ {LULU, LUSU, LUSU, LUSU, LUPU, LUSU, LUPU, LTPT, LTPT, LSRS, NULL, LTPT},
/*CO*/ {SULU, SUSU, SUSU, SUSU, SUPU, SUSU, SUPU, NULL, NULL, LSRS, NULL, LTRT},
/*AD*/ {SULU, SUSU, SUSU, SUSU, SUPU, SUSU, SUPU, NULL, NULL, LSRS, NULL, LTRT},
/*PO*/ {SULU, SUSU, SUSU, SUSU, RUPU, SUSU, RUPU, NULL, NULL, LSRS, NULL, LTRT},
/*SY*/ {PULU, PUSU, PUSU, PURU, PURU, PUSU, PURU, NULL, NULL, LSRS, NULL, LTRT},
/*2C*/ {SULU, SUSU, SUSU, SUSU, SUPU, SUSU, SUPU, NULL, NULL, LSRS, NULL, LTRT},
/*SC*/ {PULU, PUSU, PUSU, PURU, PURU, PUSU, PURU, NULL, NULL, LSRS, NULL, LTRT},
/*!U*/ {LTPT, NULL, NULL, NULL, NULL, NULL, NULL, LTPT, LTPT, LSRS, NULL, LTRT},
/*!T*/ {PTLT, 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
#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 \
LOG(LS_WARNING) << "Not yet implemented"; \
}
#define P2P_TEST(x, y) \
P2P_TEST_DECLARATION(x, y,)
#define 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, HandleUfragPwdChange) {
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
CreateChannels();
TestHandleIceUfragPasswordChanged();
DestroyChannels();
}
// Same as above test, but with a symmetric NAT.
// We should end up with relay<->prflx candidate pairs, with generation "1".
TEST_F(P2PTransportChannelTest, HandleUfragPwdChangeSymmetricNat) {
ConfigureEndpoints(NAT_SYMMETRIC, NAT_SYMMETRIC, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
CreateChannels();
TestHandleIceUfragPasswordChanged();
DestroyChannels();
}
// Test the operation of GetStats.
TEST_F(P2PTransportChannelTest, GetStats) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
CreateChannels();
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kMediumTimeout, clock);
TestSendRecv(clock);
ConnectionInfos infos;
ASSERT_TRUE(ep1_ch1()->GetStats(&infos));
ASSERT_TRUE(infos.size() >= 1);
ConnectionInfo* best_conn_info = nullptr;
for (ConnectionInfo& info : infos) {
if (info.best_connection) {
best_conn_info = &info;
break;
}
}
ASSERT_TRUE(best_conn_info != nullptr);
EXPECT_TRUE(best_conn_info->new_connection);
EXPECT_TRUE(best_conn_info->receiving);
EXPECT_TRUE(best_conn_info->writable);
EXPECT_FALSE(best_conn_info->timeout);
EXPECT_EQ(10U, best_conn_info->sent_total_packets);
EXPECT_EQ(0U, best_conn_info->sent_discarded_packets);
EXPECT_EQ(10 * 36U, best_conn_info->sent_total_bytes);
EXPECT_EQ(10 * 36U, best_conn_info->recv_total_bytes);
DestroyChannels();
}
// Tests that UMAs are recorded when ICE restarts while the channel
// is disconnected.
TEST_F(P2PTransportChannelTest, TestUMAIceRestartWhileDisconnected) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts);
CreateChannels();
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kDefaultTimeout, clock);
// Drop all packets so that both channels become not writable.
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kPublicAddrs[0]);
const int kWriteTimeoutDelay = 8000;
EXPECT_TRUE_SIMULATED_WAIT(!ep1_ch1()->writable() && !ep2_ch1()->writable(),
kWriteTimeoutDelay, clock);
ep1_ch1()->SetIceParameters(kIceParams[2]);
ep1_ch1()->SetRemoteIceParameters(kIceParams[3]);
ep1_ch1()->MaybeStartGathering();
EXPECT_EQ(1, GetMetricsObserver(0)->GetEnumCounter(
webrtc::kEnumCounterIceRestart,
static_cast<int>(IceRestartState::DISCONNECTED)));
ep2_ch1()->SetIceParameters(kIceParams[3]);
ep2_ch1()->SetRemoteIceParameters(kIceParams[2]);
ep2_ch1()->MaybeStartGathering();
EXPECT_EQ(1, GetMetricsObserver(1)->GetEnumCounter(
webrtc::kEnumCounterIceRestart,
static_cast<int>(IceRestartState::DISCONNECTED)));
DestroyChannels();
}
// Tests that UMAs are recorded when ICE restarts while the channel
// is connected.
TEST_F(P2PTransportChannelTest, TestUMAIceRestartWhileConnected) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts);
CreateChannels();
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kDefaultTimeout, clock);
ep1_ch1()->SetIceParameters(kIceParams[2]);
ep1_ch1()->SetRemoteIceParameters(kIceParams[3]);
ep1_ch1()->MaybeStartGathering();
EXPECT_EQ(1, GetMetricsObserver(0)->GetEnumCounter(
webrtc::kEnumCounterIceRestart,
static_cast<int>(IceRestartState::CONNECTED)));
ep2_ch1()->SetIceParameters(kIceParams[3]);
ep2_ch1()->SetRemoteIceParameters(kIceParams[2]);
ep2_ch1()->MaybeStartGathering();
EXPECT_EQ(1, GetMetricsObserver(1)->GetEnumCounter(
webrtc::kEnumCounterIceRestart,
static_cast<int>(IceRestartState::CONNECTED)));
DestroyChannels();
}
// Tests that UMAs are recorded when ICE restarts while the channel
// is connecting.
TEST_F(P2PTransportChannelTest, TestUMAIceRestartWhileConnecting) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts);
// Create the channels without waiting for them to become connected.
CreateChannels();
ep1_ch1()->SetIceParameters(kIceParams[2]);
ep1_ch1()->SetRemoteIceParameters(kIceParams[3]);
ep1_ch1()->MaybeStartGathering();
EXPECT_EQ(1, GetMetricsObserver(0)->GetEnumCounter(
webrtc::kEnumCounterIceRestart,
static_cast<int>(IceRestartState::CONNECTING)));
ep2_ch1()->SetIceParameters(kIceParams[3]);
ep2_ch1()->SetRemoteIceParameters(kIceParams[2]);
ep2_ch1()->MaybeStartGathering();
EXPECT_EQ(1, GetMetricsObserver(1)->GetEnumCounter(
webrtc::kEnumCounterIceRestart,
static_cast<int>(IceRestartState::CONNECTING)));
DestroyChannels();
}
// Tests that a UMA on ICE regathering is recorded when there is a network
// change if and only if continual gathering is enabled.
TEST_F(P2PTransportChannelTest,
TestIceRegatheringReasonContinualGatheringByNetworkChange) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts);
// ep1 gathers continually but ep2 does not.
IceConfig continual_gathering_config =
CreateIceConfig(1000, GATHER_CONTINUALLY);
IceConfig default_config;
CreateChannels(continual_gathering_config, default_config);
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kDefaultTimeout, clock);
// Adding address in ep1 will trigger continual gathering.
AddAddress(0, kAlternateAddrs[0]);
EXPECT_EQ_SIMULATED_WAIT(
1, GetMetricsObserver(0)->GetEnumCounter(
webrtc::kEnumCounterIceRegathering,
static_cast<int>(IceRegatheringReason::NETWORK_CHANGE)),
kDefaultTimeout, clock);
ep2_ch1()->SetIceParameters(kIceParams[3]);
ep2_ch1()->SetRemoteIceParameters(kIceParams[2]);
ep2_ch1()->MaybeStartGathering();
AddAddress(1, kAlternateAddrs[1]);
SIMULATED_WAIT(false, kDefaultTimeout, clock);
// ep2 has not enabled continual gathering.
EXPECT_EQ(0, GetMetricsObserver(1)->GetEnumCounter(
webrtc::kEnumCounterIceRegathering,
static_cast<int>(IceRegatheringReason::NETWORK_CHANGE)));
DestroyChannels();
}
// Tests that a UMA on ICE regathering is recorded when there is a network
// failure if and only if continual gathering is enabled.
TEST_F(P2PTransportChannelTest,
TestIceRegatheringReasonContinualGatheringByNetworkFailure) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts);
// ep1 gathers continually but ep2 does not.
IceConfig config1 = CreateIceConfig(1000, GATHER_CONTINUALLY);
config1.regather_on_failed_networks_interval = rtc::Optional<int>(2000);
IceConfig config2;
config2.regather_on_failed_networks_interval = rtc::Optional<int>(2000);
CreateChannels(config1, config2);
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kDefaultTimeout, clock);
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kPublicAddrs[0]);
// Timeout value such that all connections are deleted.
const int kNetworkFailureTimeout = 35000;
SIMULATED_WAIT(false, kNetworkFailureTimeout, clock);
EXPECT_LE(1, GetMetricsObserver(0)->GetEnumCounter(
webrtc::kEnumCounterIceRegathering,
static_cast<int>(IceRegatheringReason::NETWORK_FAILURE)));
EXPECT_EQ(0, GetMetricsObserver(1)->GetEnumCounter(
webrtc::kEnumCounterIceRegathering,
static_cast<int>(IceRegatheringReason::NETWORK_FAILURE)));
DestroyChannels();
}
// Tests that ICE regathering occurs regularly when
// regather_all_networks_interval_range configuration value is set.
TEST_F(P2PTransportChannelTest, TestIceRegatherOnAllNetworksContinual) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts);
// ep1 gathers continually but ep2 does not.
const int kRegatherInterval = 2000;
IceConfig config1 = CreateIceConfig(1000, GATHER_CONTINUALLY);
config1.regather_all_networks_interval_range.emplace(
kRegatherInterval, kRegatherInterval);
IceConfig config2;
CreateChannels(config1, config2);
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kDefaultTimeout, clock);
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kPublicAddrs[0]);
// Timeout value such that all connections are deleted.
const int kNetworkGatherDuration = 11000;
SIMULATED_WAIT(false, kNetworkGatherDuration, clock);
// Expect regathering to happen 5 times in 11s with 2s interval.
EXPECT_LE(5, GetMetricsObserver(0)->GetEnumCounter(
webrtc::kEnumCounterIceRegathering,
static_cast<int>(IceRegatheringReason::OCCASIONAL_REFRESH)));
// Expect no regathering if continual gathering not configured.
EXPECT_EQ(0, GetMetricsObserver(1)->GetEnumCounter(
webrtc::kEnumCounterIceRegathering,
static_cast<int>(IceRegatheringReason::OCCASIONAL_REFRESH)));
DestroyChannels();
}
// Test that ICE periodic regathering can change the selected connection on the
// specified interval and that the peers can communicate over the new
// connection. The test is parameterized to test that it works when regathering
// is done by the ICE controlling peer and when done by the controlled peer.
class P2PTransportRegatherAllNetworksTest : public P2PTransportChannelTest {
protected:
void TestWithRoles(IceRole p1_role, IceRole p2_role) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(NAT_SYMMETRIC, NAT_SYMMETRIC, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
set_force_relay(true);
const int kRegatherInterval = 2000;
const int kNumRegathers = 2;
// Set up peer 1 to auto regather every 2s.
IceConfig config1 = CreateIceConfig(1000, GATHER_CONTINUALLY);
config1.regather_all_networks_interval_range.emplace(
kRegatherInterval, kRegatherInterval);
IceConfig config2 = CreateIceConfig(1000, GATHER_CONTINUALLY);
// Set peer roles.
SetIceRole(0, p1_role);
SetIceRole(1, p2_role);
CreateChannels(config1, config2);
// Wait for initial connection to be made.
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() &&
ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kMediumTimeout, clock);
const Connection* initial_selected = ep1_ch1()->selected_connection();
// Wait long enough for 2 regathering cycles to happen plus some extra so
// the new connection has time to settle.
const int kWaitRegather =
kRegatherInterval * kNumRegathers + kRegatherInterval / 2;
SIMULATED_WAIT(false, kWaitRegather, clock);
EXPECT_EQ(kNumRegathers, GetMetricsObserver(0)->GetEnumCounter(
webrtc::kEnumCounterIceRegathering,
static_cast<int>(IceRegatheringReason::OCCASIONAL_REFRESH)));
const Connection* new_selected = ep1_ch1()->selected_connection();
// Want the new selected connection to be different.
ASSERT_NE(initial_selected, new_selected);
// Make sure we can communicate over the new connection too.
TestSendRecv(clock);
}
};
TEST_F(P2PTransportRegatherAllNetworksTest, TestControlling) {
TestWithRoles(ICEROLE_CONTROLLING, ICEROLE_CONTROLLED);
}
TEST_F(P2PTransportRegatherAllNetworksTest, TestControlled) {
TestWithRoles(ICEROLE_CONTROLLED, ICEROLE_CONTROLLING);
}
// 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, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
// Emulate no remote parameters coming in.
set_remote_ice_parameter_source(FROM_CANDIDATE);
CreateChannels();
// Only have remote parameters come in for ep2, not ep1.
ep2_ch1()->SetRemoteIceParameters(kIceParams[0]);
// Pause sending ep2's candidates to ep1 until ep1 receives the peer reflexive
// candidate.
PauseCandidates(1);
// Wait until the callee becomes writable to make sure that a ping request is
// received by the caller before his remote ICE credentials are set.
ASSERT_TRUE_WAIT(ep2_ch1()->selected_connection() != nullptr, kMediumTimeout);
// Add two sets of remote ICE credentials, so that the ones used by the
// candidate will be generation 1 instead of 0.
ep1_ch1()->SetRemoteIceParameters(kIceParams[3]);
ep1_ch1()->SetRemoteIceParameters(kIceParams[1]);
// The caller should have the selected connection connected to the peer
// reflexive candidate.
const Connection* selected_connection = nullptr;
ASSERT_TRUE_WAIT(
(selected_connection = ep1_ch1()->selected_connection()) != nullptr,
kMediumTimeout);
EXPECT_EQ("prflx", selected_connection->remote_candidate().type());
EXPECT_EQ(kIceUfrag[1], selected_connection->remote_candidate().username());
EXPECT_EQ(kIcePwd[1], selected_connection->remote_candidate().password());
EXPECT_EQ(1u, selected_connection->remote_candidate().generation());
ResumeCandidates(1);
// Verify ep1's selected connection is updated to use the 'local' candidate.
EXPECT_EQ_WAIT("local",
ep1_ch1()->selected_connection()->remote_candidate().type(),
kMediumTimeout);
EXPECT_EQ(selected_connection, ep1_ch1()->selected_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, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
// Emulate no remote parameters coming in.
set_remote_ice_parameter_source(FROM_CANDIDATE);
CreateChannels();
// Only have remote parameters come in for ep2, not ep1.
ep2_ch1()->SetRemoteIceParameters(kIceParams[0]);
// Pause sending ep2's candidates to ep1 until ep1 receives the peer reflexive
// candidate.
PauseCandidates(1);
// Wait until the callee becomes writable to make sure that a ping request is
// received by the caller before his remote ICE credentials are set.
ASSERT_TRUE_WAIT(ep2_ch1()->selected_connection() != nullptr, kMediumTimeout);
// Add two sets of remote ICE credentials, so that the ones used by the
// candidate will be generation 1 instead of 0.
ep1_ch1()->SetRemoteIceParameters(kIceParams[3]);
ep1_ch1()->SetRemoteIceParameters(kIceParams[1]);
// The caller's selected connection should be connected to the peer reflexive
// candidate.
const Connection* selected_connection = nullptr;
ASSERT_TRUE_WAIT(
(selected_connection = ep1_ch1()->selected_connection()) != nullptr,
kMediumTimeout);
EXPECT_EQ("prflx", selected_connection->remote_candidate().type());
EXPECT_EQ(kIceUfrag[1], selected_connection->remote_candidate().username());
EXPECT_EQ(kIcePwd[1], selected_connection->remote_candidate().password());
EXPECT_EQ(1u, selected_connection->remote_candidate().generation());
ResumeCandidates(1);
EXPECT_EQ_WAIT("prflx",
ep1_ch1()->selected_connection()->remote_candidate().type(),
kMediumTimeout);
EXPECT_EQ(selected_connection, ep1_ch1()->selected_connection());
DestroyChannels();
}
// Test that we properly create a connection on a STUN ping from unknown address
// when the signaling is slow, even if the new candidate is created due to the
// remote peer doing an ICE restart, pairing this candidate across generations.
//
// Previously this wasn't working due to a bug where the peer reflexive
// candidate was only updated for the newest generation candidate pairs, and
// not older-generation candidate pairs created by pairing candidates across
// generations. This resulted in the old-generation prflx candidate being
// prioritized above new-generation candidate pairs.
TEST_F(P2PTransportChannelTest,
PeerReflexiveCandidateBeforeSignalingWithIceRestart) {
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
// Only gather relay candidates, so that when the prflx candidate arrives
// it's prioritized above the current candidate pair.
GetEndpoint(0)->allocator_->set_candidate_filter(CF_RELAY);
GetEndpoint(1)->allocator_->set_candidate_filter(CF_RELAY);
// Setting this allows us to control when SetRemoteIceParameters is called.
set_remote_ice_parameter_source(FROM_CANDIDATE);
CreateChannels();
// Wait for the initial connection to be made.
ep1_ch1()->SetRemoteIceParameters(kIceParams[1]);
ep2_ch1()->SetRemoteIceParameters(kIceParams[0]);
EXPECT_TRUE_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() && ep2_ch1()->writable(),
kDefaultTimeout);
// Simulate an ICE restart on ep2, but don't signal the candidate or new
// ICE parameters until after a prflx connection has been made.
PauseCandidates(1);
ep2_ch1()->SetIceParameters(kIceParams[3]);
ep1_ch1()->SetRemoteIceParameters(kIceParams[3]);
ep2_ch1()->MaybeStartGathering();
// The caller should have the selected connection connected to the peer
// reflexive candidate.
EXPECT_EQ_WAIT("prflx",
ep1_ch1()->selected_connection()->remote_candidate().type(),
kDefaultTimeout);
const Connection* prflx_selected_connection =
ep1_ch1()->selected_connection();
// Now simulate the ICE restart on ep1.
ep1_ch1()->SetIceParameters(kIceParams[2]);
ep2_ch1()->SetRemoteIceParameters(kIceParams[2]);
ep1_ch1()->MaybeStartGathering();
// Finally send the candidates from ep2's ICE restart and verify that ep1 uses
// their information to update the peer reflexive candidate.
ResumeCandidates(1);
EXPECT_EQ_WAIT("relay",
ep1_ch1()->selected_connection()->remote_candidate().type(),
kDefaultTimeout);
EXPECT_EQ(prflx_selected_connection, ep1_ch1()->selected_connection());
DestroyChannels();
}
// Test that if remote candidates don't have ufrag and pwd, we still work.
TEST_F(P2PTransportChannelTest, RemoteCandidatesWithoutUfragPwd) {
rtc::ScopedFakeClock clock;
set_remote_ice_parameter_source(FROM_SETICEPARAMETERS);
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
CreateChannels();
const Connection* selected_connection = NULL;
// Wait until the callee's connections are created.
EXPECT_TRUE_SIMULATED_WAIT(
(selected_connection = ep2_ch1()->selected_connection()) != NULL,
kMediumTimeout, clock);
// Wait to make sure the selected connection is not changed.
SIMULATED_WAIT(ep2_ch1()->selected_connection() != selected_connection,
kShortTimeout, clock);
EXPECT_TRUE(ep2_ch1()->selected_connection() == selected_connection);
DestroyChannels();
}
// Test that a host behind NAT cannot be reached when incoming_only
// is set to true.
TEST_F(P2PTransportChannelTest, IncomingOnlyBlocked) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(NAT_FULL_CONE, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
SetAllocatorFlags(0, kOnlyLocalPorts);
CreateChannels();
ep1_ch1()->set_incoming_only(true);
// Pump for 1 second and verify that the channels are not connected.
SIMULATED_WAIT(false, kShortTimeout, clock);
EXPECT_FALSE(ep1_ch1()->receiving());
EXPECT_FALSE(ep1_ch1()->writable());
EXPECT_FALSE(ep2_ch1()->receiving());
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) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(OPEN, NAT_FULL_CONE, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
SetAllocatorFlags(0, kOnlyLocalPorts);
CreateChannels();
ep1_ch1()->set_incoming_only(true);
EXPECT_TRUE_SIMULATED_WAIT(
ep1_ch1() != NULL && ep2_ch1() != NULL && ep1_ch1()->receiving() &&
ep1_ch1()->writable() && ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kMediumTimeout, clock);
DestroyChannels();
}
// Test that two peers can connect when one can only make outgoing TCP
// connections. This has been observed in some scenarios involving
// VPNs/firewalls.
TEST_F(P2PTransportChannelTest, CanOnlyMakeOutgoingTcpConnections) {
// The PORTALLOCATOR_ENABLE_ANY_ADDRESS_PORTS flag is required if the
// application needs this use case to work, since the application must accept
// the tradeoff that more candidates need to be allocated.
//
// TODO(deadbeef): Later, make this flag the default, and do more elegant
// things to ensure extra candidates don't waste resources?
ConfigureEndpoints(
OPEN, OPEN,
kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_ANY_ADDRESS_PORTS,
kDefaultPortAllocatorFlags);
// In order to simulate nothing working but outgoing TCP connections, prevent
// the endpoint from binding to its interface's address as well as the
// "any" addresses. It can then only make a connection by using "Connect()".
fw()->SetUnbindableIps({rtc::GetAnyIP(AF_INET), rtc::GetAnyIP(AF_INET6),
kPublicAddrs[0].ipaddr()});
CreateChannels();
// Expect a "prflx" candidate on the side that can only make outgoing
// connections, endpoint 0.
Test(kPrflxTcpToLocalTcp);
DestroyChannels();
}
TEST_F(P2PTransportChannelTest, TestTcpConnectionsFromActiveToPassive) {
rtc::ScopedFakeClock clock;
AddAddress(0, kPublicAddrs[0]);
AddAddress(1, kPublicAddrs[1]);
SetAllocationStepDelay(0, kMinimumStepDelay);
SetAllocationStepDelay(1, kMinimumStepDelay);
int kOnlyLocalTcpPorts = PORTALLOCATOR_DISABLE_UDP |
PORTALLOCATOR_DISABLE_STUN |
PORTALLOCATOR_DISABLE_RELAY;
// Disable all protocols except TCP.
SetAllocatorFlags(0, kOnlyLocalTcpPorts);
SetAllocatorFlags(1, kOnlyLocalTcpPorts);
SetAllowTcpListen(0, true); // actpass.
SetAllowTcpListen(1, false); // active.
// We want SetRemoteIceParameters to be called as it normally would.
// Otherwise we won't know what parameters to use for the expected
// prflx TCP candidates.
set_remote_ice_parameter_source(FROM_SETICEPARAMETERS);
// Pause candidate so we could verify the candidate properties.
PauseCandidates(0);
PauseCandidates(1);
CreateChannels();
// Verify tcp candidates.
VerifySavedTcpCandidates(0, TCPTYPE_PASSIVE_STR);
VerifySavedTcpCandidates(1, TCPTYPE_ACTIVE_STR);
// Resume candidates.
ResumeCandidates(0);
ResumeCandidates(1);
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kShortTimeout, clock);
EXPECT_TRUE(ep1_ch1()->selected_connection() &&
ep2_ch1()->selected_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
TestSendRecv(clock);
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) {
rtc::ScopedFakeClock clock;
AddAddress(0, kPublicAddrs[0]);
AddAddress(1, kPublicAddrs[1]);
// Give the first connection the higher tiebreaker so its role won't
// change unless we tell it to.
SetIceRole(0, ICEROLE_CONTROLLING);
SetIceTiebreaker(0, kHighTiebreaker);
SetIceRole(1, ICEROLE_CONTROLLING);
SetIceTiebreaker(1, kLowTiebreaker);
CreateChannels();
EXPECT_EQ_SIMULATED_WAIT(2u, ep1_ch1()->ports().size(), kShortTimeout, clock);
const std::vector<PortInterface*> ports_before = ep1_ch1()->ports();
for (size_t i = 0; i < ports_before.size(); ++i) {
EXPECT_EQ(ICEROLE_CONTROLLING, ports_before[i]->GetIceRole());
EXPECT_EQ(kHighTiebreaker, ports_before[i]->IceTiebreaker());
}
ep1_ch1()->SetIceRole(ICEROLE_CONTROLLED);
ep1_ch1()->SetIceTiebreaker(kLowTiebreaker);
const std::vector<PortInterface*> ports_after = ep1_ch1()->ports();
for (size_t i = 0; i < ports_after.size(); ++i) {
EXPECT_EQ(ICEROLE_CONTROLLED, ports_before[i]->GetIceRole());
// SetIceTiebreaker after ports have been created will fail. So expect the
// original value.
EXPECT_EQ(kHighTiebreaker, ports_before[i]->IceTiebreaker());
}
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kShortTimeout, clock);
EXPECT_TRUE(ep1_ch1()->selected_connection() &&
ep2_ch1()->selected_connection());
TestSendRecv(clock);
DestroyChannels();
}
// 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();
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) {
rtc::ScopedFakeClock clock;
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, PORTALLOCATOR_ENABLE_IPV6 | PORTALLOCATOR_ENABLE_IPV6_ON_WIFI);
SetAllocatorFlags(
1, PORTALLOCATOR_ENABLE_IPV6 | PORTALLOCATOR_ENABLE_IPV6_ON_WIFI);
CreateChannels();
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kShortTimeout, clock);
EXPECT_TRUE(
ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(kIPv6PublicAddrs[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(kIPv6PublicAddrs[1]));
TestSendRecv(clock);
DestroyChannels();
}
// Testing forceful TURN connections.
TEST_F(P2PTransportChannelTest, TestForceTurn) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(
NAT_PORT_RESTRICTED, NAT_SYMMETRIC,
kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET,
kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET);
set_force_relay(true);
SetAllocationStepDelay(0, kMinimumStepDelay);
SetAllocationStepDelay(1, kMinimumStepDelay);
CreateChannels();
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kMediumTimeout, clock);
EXPECT_TRUE(ep1_ch1()->selected_connection() &&
ep2_ch1()->selected_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(clock);
DestroyChannels();
}
// Test that if continual gathering is set to true, ICE gathering state will
// not change to "Complete", and vice versa.
TEST_F(P2PTransportChannelTest, TestContinualGathering) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
SetAllocationStepDelay(0, kDefaultStepDelay);
SetAllocationStepDelay(1, kDefaultStepDelay);
IceConfig continual_gathering_config =
CreateIceConfig(1000, GATHER_CONTINUALLY);
// By default, ep2 does not gather continually.
IceConfig default_config;
CreateChannels(continual_gathering_config, default_config);
EXPECT_TRUE_SIMULATED_WAIT(
ep1_ch1() != NULL && ep2_ch1() != NULL && ep1_ch1()->receiving() &&
ep1_ch1()->writable() && ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kMediumTimeout, clock);
SIMULATED_WAIT(
IceGatheringState::kIceGatheringComplete == ep1_ch1()->gathering_state(),
kShortTimeout, clock);
EXPECT_EQ(IceGatheringState::kIceGatheringGathering,
ep1_ch1()->gathering_state());
// By now, ep2 should have completed gathering.
EXPECT_EQ(IceGatheringState::kIceGatheringComplete,
ep2_ch1()->gathering_state());
DestroyChannels();
}
// Test that a connection succeeds when the P2PTransportChannel uses a pooled
// PortAllocatorSession that has not yet finished gathering candidates.
TEST_F(P2PTransportChannelTest, TestUsingPooledSessionBeforeDoneGathering) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
// First create a pooled session for each endpoint.
auto& allocator_1 = GetEndpoint(0)->allocator_;
auto& allocator_2 = GetEndpoint(1)->allocator_;
int pool_size = 1;
allocator_1->SetConfiguration(allocator_1->stun_servers(),
allocator_1->turn_servers(), pool_size, false);
allocator_2->SetConfiguration(allocator_2->stun_servers(),
allocator_2->turn_servers(), pool_size, false);
const PortAllocatorSession* pooled_session_1 =
allocator_1->GetPooledSession();
const PortAllocatorSession* pooled_session_2 =
allocator_2->GetPooledSession();
ASSERT_NE(nullptr, pooled_session_1);
ASSERT_NE(nullptr, pooled_session_2);
// Sanity check that pooled sessions haven't gathered anything yet.
EXPECT_TRUE(pooled_session_1->ReadyPorts().empty());
EXPECT_TRUE(pooled_session_1->ReadyCandidates().empty());
EXPECT_TRUE(pooled_session_2->ReadyPorts().empty());
EXPECT_TRUE(pooled_session_2->ReadyCandidates().empty());
// Now let the endpoints connect and try exchanging some data.
CreateChannels();
EXPECT_TRUE_SIMULATED_WAIT(
ep1_ch1() != NULL && ep2_ch1() != NULL && ep1_ch1()->receiving() &&
ep1_ch1()->writable() && ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kMediumTimeout, clock);
TestSendRecv(clock);
// Make sure the P2PTransportChannels are actually using ports from the
// pooled sessions.
auto pooled_ports_1 = pooled_session_1->ReadyPorts();
auto pooled_ports_2 = pooled_session_2->ReadyPorts();
EXPECT_NE(pooled_ports_1.end(),
std::find(pooled_ports_1.begin(), pooled_ports_1.end(),
ep1_ch1()->selected_connection()->port()));
EXPECT_NE(pooled_ports_2.end(),
std::find(pooled_ports_2.begin(), pooled_ports_2.end(),
ep2_ch1()->selected_connection()->port()));
}
// Test that a connection succeeds when the P2PTransportChannel uses a pooled
// PortAllocatorSession that already finished gathering candidates.
TEST_F(P2PTransportChannelTest, TestUsingPooledSessionAfterDoneGathering) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
// First create a pooled session for each endpoint.
auto& allocator_1 = GetEndpoint(0)->allocator_;
auto& allocator_2 = GetEndpoint(1)->allocator_;
int pool_size = 1;
allocator_1->SetConfiguration(allocator_1->stun_servers(),
allocator_1->turn_servers(), pool_size, false);
allocator_2->SetConfiguration(allocator_2->stun_servers(),
allocator_2->turn_servers(), pool_size, false);
const PortAllocatorSession* pooled_session_1 =
allocator_1->GetPooledSession();
const PortAllocatorSession* pooled_session_2 =
allocator_2->GetPooledSession();
ASSERT_NE(nullptr, pooled_session_1);
ASSERT_NE(nullptr, pooled_session_2);
// Wait for the pooled sessions to finish gathering before the
// P2PTransportChannels try to use them.
EXPECT_TRUE_SIMULATED_WAIT(pooled_session_1->CandidatesAllocationDone() &&
pooled_session_2->CandidatesAllocationDone(),
kDefaultTimeout, clock);
// Now let the endpoints connect and try exchanging some data.
CreateChannels();
EXPECT_TRUE_SIMULATED_WAIT(
ep1_ch1() != NULL && ep2_ch1() != NULL && ep1_ch1()->receiving() &&
ep1_ch1()->writable() && ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kMediumTimeout, clock);
TestSendRecv(clock);
// Make sure the P2PTransportChannels are actually using ports from the
// pooled sessions.
auto pooled_ports_1 = pooled_session_1->ReadyPorts();
auto pooled_ports_2 = pooled_session_2->ReadyPorts();
EXPECT_NE(pooled_ports_1.end(),
std::find(pooled_ports_1.begin(), pooled_ports_1.end(),
ep1_ch1()->selected_connection()->port()));
EXPECT_NE(pooled_ports_2.end(),
std::find(pooled_ports_2.begin(), pooled_ports_2.end(),
ep2_ch1()->selected_connection()->port()));
}
// Test that when the "presume_writable_when_fully_relayed" flag is set to
// true and there's a TURN-TURN candidate pair, it's presumed to be writable
// as soon as it's created.
// TODO(deadbeef): Move this and other "presumed writable" tests into a test
// class that operates on a single P2PTransportChannel, once an appropriate one
// (which supports TURN servers and TURN candidate gathering) is available.
TEST_F(P2PTransportChannelTest, TurnToTurnPresumedWritable) {
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
// Only configure one channel so we can control when the remote candidate
// is added.
GetEndpoint(0)->cd1_.ch_.reset(CreateChannel(
0, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[0], kIceParams[1]));
IceConfig config;
config.presume_writable_when_fully_relayed = true;
ep1_ch1()->SetIceConfig(config);
ep1_ch1()->MaybeStartGathering();
EXPECT_EQ_WAIT(IceGatheringState::kIceGatheringComplete,
ep1_ch1()->gathering_state(), kDefaultTimeout);
// Add two remote candidates; a host candidate (with higher priority)
// and TURN candidate.
ep1_ch1()->AddRemoteCandidate(
CreateUdpCandidate(LOCAL_PORT_TYPE, "1.1.1.1", 1, 100));
ep1_ch1()->AddRemoteCandidate(
CreateUdpCandidate(RELAY_PORT_TYPE, "2.2.2.2", 2, 0));
// Expect that the TURN-TURN candidate pair will be prioritized since it's
// "probably writable".
EXPECT_TRUE(ep1_ch1()->selected_connection() != nullptr);
EXPECT_EQ(RELAY_PORT_TYPE, LocalCandidate(ep1_ch1())->type());
EXPECT_EQ(RELAY_PORT_TYPE, RemoteCandidate(ep1_ch1())->type());
// Also expect that the channel instantly indicates that it's writable since
// it has a TURN-TURN pair.
EXPECT_TRUE(ep1_ch1()->writable());
EXPECT_TRUE(GetEndpoint(0)->ready_to_send_);
// Also make sure we can immediately send packets.
const char* data = "test";
int len = static_cast<int>(strlen(data));
EXPECT_EQ(len, SendData(ep1_ch1(), data, len));
}
// Test that a TURN/peer reflexive candidate pair is also presumed writable.
TEST_F(P2PTransportChannelTest, TurnToPrflxPresumedWritable) {
rtc::ScopedFakeClock fake_clock;
// We need to add artificial network delay to verify that the connection
// is presumed writable before it's actually writable. Without this delay
// it would become writable instantly.
virtual_socket_server()->set_delay_mean(50);
virtual_socket_server()->UpdateDelayDistribution();
ConfigureEndpoints(NAT_SYMMETRIC, NAT_SYMMETRIC, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
// We want the remote TURN candidate to show up as prflx. To do this we need
// to configure the server to accept packets from an address we haven't
// explicitly installed permission for.
test_turn_server()->set_enable_permission_checks(false);
IceConfig config;
config.presume_writable_when_fully_relayed = true;
GetEndpoint(0)->cd1_.ch_.reset(CreateChannel(
0, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[0], kIceParams[1]));
GetEndpoint(1)->cd1_.ch_.reset(CreateChannel(
1, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[1], kIceParams[0]));
ep1_ch1()->SetIceConfig(config);
ep2_ch1()->SetIceConfig(config);
// Don't signal candidates from channel 2, so that channel 1 sees the TURN
// candidate as peer reflexive.
PauseCandidates(1);
ep1_ch1()->MaybeStartGathering();
ep2_ch1()->MaybeStartGathering();
// Wait for the TURN<->prflx connection.
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable(),
kShortTimeout, fake_clock);
ASSERT_NE(nullptr, ep1_ch1()->selected_connection());
EXPECT_EQ(RELAY_PORT_TYPE, LocalCandidate(ep1_ch1())->type());
EXPECT_EQ(PRFLX_PORT_TYPE, RemoteCandidate(ep1_ch1())->type());
// Make sure that at this point the connection is only presumed writable,
// not fully writable.
EXPECT_FALSE(ep1_ch1()->selected_connection()->writable());
// Now wait for it to actually become writable.
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection()->writable(),
kShortTimeout, fake_clock);
// Explitly destroy channels, before fake clock is destroyed.
DestroyChannels();
}
// Test that a presumed-writable TURN<->TURN connection is preferred above an
// unreliable connection (one that has failed to be pinged for some time).
TEST_F(P2PTransportChannelTest, PresumedWritablePreferredOverUnreliable) {
rtc::ScopedFakeClock fake_clock;
ConfigureEndpoints(NAT_SYMMETRIC, NAT_SYMMETRIC, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
IceConfig config;
config.presume_writable_when_fully_relayed = true;
GetEndpoint(0)->cd1_.ch_.reset(CreateChannel(
0, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[0], kIceParams[1]));
GetEndpoint(1)->cd1_.ch_.reset(CreateChannel(
1, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[1], kIceParams[0]));
ep1_ch1()->SetIceConfig(config);
ep2_ch1()->SetIceConfig(config);
ep1_ch1()->MaybeStartGathering();
ep2_ch1()->MaybeStartGathering();
// Wait for initial connection as usual.
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep1_ch1()->selected_connection()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable() &&
ep2_ch1()->selected_connection()->writable(),
kShortTimeout, fake_clock);
const Connection* old_selected_connection = ep1_ch1()->selected_connection();
// Destroy the second channel and wait for the current connection on the
// first channel to become "unreliable", making it no longer writable.
GetEndpoint(1)->cd1_.ch_.reset();
EXPECT_TRUE_SIMULATED_WAIT(!ep1_ch1()->writable(), kDefaultTimeout,
fake_clock);
EXPECT_NE(nullptr, ep1_ch1()->selected_connection());
// Add a remote TURN candidate. The first channel should still have a TURN
// port available to make a TURN<->TURN pair that's presumed writable.
ep1_ch1()->AddRemoteCandidate(
CreateUdpCandidate(RELAY_PORT_TYPE, "2.2.2.2", 2, 0));
EXPECT_EQ(RELAY_PORT_TYPE, LocalCandidate(ep1_ch1())->type());
EXPECT_EQ(RELAY_PORT_TYPE, RemoteCandidate(ep1_ch1())->type());
EXPECT_TRUE(ep1_ch1()->writable());
EXPECT_TRUE(GetEndpoint(0)->ready_to_send_);
EXPECT_NE(old_selected_connection, ep1_ch1()->selected_connection());
// Explitly destroy channels, before fake clock is destroyed.
DestroyChannels();
}
// Ensure that "SignalReadyToSend" is fired as expected with a "presumed
// writable" connection. Previously this did not work.
TEST_F(P2PTransportChannelTest, SignalReadyToSendWithPresumedWritable) {
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
// Only test one endpoint, so we can ensure the connection doesn't receive a
// binding response and advance beyond being "presumed" writable.
GetEndpoint(0)->cd1_.ch_.reset(CreateChannel(
0, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[0], kIceParams[1]));
IceConfig config;
config.presume_writable_when_fully_relayed = true;
ep1_ch1()->SetIceConfig(config);
ep1_ch1()->MaybeStartGathering();
EXPECT_EQ_WAIT(IceGatheringState::kIceGatheringComplete,
ep1_ch1()->gathering_state(), kDefaultTimeout);
ep1_ch1()->AddRemoteCandidate(
CreateUdpCandidate(RELAY_PORT_TYPE, "1.1.1.1", 1, 0));
// Sanity checking the type of the connection.
EXPECT_TRUE(ep1_ch1()->selected_connection() != nullptr);
EXPECT_EQ(RELAY_PORT_TYPE, LocalCandidate(ep1_ch1())->type());
EXPECT_EQ(RELAY_PORT_TYPE, RemoteCandidate(ep1_ch1())->type());
// Tell the socket server to block packets (returning EWOULDBLOCK).
virtual_socket_server()->SetSendingBlocked(true);
const char* data = "test";
int len = static_cast<int>(strlen(data));
EXPECT_EQ(-1, SendData(ep1_ch1(), data, len));
// Reset |ready_to_send_| flag, which is set to true if the event fires as it
// should.
GetEndpoint(0)->ready_to_send_ = false;
virtual_socket_server()->SetSendingBlocked(false);
EXPECT_TRUE(GetEndpoint(0)->ready_to_send_);
EXPECT_EQ(len, SendData(ep1_ch1(), data, len));
}
// 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) {
RTC_CHECK_GE(nat_type, NAT_FULL_CONE);
RTC_CHECK_LE(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);
set_remote_ice_parameter_source(FROM_SETICEPARAMETERS);
}
void ConfigureEndpoint(rtc::NATSocketServer::Translator* nat,
int endpoint, Config config) {
RTC_CHECK(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(
P2PTransportChannelTestBase::Result("prflx", "udp", "stun", "udp", 1000));
}
// 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 {
public:
const Connection* GetConnectionWithRemoteAddress(
P2PTransportChannel* channel,
const SocketAddress& address) {
for (Connection* conn : channel->connections()) {
if (conn->remote_candidate().address().EqualIPs(address)) {
return conn;
}
}
return nullptr;
}
Connection* GetConnectionWithLocalAddress(P2PTransportChannel* channel,
const SocketAddress& address) {
for (Connection* conn : channel->connections()) {
if (conn->local_candidate().address().EqualIPs(address)) {
return conn;
}
}
return nullptr;
}
Connection* GetConnection(P2PTransportChannel* channel,
const SocketAddress& local,
const SocketAddress& remote) {
for (Connection* conn : channel->connections()) {
if (conn->local_candidate().address().EqualIPs(local) &&
conn->remote_candidate().address().EqualIPs(remote)) {
return conn;
}
}
return nullptr;
}
void DestroyAllButBestConnection(P2PTransportChannel* channel) {
const Connection* selected_connection = channel->selected_connection();
for (Connection* conn : channel->connections()) {
if (conn != selected_connection) {
conn->Destroy();
}
}
}
};
// Test that we can establish connectivity when both peers are multihomed.
TEST_F(P2PTransportChannelMultihomedTest, 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.
// The controlled side has two interfaces and one will die.
TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControlledSide) {
rtc::ScopedFakeClock clock;
AddAddress(0, kPublicAddrs[0]);
// Simulate failing over from Wi-Fi to cell interface.
AddAddress(1, kPublicAddrs[1], "eth0", rtc::ADAPTER_TYPE_WIFI);
AddAddress(1, kAlternateAddrs[1], "wlan0", rtc::ADAPTER_TYPE_CELLULAR);
// Use only local ports for simplicity.
SetAllocatorFlags(0, kOnlyLocalPorts);
SetAllocatorFlags(1, kOnlyLocalPorts);
// Make the receiving timeout shorter for testing.
IceConfig config = CreateIceConfig(1000, GATHER_ONCE);
// Create channels and let them go writable, as usual.
CreateChannels(config, config);
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kMediumTimeout, clock);
EXPECT_TRUE(ep1_ch1()->selected_connection() &&
ep2_ch1()->selected_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]);
// The selected connections may switch, so keep references to them.
const Connection* selected_connection1 = ep1_ch1()->selected_connection();
const Connection* selected_connection2 = ep2_ch1()->selected_connection();
// We should detect loss of receiving within 1 second or so.
EXPECT_TRUE_SIMULATED_WAIT(
!selected_connection1->receiving() && !selected_connection2->receiving(),
kMediumTimeout, clock);
// We should switch over to use the alternate addr on both sides
// when we are not receiving.
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection()->receiving() &&
ep2_ch1()->selected_connection()->receiving(),
kMediumTimeout, clock);
EXPECT_TRUE(LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]));
EXPECT_TRUE(
RemoteCandidate(ep1_ch1())->address().EqualIPs(kAlternateAddrs[1]));
EXPECT_TRUE(
LocalCandidate(ep2_ch1())->address().EqualIPs(kAlternateAddrs[1]));
DestroyChannels();
}
// Test that we can quickly switch links if an interface goes down.
// The controlling side has two interfaces and one will die.
TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControllingSide) {
rtc::ScopedFakeClock clock;
// Simulate failing over from Wi-Fi to cell interface.
AddAddress(0, kPublicAddrs[0], "eth0", rtc::ADAPTER_TYPE_WIFI);
AddAddress(0, kAlternateAddrs[0], "wlan0", rtc::ADAPTER_TYPE_CELLULAR);
AddAddress(1, kPublicAddrs[1]);
// Use only local ports for simplicity.
SetAllocatorFlags(0, kOnlyLocalPorts);
SetAllocatorFlags(1, kOnlyLocalPorts);
// Make the receiving timeout shorter for testing.
IceConfig config = CreateIceConfig(1000, GATHER_ONCE);
// Create channels and let them go writable, as usual.
CreateChannels(config, config);
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kMediumTimeout, clock);
EXPECT_TRUE(ep1_ch1()->selected_connection() &&
ep2_ch1()->selected_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[0]);
// The selected connections will switch, so keep references to them.
const Connection* selected_connection1 = ep1_ch1()->selected_connection();
const Connection* selected_connection2 = ep2_ch1()->selected_connection();
// We should detect loss of receiving within 1 second or so.
EXPECT_TRUE_SIMULATED_WAIT(
!selected_connection1->receiving() && !selected_connection2->receiving(),
kMediumTimeout, clock);
// We should switch over to use the alternate addr on both sides
// when we are not receiving.
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection()->receiving() &&
ep2_ch1()->selected_connection()->receiving(),
kMediumTimeout, clock);
EXPECT_TRUE(
LocalCandidate(ep1_ch1())->address().EqualIPs(kAlternateAddrs[0]));
EXPECT_TRUE(RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
EXPECT_TRUE(
RemoteCandidate(ep2_ch1())->address().EqualIPs(kAlternateAddrs[0]));
DestroyChannels();
}
// Tests that we can quickly switch links if an interface goes down when
// there are many connections.
TEST_F(P2PTransportChannelMultihomedTest, TestFailoverWithManyConnections) {
rtc::ScopedFakeClock clock;
test_turn_server()->AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP);
RelayServerConfig turn_server(RELAY_TURN);
turn_server.credentials = kRelayCredentials;
turn_server.ports.push_back(ProtocolAddress(kTurnTcpIntAddr, PROTO_TCP));
GetAllocator(0)->AddTurnServer(turn_server);
GetAllocator(1)->AddTurnServer(turn_server);
// Enable IPv6
SetAllocatorFlags(
0, PORTALLOCATOR_ENABLE_IPV6 | PORTALLOCATOR_ENABLE_IPV6_ON_WIFI);
SetAllocatorFlags(
1, PORTALLOCATOR_ENABLE_IPV6 | PORTALLOCATOR_ENABLE_IPV6_ON_WIFI);
SetAllocationStepDelay(0, kMinimumStepDelay);
SetAllocationStepDelay(1, kMinimumStepDelay);
auto& wifi = kPublicAddrs;
auto& cellular = kAlternateAddrs;
auto& wifiIpv6 = kIPv6PublicAddrs;
auto& cellularIpv6 = kIPv6AlternateAddrs;
AddAddress(0, wifi[0], "wifi0", rtc::ADAPTER_TYPE_WIFI);
AddAddress(0, wifiIpv6[0], "wifi0", rtc::ADAPTER_TYPE_WIFI);
AddAddress(0, cellular[0], "cellular0", rtc::ADAPTER_TYPE_CELLULAR);
AddAddress(0, cellularIpv6[0], "cellular0", rtc::ADAPTER_TYPE_CELLULAR);
AddAddress(1, wifi[1], "wifi1", rtc::ADAPTER_TYPE_WIFI);
AddAddress(1, wifiIpv6[1], "wifi1", rtc::ADAPTER_TYPE_WIFI);
AddAddress(1, cellular[1], "cellular1", rtc::ADAPTER_TYPE_CELLULAR);
AddAddress(1, cellularIpv6[1], "cellular1", rtc::ADAPTER_TYPE_CELLULAR);
// Set smaller delay on the TCP TURN server so that TCP TURN candidates
// will be created in time.
virtual_socket_server()->SetDelayOnAddress(kTurnTcpIntAddr, 1);
virtual_socket_server()->SetDelayOnAddress(kTurnUdpExtAddr, 1);
virtual_socket_server()->set_delay_mean(500);
virtual_socket_server()->UpdateDelayDistribution();
// Make the receiving timeout shorter for testing.
IceConfig config = CreateIceConfig(1000, GATHER_CONTINUALLY);
// Create channels and let them go writable, as usual.
CreateChannels(config, config, true /* ice_renomination */);
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kMediumTimeout, clock);
EXPECT_TRUE_SIMULATED_WAIT(
ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(wifiIpv6[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(wifiIpv6[1]),
kMediumTimeout, clock);
// Blackhole any traffic to or from the wifi on endpoint 1.
LOG(LS_INFO) << "Failing over...";
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, wifi[0]);
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, wifiIpv6[0]);
// The selected connections may switch, so keep references to them.
const Connection* selected_connection1 = ep1_ch1()->selected_connection();
const Connection* selected_connection2 = ep2_ch1()->selected_connection();
EXPECT_TRUE_SIMULATED_WAIT(
!selected_connection1->receiving() && !selected_connection2->receiving(),
kMediumTimeout, clock);
// Per-network best connections will be pinged at relatively higher rate when
// the selected connection becomes not receiving.
Connection* per_network_best_connection1 =
GetConnection(ep1_ch1(), cellularIpv6[0], wifiIpv6[1]);
ASSERT_NE(nullptr, per_network_best_connection1);
int64_t last_ping_sent1 = per_network_best_connection1->last_ping_sent();
int num_pings_sent1 = per_network_best_connection1->num_pings_sent();
EXPECT_TRUE_SIMULATED_WAIT(
num_pings_sent1 < per_network_best_connection1->num_pings_sent(),
kMediumTimeout, clock);
int64_t ping_interval1 =
(per_network_best_connection1->last_ping_sent() - last_ping_sent1) /
(per_network_best_connection1->num_pings_sent() - num_pings_sent1);
constexpr int SCHEDULING_DELAY = 200;
EXPECT_LT(
ping_interval1,
WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL + SCHEDULING_DELAY);
// It should switch over to use the cellular IPv6 addr on endpoint 1 before
// it timed out on writing.
EXPECT_TRUE_SIMULATED_WAIT(
ep1_ch1()->selected_connection()->receiving() &&
ep2_ch1()->selected_connection()->receiving() &&
RemoteCandidate(ep2_ch1())->address().EqualIPs(cellularIpv6[0]) &&
LocalCandidate(ep1_ch1())->address().EqualIPs(cellularIpv6[0]),
kMediumTimeout, clock);
DestroyChannels();
}
// Test that when the controlling side switches the selected connection,
// the nomination of the selected connection on the controlled side will
// increase.
TEST_F(P2PTransportChannelMultihomedTest, TestIceRenomination) {
rtc::ScopedFakeClock clock;
// Simulate failing over from Wi-Fi to cell interface.
AddAddress(0, kPublicAddrs[0], "eth0", rtc::ADAPTER_TYPE_WIFI);
AddAddress(0, kAlternateAddrs[0], "wlan0", rtc::ADAPTER_TYPE_CELLULAR);
AddAddress(1, kPublicAddrs[1]);
// Use only local ports for simplicity.
SetAllocatorFlags(0, kOnlyLocalPorts);
SetAllocatorFlags(1, kOnlyLocalPorts);
// We want it to set the remote ICE parameters when creating channels.
set_remote_ice_parameter_source(FROM_SETICEPARAMETERS);
// Make the receiving timeout shorter for testing.
IceConfig config = CreateIceConfig(1000, GATHER_ONCE);
// Create channels with ICE renomination and let them go writable as usual.
CreateChannels(config, config, true);
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kMediumTimeout, clock);
EXPECT_TRUE_SIMULATED_WAIT(
ep2_ch1()->selected_connection()->remote_nomination() > 0 &&
ep1_ch1()->selected_connection()->acked_nomination() > 0,
kDefaultTimeout, clock);
const Connection* selected_connection1 = ep1_ch1()->selected_connection();
Connection* selected_connection2 =
const_cast<Connection*>(ep2_ch1()->selected_connection());
uint32_t remote_nomination2 = selected_connection2->remote_nomination();
// |selected_connection2| should not be nominated any more since the previous
// nomination has been acknowledged.
ConnectSignalNominated(selected_connection2);
SIMULATED_WAIT(nominated(), kMediumTimeout, clock);
EXPECT_FALSE(nominated());
// Blackhole any traffic to or from the public addrs.
LOG(LS_INFO) << "Failing over...";
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kPublicAddrs[0]);
// The selected connection on the controlling side should switch.
EXPECT_TRUE_SIMULATED_WAIT(
ep1_ch1()->selected_connection() != selected_connection1, kMediumTimeout,
clock);
// The connection on the controlled side should be nominated again
// and have an increased nomination.
EXPECT_TRUE_SIMULATED_WAIT(
ep2_ch1()->selected_connection()->remote_nomination() >
remote_nomination2,
kDefaultTimeout, clock);
DestroyChannels();
}
// Test that if an interface fails temporarily and then recovers quickly,
// the selected connection will not switch.
// The case that it will switch over to the backup connection if the selected
// connection does not recover after enough time is covered in
// TestFailoverControlledSide and TestFailoverControllingSide.
TEST_F(P2PTransportChannelMultihomedTest,
TestConnectionSwitchDampeningControlledSide) {
rtc::ScopedFakeClock clock;
AddAddress(0, kPublicAddrs[0]);
// Simulate failing over from Wi-Fi to cell interface.
AddAddress(1, kPublicAddrs[1], "eth0", rtc::ADAPTER_TYPE_WIFI);
AddAddress(1, kAlternateAddrs[1], "wlan0", rtc::ADAPTER_TYPE_CELLULAR);
// Use only local ports for simplicity.
SetAllocatorFlags(0, kOnlyLocalPorts);
SetAllocatorFlags(1, kOnlyLocalPorts);
// Create channels and let them go writable, as usual.
CreateChannels();
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kMediumTimeout, clock);
EXPECT_TRUE(ep1_ch1()->selected_connection() &&
ep2_ch1()->selected_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
// Make the receiving timeout shorter for testing.
IceConfig config = CreateIceConfig(1000, GATHER_ONCE);
ep1_ch1()->SetIceConfig(config);
ep2_ch1()->SetIceConfig(config);
reset_selected_candidate_pair_switches();
// 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]);
// The selected connections may switch, so keep references to them.
const Connection* selected_connection1 = ep1_ch1()->selected_connection();
const Connection* selected_connection2 = ep2_ch1()->selected_connection();
// We should detect loss of receiving within 1 second or so.
EXPECT_TRUE_SIMULATED_WAIT(
!selected_connection1->receiving() && !selected_connection2->receiving(),
kMediumTimeout, clock);
// After a short while, the link recovers itself.
SIMULATED_WAIT(false, 10, clock);
fw()->ClearRules();
// We should remain on the public address on both sides and no connection
// switches should have happened.
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection()->receiving() &&
ep2_ch1()->selected_connection()->receiving(),
kMediumTimeout, clock);
EXPECT_TRUE(RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
EXPECT_TRUE(LocalCandidate(ep2_ch1())->address().EqualIPs(kPublicAddrs[1]));
EXPECT_EQ(0, reset_selected_candidate_pair_switches());
DestroyChannels();
}
// Test that if an interface fails temporarily and then recovers quickly,
// the selected connection will not switch.
TEST_F(P2PTransportChannelMultihomedTest,
TestConnectionSwitchDampeningControllingSide) {
rtc::ScopedFakeClock clock;
// Simulate failing over from Wi-Fi to cell interface.
AddAddress(0, kPublicAddrs[0], "eth0", rtc::ADAPTER_TYPE_WIFI);
AddAddress(0, kAlternateAddrs[0], "wlan0", rtc::ADAPTER_TYPE_CELLULAR);
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();
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kMediumTimeout, clock);
EXPECT_TRUE(ep1_ch1()->selected_connection() &&
ep2_ch1()->selected_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
// Make the receiving timeout shorter for testing.
IceConfig config = CreateIceConfig(1000, GATHER_ONCE);
ep1_ch1()->SetIceConfig(config);
ep2_ch1()->SetIceConfig(config);
reset_selected_candidate_pair_switches();
// Blackhole any traffic to or from the public addrs.
LOG(LS_INFO) << "Failing over...";
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kPublicAddrs[0]);
// The selected connections may switch, so keep references to them.
const Connection* selected_connection1 = ep1_ch1()->selected_connection();
const Connection* selected_connection2 = ep2_ch1()->selected_connection();
// We should detect loss of receiving within 1 second or so.
EXPECT_TRUE_SIMULATED_WAIT(
!selected_connection1->receiving() && !selected_connection2->receiving(),
kMediumTimeout, clock);
// The link recovers after a short while.
SIMULATED_WAIT(false, 10, clock);
fw()->ClearRules();
// We should not switch to the alternate addr on both sides because of the
// dampening.
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection()->receiving() &&
ep2_ch1()->selected_connection()->receiving(),
kMediumTimeout, clock);
EXPECT_TRUE(LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]));
EXPECT_TRUE(RemoteCandidate(ep2_ch1())->address().EqualIPs(kPublicAddrs[0]));
EXPECT_EQ(0, reset_selected_candidate_pair_switches());
DestroyChannels();
}
// Tests that if the remote side's network failed, it won't cause the local
// side to switch connections and networks.
TEST_F(P2PTransportChannelMultihomedTest, TestRemoteFailover) {
rtc::ScopedFakeClock clock;
// The interface names are chosen so that |cellular| would have higher
// candidate priority and higher cost.
auto& wifi = kPublicAddrs;
auto& cellular = kAlternateAddrs;
AddAddress(0, wifi[0], "wifi0", rtc::ADAPTER_TYPE_WIFI);
AddAddress(0, cellular[0], "cellular0", rtc::ADAPTER_TYPE_CELLULAR);
AddAddress(1, wifi[1], "wifi0", rtc::ADAPTER_TYPE_WIFI);
// Use only local ports for simplicity.
SetAllocatorFlags(0, kOnlyLocalPorts);
SetAllocatorFlags(1, kOnlyLocalPorts);
// Create channels and let them go writable, as usual.
CreateChannels();
// Make the receiving timeout shorter for testing.
// Set the backup connection ping interval to 25s.
IceConfig config = CreateIceConfig(1000, GATHER_ONCE, 25000);
// Ping the best connection more frequently since we don't have traffic.
config.stable_writable_connection_ping_interval = 900;
ep1_ch1()->SetIceConfig(config);
ep2_ch1()->SetIceConfig(config);
// Need to wait to make sure the connections on both networks are writable.
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
kMediumTimeout, clock);
EXPECT_TRUE_SIMULATED_WAIT(
ep1_ch1()->selected_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(wifi[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(wifi[1]),
kDefaultTimeout, clock);
Connection* backup_conn =
GetConnectionWithLocalAddress(ep1_ch1(), cellular[0]);
ASSERT_NE(nullptr, backup_conn);
// After a short while, the backup connection will be writable but not
// receiving because backup connection is pinged at a slower rate.
EXPECT_TRUE_SIMULATED_WAIT(
backup_conn->writable() && !backup_conn->receiving(), kDefaultTimeout,
clock);
reset_selected_candidate_pair_switches();
// Blackhole any traffic to or from the remote WiFi networks.
LOG(LS_INFO) << "Failing over...";
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, wifi[1]);
int num_switches = 0;
SIMULATED_WAIT((num_switches = reset_selected_candidate_pair_switches()) > 0,
20000, clock);
EXPECT_EQ(0, num_switches);
DestroyChannels();
}
// Tests that a Wifi-Wifi connection has the highest precedence.
TEST_F(P2PTransportChannelMultihomedTest, TestPreferWifiToWifiConnection) {
// The interface names are chosen so that |cellular| would have higher
// candidate priority if it is not for the network type.
auto& wifi = kAlternateAddrs;
auto& cellular = kPublicAddrs;
AddAddress(0, wifi[0], "test0", rtc::ADAPTER_TYPE_WIFI);
AddAddress(0, cellular[0], "test1", rtc::ADAPTER_TYPE_CELLULAR);
AddAddress(1, wifi[1], "test0", rtc::ADAPTER_TYPE_WIFI);
AddAddress(1, cellular[1], "test1", rtc::ADAPTER_TYPE_CELLULAR);
// Use only local ports for simplicity.
SetAllocatorFlags(0, kOnlyLocalPorts);
SetAllocatorFlags(1, kOnlyLocalPorts);
// Create channels and let them go writable, as usual.
CreateChannels();
EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() && ep2_ch1()->writable(),
1000, 1000);
// Need to wait to make sure the connections on both networks are writable.
EXPECT_TRUE_WAIT(ep1_ch1()->selected_connection() &&
LocalCandidate(ep1_ch1())->address().EqualIPs(wifi[0]) &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(wifi[1]),
1000);
EXPECT_TRUE_WAIT(ep2_ch1()->selected_connection() &&
LocalCandidate(ep2_ch1())->address().EqualIPs(wifi[1]) &&
RemoteCandidate(ep2_ch1())->address().EqualIPs(wifi[0]),
1000);
DestroyChannels();
}
// Tests that a Wifi-Cellular connection has higher precedence than
// a Cellular-Cellular connection.
TEST_F(P2PTransportChannelMultihomedTest, TestPreferWifiOverCellularNetwork) {
// The interface names are chosen so that |cellular| would have higher
// candidate priority if it is not for the network type.
auto& wifi = kAlternateAddrs;
auto& cellular = kPublicAddrs;
AddAddress(0, cellular[0], "test1", rtc::ADAPTER_TYPE_CELLULAR);
AddAddress(1, wifi[1], "test0", rtc::ADAPTER_TYPE_WIFI);
AddAddress(1, cellular[1], "test1", rtc::ADAPTER_TYPE_CELLULAR);
// Use only local ports for simplicity.
SetAllocatorFlags(0, kOnlyLocalPorts);
SetAllocatorFlags(1, kOnlyLocalPorts);
// Create channels and let them go writable, as usual.
CreateChannels();
EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() && ep2_ch1()->writable(),
1000, 1000);
// Need to wait to make sure the connections on both networks are writable.
EXPECT_TRUE_WAIT(ep1_ch1()->selected_connection() &&
RemoteCandidate(ep1_ch1())->address().EqualIPs(wifi[1]),
1000);
EXPECT_TRUE_WAIT(ep2_ch1()->selected_connection() &&
LocalCandidate(ep2_ch1())->address().EqualIPs(wifi[1]),
1000);
DestroyChannels();
}
// Test that the backup connection is pinged at a rate no faster than
// what was configured.
TEST_F(P2PTransportChannelMultihomedTest, TestPingBackupConnectionRate) {
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();
EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1