| /* |
| * 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/p2p/base/fakeportallocator.h" |
| #include "webrtc/p2p/base/p2ptransportchannel.h" |
| #include "webrtc/p2p/base/testrelayserver.h" |
| #include "webrtc/p2p/base/teststunserver.h" |
| #include "webrtc/p2p/base/testturnserver.h" |
| #include "webrtc/p2p/client/basicportallocator.h" |
| #include "webrtc/base/dscp.h" |
| #include "webrtc/base/fakeclock.h" |
| #include "webrtc/base/fakenetwork.h" |
| #include "webrtc/base/firewallsocketserver.h" |
| #include "webrtc/base/gunit.h" |
| #include "webrtc/base/helpers.h" |
| #include "webrtc/base/logging.h" |
| #include "webrtc/base/natserver.h" |
| #include "webrtc/base/natsocketfactory.h" |
| #include "webrtc/base/physicalsocketserver.h" |
| #include "webrtc/base/proxyserver.h" |
| #include "webrtc/base/socketaddress.h" |
| #include "webrtc/base/ssladapter.h" |
| #include "webrtc/base/thread.h" |
| #include "webrtc/base/virtualsocketserver.h" |
| |
| 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 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("2620: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)}; |
| // Addresses for HTTP proxy servers. |
| static const SocketAddress kHttpsProxyAddrs[2] = |
| { SocketAddress("11.11.11.1", 443), SocketAddress("22.22.22.1", 443) }; |
| // Addresses for SOCKS proxy servers. |
| static const SocketAddress kSocksProxyAddrs[2] = |
| { SocketAddress("11.11.11.1", 1080), SocketAddress("22.22.22.1", 1080) }; |
| // Internal addresses for NAT boxes. |
| static const SocketAddress kNatAddrs[2] = |
| { SocketAddress("192.168.1.1", 0), SocketAddress("192.168.2.1", 0) }; |
| // Private addresses inside the NAT private networks. |
| static const SocketAddress kPrivateAddrs[2] = |
| { SocketAddress("192.168.1.11", 0), SocketAddress("192.168.2.22", 0) }; |
| // For cascaded NATs, the internal addresses of the inner NAT boxes. |
| static const SocketAddress kCascadedNatAddrs[2] = |
| { SocketAddress("192.168.10.1", 0), SocketAddress("192.168.20.1", 0) }; |
| // For cascaded NATs, private addresses inside the inner private networks. |
| static const SocketAddress kCascadedPrivateAddrs[2] = |
| { SocketAddress("192.168.10.11", 0), SocketAddress("192.168.20.22", 0) }; |
| // The address of the public STUN server. |
| static const SocketAddress kStunAddr("99.99.99.1", cricket::STUN_SERVER_PORT); |
| // The addresses for the public relay server. |
| static const SocketAddress kRelayUdpIntAddr("99.99.99.2", 5000); |
| static const SocketAddress kRelayUdpExtAddr("99.99.99.3", 5001); |
| static const SocketAddress kRelayTcpIntAddr("99.99.99.2", 5002); |
| static const SocketAddress kRelayTcpExtAddr("99.99.99.3", 5003); |
| static const SocketAddress kRelaySslTcpIntAddr("99.99.99.2", 5004); |
| static const SocketAddress kRelaySslTcpExtAddr("99.99.99.3", 5005); |
| // The addresses for the public turn server. |
| static const SocketAddress kTurnUdpIntAddr("99.99.99.4", |
| cricket::STUN_SERVER_PORT); |
| static const SocketAddress 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; |
| } |
| |
| } // 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() |
| : main_(rtc::Thread::Current()), |
| pss_(new rtc::PhysicalSocketServer), |
| vss_(new rtc::VirtualSocketServer(pss_.get())), |
| nss_(new rtc::NATSocketServer(vss_.get())), |
| ss_(new rtc::FirewallSocketServer(nss_.get())), |
| ss_scope_(ss_.get()), |
| stun_server_(TestStunServer::Create(main_, kStunAddr)), |
| turn_server_(main_, kTurnUdpIntAddr, kTurnUdpExtAddr), |
| relay_server_(main_, |
| kRelayUdpIntAddr, |
| kRelayUdpExtAddr, |
| kRelayTcpIntAddr, |
| kRelayTcpExtAddr, |
| kRelaySslTcpIntAddr, |
| kRelaySslTcpExtAddr), |
| socks_server1_(ss_.get(), |
| kSocksProxyAddrs[0], |
| ss_.get(), |
| kSocksProxyAddrs[0]), |
| socks_server2_(ss_.get(), |
| kSocksProxyAddrs[1], |
| ss_.get(), |
| kSocksProxyAddrs[1]), |
| force_relay_(false) { |
| ep1_.role_ = ICEROLE_CONTROLLING; |
| ep2_.role_ = ICEROLE_CONTROLLED; |
| |
| ServerAddresses stun_servers; |
| stun_servers.insert(kStunAddr); |
| ep1_.allocator_.reset(new BasicPortAllocator( |
| &ep1_.network_manager_, stun_servers, kRelayUdpIntAddr, |
| kRelayTcpIntAddr, kRelaySslTcpIntAddr)); |
| ep2_.allocator_.reset(new BasicPortAllocator( |
| &ep2_.network_manager_, stun_servers, kRelayUdpIntAddr, |
| kRelayTcpIntAddr, kRelaySslTcpIntAddr)); |
| } |
| |
| protected: |
| enum Config { |
| OPEN, // Open to the Internet |
| NAT_FULL_CONE, // NAT, no filtering |
| NAT_ADDR_RESTRICTED, // NAT, must send to an addr to recv |
| NAT_PORT_RESTRICTED, // NAT, must send to an addr+port to recv |
| NAT_SYMMETRIC, // NAT, endpoint-dependent bindings |
| NAT_DOUBLE_CONE, // Double NAT, both cone |
| NAT_SYMMETRIC_THEN_CONE, // Double NAT, symmetric outer, cone inner |
| BLOCK_UDP, // Firewall, UDP in/out blocked |
| BLOCK_UDP_AND_INCOMING_TCP, // Firewall, UDP in/out and TCP in blocked |
| BLOCK_ALL_BUT_OUTGOING_HTTP, // Firewall, only TCP out on 80/443 |
| PROXY_HTTPS, // All traffic through HTTPS proxy |
| PROXY_SOCKS, // All traffic through SOCKS proxy |
| NUM_CONFIGS |
| }; |
| |
| struct Result { |
| Result(const std::string& 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(TransportChannel* ch, const Candidate& c) |
| : channel(ch), candidates(1, c) {} |
| CandidatesData(TransportChannel* ch, const std::vector<Candidate>& cc) |
| : channel(ch), candidates(cc) {} |
| TransportChannel* channel; |
| Candidates candidates; |
| }; |
| |
| struct Endpoint { |
| Endpoint() |
| : role_(ICEROLE_UNKNOWN), |
| tiebreaker_(0), |
| role_conflict_(false), |
| save_candidates_(false) {} |
| bool HasChannel(TransportChannel* ch) { |
| return (ch == cd1_.ch_.get() || ch == cd2_.ch_.get()); |
| } |
| ChannelData* GetChannelData(TransportChannel* ch) { |
| if (!HasChannel(ch)) return NULL; |
| if (cd1_.ch_.get() == ch) |
| 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_; |
| 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(TransportChannel* channel) { |
| if (ep1_.HasChannel(channel)) |
| return ep1_.GetChannelData(channel); |
| else |
| return ep2_.GetChannelData(channel); |
| } |
| |
| IceParameters IceParamsWithRenomination(const IceParameters& ice, |
| bool renomination) { |
| IceParameters new_ice = ice; |
| new_ice.renomination = renomination; |
| return new_ice; |
| } |
| |
| void CreateChannels(int num, |
| 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_->SetIceConfig(ep1_config); |
| ep2_.cd1_.ch_->SetIceConfig(ep2_config); |
| ep1_.cd1_.ch_->MaybeStartGathering(); |
| ep2_.cd1_.ch_->MaybeStartGathering(); |
| if (num == 2) { |
| IceParameters ice_ep1_cd2_ch = |
| IceParamsWithRenomination(kIceParams[2], renomination); |
| IceParameters ice_ep2_cd2_ch = |
| IceParamsWithRenomination(kIceParams[3], renomination); |
| ep1_.cd2_.ch_.reset(CreateChannel(0, ICE_CANDIDATE_COMPONENT_DEFAULT, |
| ice_ep1_cd2_ch, ice_ep2_cd2_ch)); |
| ep2_.cd2_.ch_.reset(CreateChannel(1, ICE_CANDIDATE_COMPONENT_DEFAULT, |
| ice_ep2_cd2_ch, ice_ep1_cd2_ch)); |
| ep1_.cd2_.ch_->SetIceConfig(ep1_config); |
| ep2_.cd2_.ch_->SetIceConfig(ep2_config); |
| ep1_.cd2_.ch_->MaybeStartGathering(); |
| ep2_.cd2_.ch_->MaybeStartGathering(); |
| } |
| } |
| |
| void CreateChannels(int num) { |
| IceConfig default_config; |
| CreateChannels(num, 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; |
| } |
| } |
| PortAllocator* GetAllocator(int endpoint) { |
| return GetEndpoint(endpoint)->allocator_.get(); |
| } |
| 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) { |
| int64_t connect_start = rtc::TimeMillis(); |
| int64_t connect_time; |
| |
| // Create the channels and wait for them to connect. |
| CreateChannels(1); |
| EXPECT_TRUE_WAIT_MARGIN(ep1_ch1() != NULL && |
| ep2_ch1() != NULL && |
| ep1_ch1()->receiving() && |
| ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && |
| ep2_ch1()->writable(), |
| expected.connect_wait, |
| 1000); |
| 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; |
| int64_t converge_wait = 2000; |
| // 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_WAIT_MARGIN( |
| CheckCandidate1(expected) && CheckCandidate2(expected), converge_wait, |
| converge_wait); |
| // Also do EXPECT_EQ on each part so that failures are more verbose. |
| ExpectCandidate1(expected); |
| ExpectCandidate2(expected); |
| |
| converge_time = rtc::TimeMillis() - converge_start; |
| if (converge_time < converge_wait) { |
| LOG(LS_INFO) << "Converge time: " << converge_time << " ms"; |
| } else { |
| LOG(LS_INFO) << "Converge time: " << "TIMEOUT (" |
| << converge_wait << " ms)"; |
| } |
| } |
| // Try sending some data to other end. |
| TestSendRecv(1); |
| |
| // Destroy the channels, and wait for them to be fully cleaned up. |
| DestroyChannels(); |
| } |
| |
| void TestSendRecv(int channels) { |
| for (int i = 0; i < 10; ++i) { |
| const char* data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; |
| int len = static_cast<int>(strlen(data)); |
| // local_channel1 <==> remote_channel1 |
| EXPECT_EQ_WAIT(len, SendData(ep1_ch1(), data, len), 1000); |
| EXPECT_TRUE_WAIT(CheckDataOnChannel(ep2_ch1(), data, len), 1000); |
| EXPECT_EQ_WAIT(len, SendData(ep2_ch1(), data, len), 1000); |
| EXPECT_TRUE_WAIT(CheckDataOnChannel(ep1_ch1(), data, len), 1000); |
| if (channels == 2 && ep1_ch2() && ep2_ch2()) { |
| // local_channel2 <==> remote_channel2 |
| EXPECT_EQ_WAIT(len, SendData(ep1_ch2(), data, len), 1000); |
| EXPECT_TRUE_WAIT(CheckDataOnChannel(ep2_ch2(), data, len), 1000); |
| EXPECT_EQ_WAIT(len, SendData(ep2_ch2(), data, len), 1000); |
| EXPECT_TRUE_WAIT(CheckDataOnChannel(ep1_ch2(), data, len), 1000); |
| } |
| } |
| } |
| |
| // This test waits for the transport to become 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() { |
| ep1_ch1()->SetRemoteIceParameters(kIceParams[1]); |
| ep2_ch1()->SetRemoteIceParameters(kIceParams[0]); |
| EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && ep2_ch1()->writable(), |
| 1000, 1000); |
| |
| 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_WAIT_MARGIN(LocalCandidate(ep1_ch1())->generation() != |
| old_local_candidate1->generation(), |
| 1000, 1000); |
| EXPECT_TRUE_WAIT_MARGIN(LocalCandidate(ep2_ch1())->generation() != |
| old_local_candidate2->generation(), |
| 1000, 1000); |
| EXPECT_TRUE_WAIT_MARGIN(RemoteCandidate(ep1_ch1())->generation() != |
| old_remote_candidate1->generation(), |
| 1000, 1000); |
| EXPECT_TRUE_WAIT_MARGIN(RemoteCandidate(ep2_ch1())->generation() != |
| old_remote_candidate2->generation(), |
| 1000, 1000); |
| EXPECT_EQ(1u, RemoteCandidate(ep2_ch1())->generation()); |
| EXPECT_EQ(1u, RemoteCandidate(ep1_ch1())->generation()); |
| } |
| |
| void TestSignalRoleConflict() { |
| SetIceTiebreaker(0, |
| kLowTiebreaker); // Default EP1 is in controlling state. |
| |
| SetIceRole(1, ICEROLE_CONTROLLING); |
| SetIceTiebreaker(1, kHighTiebreaker); |
| |
| // Creating channels with both channels role set to CONTROLLING. |
| CreateChannels(1); |
| // Since both the channels initiated with controlling state and channel2 |
| // has higher tiebreaker value, channel1 should receive SignalRoleConflict. |
| EXPECT_TRUE_WAIT(GetRoleConflict(0), 1000); |
| EXPECT_FALSE(GetRoleConflict(1)); |
| |
| EXPECT_TRUE_WAIT(ep1_ch1()->receiving() && |
| ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && |
| ep2_ch1()->writable(), |
| 1000); |
| |
| EXPECT_TRUE(ep1_ch1()->selected_connection() && |
| ep2_ch1()->selected_connection()); |
| |
| TestSendRecv(1); |
| } |
| |
| void OnReadyToSend(TransportChannel* ch) { |
| GetEndpoint(ch)->ready_to_send_ = true; |
| } |
| |
| // We pass the candidates directly to the other side. |
| void OnCandidateGathered(TransportChannelImpl* 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( |
| TransportChannel* 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(TransportChannelImpl* 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(TransportChannel* channel, |
| const char* data, |
| size_t len, |
| const rtc::PacketTime& packet_time, |
| int flags) { |
| std::list<std::string>& packets = GetPacketList(channel); |
| packets.push_front(std::string(data, len)); |
| } |
| void OnRoleConflict(TransportChannelImpl* channel) { |
| GetEndpoint(channel)->OnRoleConflict(true); |
| IceRole new_role = GetEndpoint(channel)->ice_role() == ICEROLE_CONTROLLING |
| ? ICEROLE_CONTROLLED |
| : ICEROLE_CONTROLLING; |
| channel->SetIceRole(new_role); |
| } |
| |
| int SendData(TransportChannel* channel, const char* data, size_t len) { |
| rtc::PacketOptions options; |
| return channel->SendPacket(data, len, options, 0); |
| } |
| bool CheckDataOnChannel(TransportChannel* 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(TransportChannel* ch) { |
| if (ep1_.HasChannel(ch)) { |
| return &ep1_; |
| } else if (ep2_.HasChannel(ch)) { |
| return &ep2_; |
| } else { |
| return NULL; |
| } |
| } |
| P2PTransportChannel* GetRemoteChannel(TransportChannel* ch) { |
| if (ch == ep1_ch1()) |
| return ep2_ch1(); |
| else if (ch == ep1_ch2()) |
| return ep2_ch2(); |
| else if (ch == ep2_ch1()) |
| return ep1_ch1(); |
| else if (ch == ep2_ch2()) |
| return ep1_ch2(); |
| else |
| return NULL; |
| } |
| std::list<std::string>& GetPacketList(TransportChannel* ch) { |
| return GetChannelData(ch)->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: |
| rtc::Thread* main_; |
| std::unique_ptr<rtc::PhysicalSocketServer> pss_; |
| std::unique_ptr<rtc::VirtualSocketServer> vss_; |
| std::unique_ptr<rtc::NATSocketServer> nss_; |
| std::unique_ptr<rtc::FirewallSocketServer> ss_; |
| rtc::SocketServerScope ss_scope_; |
| std::unique_ptr<TestStunServer> stun_server_; |
| TestTurnServer turn_server_; |
| TestRelayServer relay_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) { |
| ServerAddresses stun_servers; |
| stun_servers.insert(kStunAddr); |
| GetEndpoint(0)->allocator_.reset(new BasicPortAllocator( |
| &(GetEndpoint(0)->network_manager_), stun_servers, rtc::SocketAddress(), |
| rtc::SocketAddress(), rtc::SocketAddress())); |
| GetEndpoint(1)->allocator_.reset(new BasicPortAllocator( |
| &(GetEndpoint(1)->network_manager_), stun_servers, rtc::SocketAddress(), |
| rtc::SocketAddress(), rtc::SocketAddress())); |
| |
| RelayServerConfig turn_server(RELAY_TURN); |
| turn_server.credentials = kRelayCredentials; |
| turn_server.ports.push_back( |
| ProtocolAddress(kTurnUdpIntAddr, PROTO_UDP, false)); |
| GetEndpoint(0)->allocator_->AddTurnServer(turn_server); |
| GetEndpoint(1)->allocator_->AddTurnServer(turn_server); |
| |
| int delay = kMinimumStepDelay; |
| ConfigureEndpoint(0, config1); |
| SetAllocatorFlags(0, allocator_flags1); |
| SetAllocationStepDelay(0, delay); |
| ConfigureEndpoint(1, config2); |
| SetAllocatorFlags(1, allocator_flags2); |
| SetAllocationStepDelay(1, delay); |
| |
| 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: |
| 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(1); |
| 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(1); |
| TestHandleIceUfragPasswordChanged(); |
| DestroyChannels(); |
| } |
| |
| // Test the operation of GetStats. |
| TEST_F(P2PTransportChannelTest, GetStats) { |
| ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, |
| kDefaultPortAllocatorFlags); |
| CreateChannels(1); |
| EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && ep2_ch1()->writable(), |
| 1000, 1000); |
| TestSendRecv(1); |
| 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); |
| EXPECT_GT(best_conn_info->rtt, 0U); |
| DestroyChannels(); |
| } |
| |
| // Test that we properly create a connection on a STUN ping from unknown address |
| // when the signaling is slow. |
| TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignaling) { |
| ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, |
| kDefaultPortAllocatorFlags); |
| // Emulate no remote parameters coming in. |
| set_remote_ice_parameter_source(FROM_CANDIDATE); |
| CreateChannels(1); |
| // 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, 3000); |
| // 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, |
| 2000); |
| 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(), |
| 2000); |
| 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(1); |
| // 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, 3000); |
| // 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, |
| 2000); |
| 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(), |
| 2000); |
| 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(1); |
| // 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) { |
| set_remote_ice_parameter_source(FROM_SETICEPARAMETERS); |
| ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, |
| kDefaultPortAllocatorFlags); |
| CreateChannels(1); |
| const Connection* selected_connection = NULL; |
| // Wait until the callee's connections are created. |
| WAIT((selected_connection = ep2_ch1()->selected_connection()) != NULL, 1000); |
| // Wait to see if they get culled; they shouldn't. |
| WAIT(ep2_ch1()->selected_connection() != selected_connection, 1000); |
| 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) { |
| ConfigureEndpoints(NAT_FULL_CONE, OPEN, kDefaultPortAllocatorFlags, |
| kDefaultPortAllocatorFlags); |
| |
| SetAllocatorFlags(0, kOnlyLocalPorts); |
| CreateChannels(1); |
| ep1_ch1()->set_incoming_only(true); |
| |
| // Pump for 1 second and verify that the channels are not connected. |
| rtc::Thread::Current()->ProcessMessages(1000); |
| |
| EXPECT_FALSE(ep1_ch1()->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) { |
| ConfigureEndpoints(OPEN, NAT_FULL_CONE, kDefaultPortAllocatorFlags, |
| kDefaultPortAllocatorFlags); |
| |
| SetAllocatorFlags(0, kOnlyLocalPorts); |
| CreateChannels(1); |
| ep1_ch1()->set_incoming_only(true); |
| |
| EXPECT_TRUE_WAIT_MARGIN(ep1_ch1() != NULL && ep2_ch1() != NULL && |
| ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && ep2_ch1()->writable(), |
| 1000, 1000); |
| |
| DestroyChannels(); |
| } |
| |
| TEST_F(P2PTransportChannelTest, TestTcpConnectionsFromActiveToPassive) { |
| AddAddress(0, kPublicAddrs[0]); |
| AddAddress(1, kPublicAddrs[1]); |
| |
| SetAllocationStepDelay(0, kMinimumStepDelay); |
| SetAllocationStepDelay(1, kMinimumStepDelay); |
| |
| int kOnlyLocalTcpPorts = 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(1); |
| |
| // Verify tcp candidates. |
| VerifySavedTcpCandidates(0, TCPTYPE_PASSIVE_STR); |
| VerifySavedTcpCandidates(1, TCPTYPE_ACTIVE_STR); |
| |
| // Resume candidates. |
| ResumeCandidates(0); |
| ResumeCandidates(1); |
| |
| EXPECT_TRUE_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && ep2_ch1()->writable(), |
| 1000); |
| 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(1); |
| DestroyChannels(); |
| } |
| |
| TEST_F(P2PTransportChannelTest, TestIceRoleConflict) { |
| AddAddress(0, kPublicAddrs[0]); |
| AddAddress(1, kPublicAddrs[1]); |
| TestSignalRoleConflict(); |
| } |
| |
| // Tests that the ice configs (protocol, tiebreaker and role) can be passed |
| // down to ports. |
| TEST_F(P2PTransportChannelTest, TestIceConfigWillPassDownToPort) { |
| AddAddress(0, kPublicAddrs[0]); |
| AddAddress(1, kPublicAddrs[1]); |
| |
| // 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(1); |
| |
| EXPECT_EQ_WAIT(2u, ep1_ch1()->ports().size(), 1000); |
| |
| 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_WAIT(ep1_ch1()->receiving() && |
| ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && |
| ep2_ch1()->writable(), |
| 1000); |
| |
| EXPECT_TRUE(ep1_ch1()->selected_connection() && |
| ep2_ch1()->selected_connection()); |
| |
| TestSendRecv(1); |
| 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(1); |
| EXPECT_EQ(rtc::DSCP_NO_CHANGE, |
| GetEndpoint(0)->cd1_.ch_->DefaultDscpValue()); |
| EXPECT_EQ(rtc::DSCP_NO_CHANGE, |
| GetEndpoint(1)->cd1_.ch_->DefaultDscpValue()); |
| GetEndpoint(0)->cd1_.ch_->SetOption( |
| rtc::Socket::OPT_DSCP, rtc::DSCP_CS6); |
| GetEndpoint(1)->cd1_.ch_->SetOption( |
| rtc::Socket::OPT_DSCP, rtc::DSCP_CS6); |
| EXPECT_EQ(rtc::DSCP_CS6, |
| GetEndpoint(0)->cd1_.ch_->DefaultDscpValue()); |
| EXPECT_EQ(rtc::DSCP_CS6, |
| GetEndpoint(1)->cd1_.ch_->DefaultDscpValue()); |
| GetEndpoint(0)->cd1_.ch_->SetOption( |
| rtc::Socket::OPT_DSCP, rtc::DSCP_AF41); |
| GetEndpoint(1)->cd1_.ch_->SetOption( |
| rtc::Socket::OPT_DSCP, rtc::DSCP_AF41); |
| EXPECT_EQ(rtc::DSCP_AF41, |
| GetEndpoint(0)->cd1_.ch_->DefaultDscpValue()); |
| EXPECT_EQ(rtc::DSCP_AF41, |
| GetEndpoint(1)->cd1_.ch_->DefaultDscpValue()); |
| } |
| |
| // Verify IPv6 connection is preferred over IPv4. |
| TEST_F(P2PTransportChannelTest, TestIPv6Connections) { |
| AddAddress(0, kIPv6PublicAddrs[0]); |
| AddAddress(0, kPublicAddrs[0]); |
| AddAddress(1, kIPv6PublicAddrs[1]); |
| AddAddress(1, kPublicAddrs[1]); |
| |
| SetAllocationStepDelay(0, kMinimumStepDelay); |
| SetAllocationStepDelay(1, kMinimumStepDelay); |
| |
| // Enable IPv6 |
| SetAllocatorFlags(0, PORTALLOCATOR_ENABLE_IPV6); |
| SetAllocatorFlags(1, PORTALLOCATOR_ENABLE_IPV6); |
| |
| CreateChannels(1); |
| |
| EXPECT_TRUE_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && ep2_ch1()->writable(), |
| 1000); |
| 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(1); |
| DestroyChannels(); |
| } |
| |
| // Testing forceful TURN connections. |
| TEST_F(P2PTransportChannelTest, TestForceTurn) { |
| 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(1); |
| |
| EXPECT_TRUE_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && ep2_ch1()->writable(), |
| 2000); |
| |
| 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(1); |
| 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) { |
| 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(1, continual_gathering_config, default_config); |
| |
| EXPECT_TRUE_WAIT_MARGIN(ep1_ch1() != NULL && ep2_ch1() != NULL && |
| ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && ep2_ch1()->writable(), |
| 1000, 1000); |
| WAIT(IceGatheringState::kIceGatheringComplete == ep1_ch1()->gathering_state(), |
| 1000); |
| 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) { |
| 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(1); |
| EXPECT_TRUE_WAIT_MARGIN(ep1_ch1() != NULL && ep2_ch1() != NULL && |
| ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && ep2_ch1()->writable(), |
| 1000, 1000); |
| TestSendRecv(1); |
| // 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) { |
| 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_WAIT(pooled_session_1->CandidatesAllocationDone() && |
| pooled_session_2->CandidatesAllocationDone(), |
| kDefaultTimeout); |
| // Now let the endpoints connect and try exchanging some data. |
| CreateChannels(1); |
| EXPECT_TRUE_WAIT_MARGIN(ep1_ch1() != NULL && ep2_ch1() != NULL && |
| ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && ep2_ch1()->writable(), |
| 1000, 1000); |
| TestSendRecv(1); |
| // 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. |
| 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(), |
| 1000, 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(), 1000, |
| 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(), |
| 1000, 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(), 10000, 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(); |
| } |
| |
| // Test what happens when we have 2 users behind the same NAT. This can lead |
| // to interesting behavior because the STUN server will only give out the |
| // address of the outermost NAT. |
| class P2PTransportChannelSameNatTest : public P2PTransportChannelTestBase { |
| protected: |
| void ConfigureEndpoints(Config nat_type, Config config1, Config config2) { |
| ASSERT(nat_type >= NAT_FULL_CONE && nat_type <= NAT_SYMMETRIC); |
| rtc::NATSocketServer::Translator* outer_nat = |
| nat()->AddTranslator(kPublicAddrs[0], kNatAddrs[0], |
| static_cast<rtc::NATType>(nat_type - NAT_FULL_CONE)); |
| ConfigureEndpoint(outer_nat, 0, config1); |
| ConfigureEndpoint(outer_nat, 1, config2); |
| set_remote_ice_parameter_source(FROM_SETICEPARAMETERS); |
| } |
| void ConfigureEndpoint(rtc::NATSocketServer::Translator* nat, |
| int endpoint, Config config) { |
| ASSERT(config <= NAT_SYMMETRIC); |
| if (config == OPEN) { |
| AddAddress(endpoint, kPrivateAddrs[endpoint]); |
| nat->AddClient(kPrivateAddrs[endpoint]); |
| } else { |
| AddAddress(endpoint, kCascadedPrivateAddrs[endpoint]); |
| nat->AddTranslator(kPrivateAddrs[endpoint], kCascadedNatAddrs[endpoint], |
| static_cast<rtc::NATType>(config - NAT_FULL_CONE))->AddClient( |
| kCascadedPrivateAddrs[endpoint]); |
| } |
| } |
| }; |
| |
| TEST_F(P2PTransportChannelSameNatTest, TestConesBehindSameCone) { |
| ConfigureEndpoints(NAT_FULL_CONE, NAT_FULL_CONE, NAT_FULL_CONE); |
| Test( |
| 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 cricket::Connection* GetConnectionWithRemoteAddress( |
| cricket::P2PTransportChannel* channel, |
| const SocketAddress& address) { |
| for (cricket::Connection* conn : channel->connections()) { |
| if (conn->remote_candidate().address().EqualIPs(address)) { |
| return conn; |
| } |
| } |
| return nullptr; |
| } |
| |
| cricket::Connection* GetConnectionWithLocalAddress( |
| cricket::P2PTransportChannel* channel, |
| const SocketAddress& address) { |
| for (cricket::Connection* conn : channel->connections()) { |
| if (conn->local_candidate().address().EqualIPs(address)) { |
| return conn; |
| } |
| } |
| return nullptr; |
| } |
| |
| void DestroyAllButBestConnection(cricket::P2PTransportChannel* channel) { |
| const cricket::Connection* selected_connection = |
| channel->selected_connection(); |
| for (cricket::Connection* conn : channel->connections()) { |
| if (conn != selected_connection) { |
| conn->Destroy(); |
| } |
| } |
| } |
| }; |
| |
| // Test that we can establish connectivity when both peers are multihomed. |
| TEST_F(P2PTransportChannelMultihomedTest, DISABLED_TestBasic) { |
| AddAddress(0, kPublicAddrs[0]); |
| AddAddress(0, kAlternateAddrs[0]); |
| AddAddress(1, kPublicAddrs[1]); |
| AddAddress(1, kAlternateAddrs[1]); |
| Test(kLocalUdpToLocalUdp); |
| } |
| |
| // Test that we can quickly switch links if an interface goes down. |
| // The controlled side has two interfaces and one will die. |
| TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControlledSide) { |
| rtc::ScopedFakeClock clock; |
| 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); |
| |
| // Make the receiving timeout shorter for testing. |
| IceConfig config = CreateIceConfig(1000, GATHER_ONCE); |
| // Create channels and let them go writable, as usual. |
| CreateChannels(1, config, config); |
| |
| EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && |
| ep2_ch1()->writable(), |
| 3000, 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(), |
| 3000, 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(), |
| 3000, 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; |
| // Adding alternate address will make sure |kPublicAddrs| has the higher |
| // priority than others. This is due to FakeNetwork::AddInterface method. |
| AddAddress(0, kAlternateAddrs[0]); |
| AddAddress(0, kPublicAddrs[0]); |
| 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(1, config, config); |
| EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && |
| ep2_ch1()->writable(), |
| 3000, 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(), |
| 3000, 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(), |
| 3000, 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(); |
| } |
| |
| // 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; |
| // Adding alternate address will make sure |kPublicAddrs| has the higher |
| // priority than others. This is due to FakeNetwork::AddInterface method. |
| AddAddress(0, kAlternateAddrs[0]); |
| AddAddress(0, kPublicAddrs[0]); |
| 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(1, config, config, true); |
| EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && |
| ep2_ch1()->writable(), |
| 3000, 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(), 3000, 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, 3000, 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]); |
| // Adding alternate address will make sure |kPublicAddrs| has the higher |
| // priority than others. This is due to FakeNetwork::AddInterface method. |
| AddAddress(1, kAlternateAddrs[1]); |
| AddAddress(1, kPublicAddrs[1]); |
| |
| // Use only local ports for simplicity. |
| SetAllocatorFlags(0, kOnlyLocalPorts); |
| SetAllocatorFlags(1, kOnlyLocalPorts); |
| |
| // Create channels and let them go writable, as usual. |
| CreateChannels(1); |
| |
| EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && |
| ep2_ch1()->writable(), |
| 3000, 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(), |
| 3000, 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(), |
| 3000, 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; |
| // Adding alternate address will make sure |kPublicAddrs| has the higher |
| // priority than others. This is due to FakeNetwork::AddInterface method. |
| AddAddress(0, kAlternateAddrs[0]); |
| AddAddress(0, kPublicAddrs[0]); |
| AddAddress(1, kPublicAddrs[1]); |
| |
| // Use only local ports for simplicity. |
| SetAllocatorFlags(0, kOnlyLocalPorts); |
| SetAllocatorFlags(1, kOnlyLocalPorts); |
| |
| // Create channels and let them go writable, as usual. |
| CreateChannels(1); |
| EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && |
| ep2_ch1()->writable(), |
| 3000, 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(), |
| 3000, 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(), |
| 3000, 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(1); |
| // 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(), |
| 3000, 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(), 5000, 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(1); |
| |
| 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(1); |
| |
| 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(1); |
| EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && ep2_ch1()->writable(), |
| 1000, 1000); |
| int backup_ping_interval = 2000; |
| ep2_ch1()->SetIceConfig( |
| CreateIceConfig(2000, GATHER_ONCE, backup_ping_interval)); |
| // After the state becomes COMPLETED, the backup connection will be pinged |
| // once every |backup_ping_interval| milliseconds. |
| ASSERT_TRUE_WAIT(ep2_ch1()->GetState() == STATE_COMPLETED, 1000); |
| const std::vector<Connection*>& connections = ep2_ch1()->connections(); |
| ASSERT_EQ(2U, connections.size()); |
| Connection* backup_conn = connections[1]; |
| EXPECT_TRUE_WAIT(backup_conn->writable(), 3000); |
| int64_t last_ping_response_ms = backup_conn->last_ping_response_received(); |
| EXPECT_TRUE_WAIT( |
| last_ping_response_ms < backup_conn->last_ping_response_received(), 5000); |
| int time_elapsed = |
| backup_conn->last_ping_response_received() - last_ping_response_ms; |
| LOG(LS_INFO) << "Time elapsed: " << time_elapsed; |
| EXPECT_GE(time_elapsed, backup_ping_interval); |
| |
| DestroyChannels(); |
| } |
| |
| TEST_F(P2PTransportChannelMultihomedTest, TestGetState) { |
| AddAddress(0, kAlternateAddrs[0]); |
| AddAddress(0, kPublicAddrs[0]); |
| AddAddress(1, kPublicAddrs[1]); |
| // Create channels and let them go writable, as usual. |
| CreateChannels(1); |
| |
| // Both transport channels will reach STATE_COMPLETED quickly. |
| EXPECT_EQ_WAIT(TransportChannelState::STATE_COMPLETED, ep1_ch1()->GetState(), |
| 1000); |
| EXPECT_EQ_WAIT(TransportChannelState::STATE_COMPLETED, ep2_ch1()->GetState(), |
| 1000); |
| } |
| |
| // Tests that when a network interface becomes inactive, if Continual Gathering |
| // policy is GATHER_CONTINUALLY, the ports associated with that network |
| // will be removed from the port list of the channel, and the respective |
| // remote candidates on the other participant will be removed eventually. |
| TEST_F(P2PTransportChannelMultihomedTest, TestNetworkBecomesInactive) { |
| rtc::ScopedFakeClock clock; |
| AddAddress(0, kPublicAddrs[0]); |
| AddAddress(1, kPublicAddrs[1]); |
| // Create channels and let them go writable, as usual. |
| IceConfig ep1_config = CreateIceConfig(2000, GATHER_CONTINUALLY); |
| IceConfig ep2_config = CreateIceConfig(2000, GATHER_ONCE); |
| CreateChannels(1, ep1_config, ep2_config); |
| |
| SetAllocatorFlags(0, kOnlyLocalPorts); |
| SetAllocatorFlags(1, kOnlyLocalPorts); |
| EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && |
| ep2_ch1()->writable(), |
| kDefaultTimeout, clock); |
| // More than one port has been created. |
| EXPECT_LE(1U, ep1_ch1()->ports().size()); |
| // Endpoint 1 enabled continual gathering; the port will be removed |
| // when the interface is removed. |
| RemoveAddress(0, kPublicAddrs[0]); |
| EXPECT_TRUE(ep1_ch1()->ports().empty()); |
| // The remote candidates will be removed eventually. |
| EXPECT_TRUE_SIMULATED_WAIT(ep2_ch1()->remote_candidates().empty(), 1000, |
| clock); |
| |
| size_t num_ports = ep2_ch1()->ports().size(); |
| EXPECT_LE(1U, num_ports); |
| size_t num_remote_candidates = ep1_ch1()->remote_candidates().size(); |
| // Endpoint 2 did not enable continual gathering; the local port will still be |
| // removed when the interface is removed but the remote candidates on the |
| // other participant will not be removed. |
| RemoveAddress(1, kPublicAddrs[1]); |
| |
| EXPECT_EQ_SIMULATED_WAIT(0U, ep2_ch1()->ports().size(), kDefaultTimeout, |
| clock); |
| SIMULATED_WAIT(0U == ep1_ch1()->remote_candidates().size(), 500, clock); |
| EXPECT_EQ(num_remote_candidates, ep1_ch1()->remote_candidates().size()); |
| |
| DestroyChannels(); |
| } |
| |
| // Tests that continual gathering will create new connections when a new |
| // interface is added. |
| TEST_F(P2PTransportChannelMultihomedTest, |
| TestContinualGatheringOnNewInterface) { |
| auto& wifi = kAlternateAddrs; |
| auto& cellular = kPublicAddrs; |
| AddAddress(0, wifi[0], "test_wifi0", rtc::ADAPTER_TYPE_WIFI); |
| AddAddress(1, cellular[1], "test_cell1", rtc::ADAPTER_TYPE_CELLULAR); |
| // Set continual gathering policy. |
| IceConfig continual_gathering_config = |
| CreateIceConfig(1000, GATHER_CONTINUALLY); |
| CreateChannels(1, continual_gathering_config, continual_gathering_config); |
| SetAllocatorFlags(0, kOnlyLocalPorts); |
| SetAllocatorFlags(1, kOnlyLocalPorts); |
| EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && ep2_ch1()->writable(), |
| kDefaultTimeout, kDefaultTimeout); |
| |
| // Add a new wifi interface on end point 2. We should expect a new connection |
| // to be created and the new one will be the best connection. |
| AddAddress(1, wifi[1], "test_wifi1", rtc::ADAPTER_TYPE_WIFI); |
| const cricket::Connection* conn; |
| EXPECT_TRUE_WAIT((conn = ep1_ch1()->selected_connection()) != nullptr && |
| conn->remote_candidate().address().EqualIPs(wifi[1]), |
| kDefaultTimeout); |
| EXPECT_TRUE_WAIT((conn = ep2_ch1()->selected_connection()) != nullptr && |
| conn->local_candidate().address().EqualIPs(wifi[1]), |
| kDefaultTimeout); |
| |
| // Add a new cellular interface on end point 1, we should expect a new |
| // backup connection created using this new interface. |
| AddAddress(0, cellular[0], "test_cellular0", rtc::ADAPTER_TYPE_CELLULAR); |
| EXPECT_TRUE_WAIT( |
| ep1_ch1()->GetState() == cricket::STATE_COMPLETED && |
| (conn = GetConnectionWithLocalAddress(ep1_ch1(), cellular[0])) != |
| nullptr && |
| conn != ep1_ch1()->selected_connection() && conn->writable(), |
| kDefaultTimeout); |
| EXPECT_TRUE_WAIT( |
| ep2_ch1()->GetState() == cricket::STATE_COMPLETED && |
| (conn = GetConnectionWithRemoteAddress(ep2_ch1(), cellular[0])) != |
| nullptr && |
| conn != ep2_ch1()->selected_connection() && conn->receiving(), |
| kDefaultTimeout); |
| |
| DestroyChannels(); |
| } |
| |
| // Tests that we can switch links via continual gathering. |
| TEST_F(P2PTransportChannelMultihomedTest, |
| TestSwitchLinksViaContinualGathering) { |
| rtc::ScopedFakeClock clock; |
| AddAddress(0, kPublicAddrs[0]); |
| AddAddress(1, kPublicAddrs[1]); |
| // Use only local ports for simplicity. |
| SetAllocatorFlags(0, kOnlyLocalPorts); |
| SetAllocatorFlags(1, kOnlyLocalPorts); |
| |
| // Set continual gathering policy. |
| IceConfig continual_gathering_config = |
| CreateIceConfig(1000, GATHER_CONTINUALLY); |
| // Create channels and let them go writable, as usual. |
| CreateChannels(1, continual_gathering_config, continual_gathering_config); |
| EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && |
| ep2_ch1()->writable(), |
| 3000, 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])); |
| |
| // Add the new address first and then remove the other one. |
| LOG(LS_INFO) << "Draining..."; |
| AddAddress(1, kAlternateAddrs[1]); |
| RemoveAddress(1, kPublicAddrs[1]); |
| // We should switch to use the alternate address after an exchange of pings. |
| EXPECT_TRUE_SIMULATED_WAIT( |
| ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection() && |
| LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) && |
| RemoteCandidate(ep1_ch1())->address().EqualIPs(kAlternateAddrs[1]), |
| 3000, clock); |
| |
| // Remove one address first and then add another address. |
| LOG(LS_INFO) << "Draining again..."; |
| RemoveAddress(1, kAlternateAddrs[1]); |
| AddAddress(1, kAlternateAddrs[0]); |
| EXPECT_TRUE_SIMULATED_WAIT( |
| ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection() && |
| LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) && |
| RemoteCandidate(ep1_ch1())->address().EqualIPs(kAlternateAddrs[0]), |
| 3000, clock); |
| |
| DestroyChannels(); |
| } |
| |
| /* |
| TODO(honghaiz) Once continual gathering fully supports |
| GATHER_CONTINUALLY_AND_RECOVER, put this test back. |
| |
| // Tests that if the backup connections are lost and then the interface with the |
| // selected connection is gone, continual gathering will restore the |
| // connectivity. |
| TEST_F(P2PTransportChannelMultihomedTest, |
| TestBackupConnectionLostThenInterfaceGone) { |
| rtc::ScopedFakeClock clock; |
| auto& wifi = kAlternateAddrs; |
| auto& cellular = kPublicAddrs; |
| AddAddress(0, wifi[0], "test_wifi0", rtc::ADAPTER_TYPE_WIFI); |
| AddAddress(0, cellular[0], "test_cell0", rtc::ADAPTER_TYPE_CELLULAR); |
| AddAddress(1, wifi[1], "test_wifi1", rtc::ADAPTER_TYPE_WIFI); |
| AddAddress(1, cellular[1], "test_cell1", rtc::ADAPTER_TYPE_CELLULAR); |
| // Use only local ports for simplicity. |
| SetAllocatorFlags(0, kOnlyLocalPorts); |
| SetAllocatorFlags(1, kOnlyLocalPorts); |
| |
| // Set continual gathering policy. |
| IceConfig config = CreateIceConfig(1000, GATHER_CONTINUALLY_AND_RECOVER); |
| // Create channels and let them go writable, as usual. |
| CreateChannels(1, config, config); |
| EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && ep2_ch1()->writable(), |
| 3000, clock); |
| EXPECT_TRUE(ep1_ch1()->selected_connection() && |
| ep2_ch1()->selected_connection() && |
| LocalCandidate(ep1_ch1())->address().EqualIPs(wifi[0]) && |
| RemoteCandidate(ep1_ch1())->address().EqualIPs(wifi[1])); |
| |
| // First destroy all backup connection. |
| DestroyAllButBestConnection(ep1_ch1()); |
| |
| SIMULATED_WAIT(false, 10, clock); |
| // Then the interface of the best connection goes away. |
| RemoveAddress(0, wifi[0]); |
| EXPECT_TRUE_SIMULATED_WAIT( |
| ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection() && |
| LocalCandidate(ep1_ch1())->address().EqualIPs(cellular[0]) && |
| RemoteCandidate(ep1_ch1())->address().EqualIPs(wifi[1]), |
| 3000, clock); |
| |
| DestroyChannels(); |
| } |
| */ |
| |
| // Tests that the backup connection will be restored after it is destroyed. |
| TEST_F(P2PTransportChannelMultihomedTest, TestRestoreBackupConnection) { |
| rtc::ScopedFakeClock clock; |
| auto& wifi = kAlternateAddrs; |
| auto& cellular = kPublicAddrs; |
| AddAddress(0, wifi[0], "test_wifi0", rtc::ADAPTER_TYPE_WIFI); |
| AddAddress(0, cellular[0], "test_cell0", rtc::ADAPTER_TYPE_CELLULAR); |
| AddAddress(1, wifi[1], "test_wifi1", rtc::ADAPTER_TYPE_WIFI); |
| AddAddress(1, cellular[1], "test_cell1", 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. |
| IceConfig config = CreateIceConfig(1000, GATHER_CONTINUALLY); |
| config.regather_on_failed_networks_interval = rtc::Optional<int>(2000); |
| CreateChannels(1, config, config); |
| EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && |
| ep2_ch1()->receiving() && |
| ep2_ch1()->writable(), |
| 3000, clock); |
| EXPECT_TRUE(ep1_ch1()->selected_connection() && |
| ep2_ch1()->selected_connection() && |
| LocalCandidate(ep1_ch1())->address().EqualIPs(wifi[0]) && |
| RemoteCandidate(ep1_ch1())->address().EqualIPs(wifi[1])); |
| |
| // Destroy all backup connections. |
| DestroyAllButBestConnection(ep1_ch1()); |
| // Ensure the backup connection is removed first. |
| EXPECT_TRUE_SIMULATED_WAIT( |
| GetConnectionWithLocalAddress(ep1_ch1(), cellular[0]) == nullptr, |
| kDefaultTimeout, clock); |
| const cricket::Connection* conn; |
| EXPECT_TRUE_SIMULATED_WAIT( |
| (conn = GetConnectionWithLocalAddress(ep1_ch1(), cellular[0])) != |
| nullptr && |
| conn != ep1_ch1()->selected_connection() && conn->writable(), |
| 5000, clock); |
| |
| DestroyChannels(); |
| } |
| |
| // A collection of tests which tests a single P2PTransportChannel by sending |
| // pings. |
| class P2PTransportChannelPingTest : public testing::Test, |
| public sigslot::has_slots<> { |
| public: |
| P2PTransportChannelPingTest() |
| : pss_(new rtc::PhysicalSocketServer), |
| vss_(new rtc::VirtualSocketServer(pss_.get())), |
| ss_scope_(vss_.get()) {} |
| |
| protected: |
| void PrepareChannel(P2PTransportChannel* ch) { |
| ch->SetIceRole(ICEROLE_CONTROLLING); |
| ch->SetIceParameters(kIceParams[0]); |
| ch->SetRemoteIceParameters(kIceParams[1]); |
| ch->SignalSelectedCandidatePairChanged.connect( |
| this, &P2PTransportChannelPingTest::OnSelectedCandidatePairChanged); |
| ch->SignalReadyToSend.connect(this, |
| &P2PTransportChannelPingTest::OnReadyToSend); |
| ch->SignalStateChanged.connect( |
| this, &P2PTransportChannelPingTest::OnChannelStateChanged); |
| } |
| |
| Connection* WaitForConnectionTo(P2PTransportChannel* ch, |
| const std::string& ip, |
| int port_num, |
| rtc::FakeClock* clock = nullptr) { |
| if (clock == nullptr) { |
| EXPECT_TRUE_WAIT(GetConnectionTo(ch, ip, port_num) != nullptr, 3000); |
| } else { |
| EXPECT_TRUE_SIMULATED_WAIT(GetConnectionTo(ch, ip, port_num) != nullptr, |
| 3000, *clock); |
| } |
| return GetConnectionTo(ch, ip, port_num); |
| } |
| |
| Port* GetPort(P2PTransportChannel* ch) { |
| if (ch->ports().empty()) { |
| return nullptr; |
| } |
| return static_cast<Port*>(ch->ports()[0]); |
| } |
| |
| Port* GetPrunedPort(P2PTransportChannel* ch) { |
| if (ch->pruned_ports().empty()) { |
| return nullptr; |
| } |
| return static_cast<Port*>(ch->pruned_ports()[0]); |
| } |
| |
| Connection* GetConnectionTo(P2PTransportChannel* ch, |
| const std::string& ip, |
| int port_num) { |
| Port* port = GetPort(ch); |
| if (!port) { |
| return nullptr; |
| } |
| return port->GetConnection(rtc::SocketAddress(ip, port_num)); |
| } |
| |
| Connection* FindNextPingableConnectionAndPingIt(P2PTransportChannel* ch) { |
| Connection* conn = ch->FindNextPingableConnection(); |
| if (conn) { |
| ch->MarkConnectionPinged(conn); |
| } |
| return conn; |
| } |
| |
| int SendData(TransportChannel& channel, |
| const char* data, |
| size_t len, |
| int packet_id) { |
| rtc::PacketOptions options; |
| options.packet_id = packet_id; |
| return channel.SendPacket(data, len, options, 0); |
| } |
| |
| Connection* CreateConnectionWithCandidate(P2PTransportChannel& channel, |
| rtc::ScopedFakeClock& clock, |
| const std::string& ip_addr, |
| int port, |
| int priority, |
| bool writable) { |
| channel.AddRemoteCandidate( |
| CreateUdpCandidate(LOCAL_PORT_TYPE, ip_addr, port, priority)); |
| EXPECT_TRUE_SIMULATED_WAIT( |
| GetConnectionTo(&channel, ip_addr, port) != nullptr, 3000, clock); |
| Connection* conn = GetConnectionTo(&channel, ip_addr, port); |
| |
| if (conn && writable) { |
| conn->ReceivedPingResponse(LOW_RTT, "id"); // make it writable |
| } |
| return conn; |
| } |
| |
| void NominateConnection(Connection* conn, uint32_t remote_nomination = 1U) { |
| conn->set_remote_nomination(remote_nomination); |
| conn->SignalNominated(conn); |
| } |
| |
| void OnSelectedCandidatePairChanged( |
| TransportChannel* transport_channel, |
| CandidatePairInterface* selected_candidate_pair, |
| int last_sent_packet_id, |
| bool ready_to_send) { |
| last_selected_candidate_pair_ = selected_candidate_pair; |
| last_sent_packet_id_ = last_sent_packet_id; |
| ++selected_candidate_pair_switches_; |
| } |
| |
| void ReceivePingOnConnection(Connection* conn, |
| const std::string& remote_ufrag, |
| int priority, |
| uint32_t nomination = 0) { |
| IceMessage msg; |
| msg.SetType(STUN_BINDING_REQUEST); |
| msg.AddAttribute(new StunByteStringAttribute( |
| STUN_ATTR_USERNAME, |
| conn->local_candidate().username() + ":" + remote_ufrag)); |
| msg.AddAttribute(new StunUInt32Attribute(STUN_ATTR_PRIORITY, priority)); |
| if (nomination != 0) { |
| msg.AddAttribute( |
| new StunUInt32Attribute(STUN_ATTR_NOMINATION, nomination)); |
| } |
| msg.SetTransactionID(rtc::CreateRandomString(kStunTransactionIdLength)); |
| msg.AddMessageIntegrity(conn->local_candidate().password()); |
| msg.AddFingerprint(); |
| rtc::ByteBufferWriter buf; |
| msg.Write(&buf); |
| conn->OnReadPacket(buf.Data(), buf.Length(), rtc::CreatePacketTime(0)); |
| } |
| |
| void OnReadyToSend(TransportChannel* channel) { |
| channel_ready_to_send_ = true; |
| } |
| void OnChannelStateChanged(TransportChannelImpl* channel) { |
| channel_state_ = channel->GetState(); |
| } |
| |
| CandidatePairInterface* last_selected_candidate_pair() { |
| return last_selected_candidate_pair_; |
| } |
| int last_sent_packet_id() { return last_sent_packet_id_; } |
| bool channel_ready_to_send() { return channel_ready_to_send_; } |
| void reset_channel_ready_to_send() { channel_ready_to_send_ = false; } |
| TransportChannelState channel_state() { return channel_state_; } |
| int reset_selected_candidate_pair_switches() { |
| int switches = selected_candidate_pair_switches_; |
| selected_candidate_pair_switches_ = 0; |
| return switches; |
| } |
| |
| private: |
| std::unique_ptr<rtc::PhysicalSocketServer> pss_; |
| std::unique_ptr<rtc::VirtualSocketServer> vss_; |
| rtc::SocketServerScope ss_scope_; |
| CandidatePairInterface* last_selected_candidate_pair_ = nullptr; |
| int selected_candidate_pair_switches_ = 0; |
| int last_sent_packet_id_ = -1; |
| bool channel_ready_to_send_ = false; |
| TransportChannelState channel_state_ = STATE_INIT;<
|