If continual gathering is enabled,

we will periodically check if any network does not have any connection on it and if yes, attempt to re-gather on those networks.

BUG=
R=pthatcher@webrtc.org

Review URL: https://codereview.webrtc.org/2025573002 .

Cr-Commit-Position: refs/heads/master@{#13367}
This commit is contained in:
Honghai Zhang 2016-07-01 13:59:29 -07:00
parent e9851116e2
commit 5622c5eae5
17 changed files with 594 additions and 216 deletions

View File

@ -1128,14 +1128,27 @@ bool WebRtcSession::RemoveRemoteIceCandidates(
cricket::IceConfig WebRtcSession::ParseIceConfig(
const PeerConnectionInterface::RTCConfiguration& config) const {
cricket::ContinualGatheringPolicy gathering_policy;
// TODO(honghaiz): Add the third continual gathering policy in
// PeerConnectionInterface and map it to GATHER_CONTINUALLY_AND_RECOVER.
switch (config.continual_gathering_policy) {
case PeerConnectionInterface::GATHER_ONCE:
gathering_policy = cricket::GATHER_ONCE;
break;
case PeerConnectionInterface::GATHER_CONTINUALLY:
gathering_policy = cricket::GATHER_CONTINUALLY;
break;
default:
RTC_DCHECK(false);
gathering_policy = cricket::GATHER_ONCE;
}
cricket::IceConfig ice_config;
ice_config.receiving_timeout = config.ice_connection_receiving_timeout;
ice_config.prioritize_most_likely_candidate_pairs =
config.prioritize_most_likely_ice_candidate_pairs;
ice_config.backup_connection_ping_interval =
config.ice_backup_candidate_pair_ping_interval;
ice_config.gather_continually = (config.continual_gathering_policy ==
PeerConnectionInterface::GATHER_CONTINUALLY);
ice_config.continual_gathering_policy = gathering_policy;
ice_config.presume_writable_when_fully_relayed =
config.presume_writable_when_fully_relayed;
return ice_config;

View File

@ -2242,7 +2242,9 @@ TEST_F(WebRtcSessionTest,
candidates = local_desc->candidates(kMediaContentIndex0);
size_t num_local_candidates = candidates->count();
// Enable Continual Gathering
session_->SetIceConfig(cricket::IceConfig(-1, -1, true, false, -1, true));
cricket::IceConfig config;
config.continual_gathering_policy = cricket::GATHER_CONTINUALLY;
session_->SetIceConfig(config);
// Bring down the network interface to trigger candidate removals.
RemoveInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
// Verify that all local candidates are removed.

View File

@ -140,7 +140,7 @@ class NetworkManagerBase : public NetworkManager {
NetworkManagerBase();
~NetworkManagerBase() override;
void GetNetworks(std::vector<Network*>* networks) const override;
void GetNetworks(NetworkList* networks) const override;
void GetAnyAddressNetworks(NetworkList* networks) override;
bool ipv6_enabled() const { return ipv6_enabled_; }
void set_ipv6_enabled(bool enabled) { ipv6_enabled_ = enabled; }
@ -290,7 +290,6 @@ class Network {
AdapterType type);
~Network();
sigslot::signal1<const Network*> SignalInactive;
sigslot::signal1<const Network*> SignalTypeChanged;
const DefaultLocalAddressProvider* default_local_address_provider() {
@ -398,12 +397,8 @@ class Network {
// it inactive, so that we can detect network changes properly.
bool active() const { return active_; }
void set_active(bool active) {
if (active_ == active) {
return;
}
active_ = active;
if (!active) {
SignalInactive(this);
if (active_ != active) {
active_ = active;
}
}

View File

@ -67,16 +67,6 @@ class NetworkTest : public testing::Test, public sigslot::has_slots<> {
callback_called_ = true;
}
void listenToNetworkInactive(BasicNetworkManager& network_manager) {
BasicNetworkManager::NetworkList networks;
network_manager.GetNetworks(&networks);
for (Network* network : networks) {
network->SignalInactive.connect(this, &NetworkTest::OnNetworkInactive);
}
}
void OnNetworkInactive(const Network* network) { num_networks_inactive_++; }
NetworkManager::Stats MergeNetworkList(
BasicNetworkManager& network_manager,
const NetworkManager::NetworkList& list,
@ -187,8 +177,6 @@ class NetworkTest : public testing::Test, public sigslot::has_slots<> {
protected:
bool callback_called_;
// Number of networks that become inactive.
int num_networks_inactive_ = 0;
};
class TestBasicNetworkManager : public BasicNetworkManager {
@ -322,7 +310,6 @@ TEST_F(NetworkTest, TestBasicMergeNetworkList) {
EXPECT_TRUE(changed);
EXPECT_EQ(stats.ipv6_network_count, 0);
EXPECT_EQ(stats.ipv4_network_count, 1);
listenToNetworkInactive(manager);
list.clear();
manager.GetNetworks(&list);
@ -339,9 +326,7 @@ TEST_F(NetworkTest, TestBasicMergeNetworkList) {
EXPECT_TRUE(changed);
EXPECT_EQ(stats.ipv6_network_count, 0);
EXPECT_EQ(stats.ipv4_network_count, 1);
EXPECT_EQ(1, num_networks_inactive_);
list.clear();
num_networks_inactive_ = 0;
manager.GetNetworks(&list);
EXPECT_EQ(1U, list.size());
@ -359,7 +344,6 @@ TEST_F(NetworkTest, TestBasicMergeNetworkList) {
EXPECT_TRUE(changed);
EXPECT_EQ(stats.ipv6_network_count, 0);
EXPECT_EQ(stats.ipv4_network_count, 2);
EXPECT_EQ(0, num_networks_inactive_);
list.clear();
// Verify that we get previous instances of Network objects.
@ -379,7 +363,6 @@ TEST_F(NetworkTest, TestBasicMergeNetworkList) {
EXPECT_FALSE(changed);
EXPECT_EQ(stats.ipv6_network_count, 0);
EXPECT_EQ(stats.ipv4_network_count, 2);
EXPECT_EQ(0, num_networks_inactive_);
list.clear();
// Verify that we get previous instances of Network objects.

View File

@ -111,7 +111,6 @@ class FakePortAllocatorSession : public PortAllocatorSession {
rtc::IPAddress(in6addr_loopback),
64),
port_(),
running_(false),
port_config_count_(0),
stun_servers_(allocator->stun_servers()),
turn_servers_(allocator->turn_servers()) {
@ -124,6 +123,7 @@ class FakePortAllocatorSession : public PortAllocatorSession {
}
void StartGettingPorts() override {
PortAllocatorSession::StartGettingPorts();
if (!port_) {
rtc::Network& network =
(rtc::HasIPv6Enabled() && (flags() & PORTALLOCATOR_ENABLE_IPV6))
@ -137,12 +137,8 @@ class FakePortAllocatorSession : public PortAllocatorSession {
AddPort(port_.get());
}
++port_config_count_;
running_ = true;
}
void StopGettingPorts() override { running_ = false; }
bool IsGettingPorts() override { return running_; }
void ClearGettingPorts() override {}
std::vector<PortInterface*> ReadyPorts() const override {
return ready_ports_;
}
@ -200,7 +196,6 @@ class FakePortAllocatorSession : public PortAllocatorSession {
rtc::Network ipv4_network_;
rtc::Network ipv6_network_;
std::unique_ptr<cricket::Port> port_;
bool running_;
int port_config_count_;
std::vector<Candidate> candidates_;
std::vector<PortInterface*> ready_ports_;

View File

@ -179,13 +179,10 @@ class FakeTransportChannel : public TransportChannelImpl,
void SetReceiving(bool receiving) { set_receiving(receiving); }
void SetIceConfig(const IceConfig& config) override {
receiving_timeout_ = config.receiving_timeout;
gather_continually_ = config.gather_continually;
}
void SetIceConfig(const IceConfig& config) override { ice_config_ = config; }
int receiving_timeout() const { return receiving_timeout_; }
bool gather_continually() const { return gather_continually_; }
int receiving_timeout() const { return ice_config_.receiving_timeout; }
bool gather_continually() const { return ice_config_.gather_continually(); }
int SendPacket(const char* data,
size_t len,
@ -318,8 +315,7 @@ class FakeTransportChannel : public TransportChannelImpl,
bool do_dtls_ = false;
std::vector<int> srtp_ciphers_;
int chosen_crypto_suite_ = rtc::SRTP_INVALID_CRYPTO_SUITE;
int receiving_timeout_ = -1;
bool gather_continually_ = false;
IceConfig ice_config_;
IceRole role_ = ICEROLE_UNKNOWN;
uint64_t tiebreaker_ = 0;
std::string ice_ufrag_;

View File

@ -27,10 +27,14 @@
namespace {
// messages for queuing up work for ourselves
enum { MSG_SORT_AND_UPDATE_STATE = 1, MSG_CHECK_AND_PING };
enum {
MSG_SORT_AND_UPDATE_STATE = 1,
MSG_CHECK_AND_PING,
MSG_REGATHER_ON_FAILED_NETWORKS
};
// The minimum improvement in RTT that justifies a switch.
static const double kMinImprovement = 10;
const int kMinImprovement = 10;
bool IsRelayRelay(const cricket::Connection* conn) {
return conn->local_candidate().type() == cricket::RELAY_PORT_TYPE &&
@ -76,6 +80,9 @@ const int STABLE_WRITABLE_CONNECTION_PING_INTERVAL = 2500; // ms
static const int MIN_CHECK_RECEIVING_INTERVAL = 50; // ms
// We periodically check if any existing networks do not have any connection
// and regather on those networks.
static const int DEFAULT_REGATHER_ON_FAILED_NETWORKS_INTERVAL = 5 * 60 * 1000;
static constexpr int a_is_better = 1;
static constexpr int b_is_better = -1;
@ -101,10 +108,11 @@ P2PTransportChannel::P2PTransportChannel(const std::string& transport_name,
check_receiving_interval_(MIN_CHECK_RECEIVING_INTERVAL * 5),
config_(MIN_CHECK_RECEIVING_INTERVAL * 50 /* receiving_timeout */,
0 /* backup_connection_ping_interval */,
false /* gather_continually */,
GATHER_ONCE /* continual_gathering_policy */,
false /* prioritize_most_likely_candidate_pairs */,
STABLE_WRITABLE_CONNECTION_PING_INTERVAL,
true /* presume_writable_when_fully_relayed */) {
true /* presume_writable_when_fully_relayed */,
DEFAULT_REGATHER_ON_FAILED_NETWORKS_INTERVAL) {
uint32_t weak_ping_interval = ::strtoul(
webrtc::field_trial::FindFullName("WebRTC-StunInterPacketDelay").c_str(),
nullptr, 10);
@ -125,9 +133,13 @@ void P2PTransportChannel::AddAllocatorSession(
session->set_generation(static_cast<uint32_t>(allocator_sessions_.size()));
session->SignalPortReady.connect(this, &P2PTransportChannel::OnPortReady);
session->SignalPortsRemoved.connect(this,
&P2PTransportChannel::OnPortsRemoved);
session->SignalPortPruned.connect(this, &P2PTransportChannel::OnPortPruned);
session->SignalCandidatesReady.connect(
this, &P2PTransportChannel::OnCandidatesReady);
session->SignalCandidatesRemoved.connect(
this, &P2PTransportChannel::OnCandidatesRemoved);
session->SignalCandidatesAllocationDone.connect(
this, &P2PTransportChannel::OnCandidatesAllocationDone);
@ -296,8 +308,11 @@ void P2PTransportChannel::SetRemoteIceMode(IceMode mode) {
}
void P2PTransportChannel::SetIceConfig(const IceConfig& config) {
config_.gather_continually = config.gather_continually;
LOG(LS_INFO) << "Set gather_continually to " << config_.gather_continually;
if (config_.continual_gathering_policy != config.continual_gathering_policy) {
LOG(LS_INFO) << "Set continual_gathering_policy to "
<< config_.continual_gathering_policy;
config_.continual_gathering_policy = config.continual_gathering_policy;
}
if (config.backup_connection_ping_interval >= 0 &&
config_.backup_connection_ping_interval !=
@ -347,6 +362,13 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) {
<< config_.presume_writable_when_fully_relayed;
}
}
if (config.regather_on_failed_networks_interval) {
config_.regather_on_failed_networks_interval =
config.regather_on_failed_networks_interval;
LOG(LS_INFO) << "Set regather_on_failed_networks_interval to "
<< *config_.regather_on_failed_networks_interval;
}
}
const IceConfig& P2PTransportChannel::config() const {
@ -418,8 +440,7 @@ void P2PTransportChannel::OnPortReady(PortAllocatorSession *session,
port->SignalUnknownAddress.connect(
this, &P2PTransportChannel::OnUnknownAddress);
port->SignalDestroyed.connect(this, &P2PTransportChannel::OnPortDestroyed);
port->SignalNetworkInactive.connect(
this, &P2PTransportChannel::OnPortNetworkInactive);
port->SignalRoleConflict.connect(
this, &P2PTransportChannel::OnRoleConflict);
port->SignalSentPacket.connect(this, &P2PTransportChannel::OnSentPacket);
@ -957,6 +978,9 @@ void P2PTransportChannel::MaybeStartPinging() {
LOG_J(LS_INFO, this) << "Have a pingable connection for the first time; "
<< "starting to ping.";
thread()->Post(RTC_FROM_HERE, this, MSG_CHECK_AND_PING);
thread()->PostDelayed(RTC_FROM_HERE,
*config_.regather_on_failed_networks_interval, this,
MSG_REGATHER_ON_FAILED_NETWORKS);
started_pinging_ = true;
}
}
@ -1313,16 +1337,16 @@ void P2PTransportChannel::MaybeStopPortAllocatorSessions() {
}
for (const auto& session : allocator_sessions_) {
if (!session->IsGettingPorts()) {
if (session->IsStopped()) {
continue;
}
// If gathering continually, keep the last session running so that it
// will gather candidates if the networks change.
if (config_.gather_continually && session == allocator_sessions_.back()) {
// If gathering continually, keep the last session running so that
// it can gather candidates if the networks change.
if (config_.gather_continually() && session == allocator_sessions_.back()) {
session->ClearGettingPorts();
break;
} else {
session->StopGettingPorts();
}
session->StopGettingPorts();
}
}
@ -1377,6 +1401,9 @@ void P2PTransportChannel::OnMessage(rtc::Message *pmsg) {
case MSG_CHECK_AND_PING:
OnCheckAndPing();
break;
case MSG_REGATHER_ON_FAILED_NETWORKS:
OnRegatherOnFailedNetworks();
break;
default:
ASSERT(false);
break;
@ -1606,37 +1633,62 @@ void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) {
}
}
// When a port is destroyed remove it from our list of ports to use for
// When a port is destroyed, remove it from our list of ports to use for
// connection attempts.
void P2PTransportChannel::OnPortDestroyed(PortInterface* port) {
ASSERT(worker_thread_ == rtc::Thread::Current());
// Remove this port from the lists (if we didn't drop it already).
ports_.erase(std::remove(ports_.begin(), ports_.end(), port), ports_.end());
removed_ports_.erase(
std::remove(removed_ports_.begin(), removed_ports_.end(), port),
removed_ports_.end());
LOG(INFO) << "Removed port because it is destroyed: "
<< static_cast<int>(ports_.size()) << " remaining";
LOG(INFO) << "Removed port because it is destroyed: " << ports_.size()
<< " remaining";
}
void P2PTransportChannel::OnPortNetworkInactive(PortInterface* port) {
// If it does not gather continually, the port will be removed from the list
// when ICE restarts.
if (!config_.gather_continually) {
void P2PTransportChannel::OnPortsRemoved(
PortAllocatorSession* session,
const std::vector<PortInterface*>& ports) {
ASSERT(worker_thread_ == rtc::Thread::Current());
LOG(LS_INFO) << "Remove " << ports.size() << " ports";
for (PortInterface* port : ports) {
if (RemovePort(port)) {
LOG(INFO) << "Removed port: " << port->ToString() << " " << ports_.size()
<< " remaining";
}
}
}
void P2PTransportChannel::OnCandidatesRemoved(
PortAllocatorSession* session,
const std::vector<Candidate>& candidates) {
ASSERT(worker_thread_ == rtc::Thread::Current());
// Do not signal candidate removals if continual gathering is not enabled, or
// if this is not the last session because an ICE restart would have signaled
// the remote side to remove all candidates in previous sessions.
if (!config_.gather_continually() || session != allocator_session()) {
return;
}
if (!RemovePort(port)) {
return;
}
LOG(INFO) << "Removed port because its network is inactive : "
<< port->ToString() << " " << ports_.size() << " remaining";
std::vector<Candidate> candidates = port->Candidates();
for (Candidate& candidate : candidates) {
std::vector<Candidate> candidates_to_remove;
for (Candidate candidate : candidates) {
candidate.set_transport_name(transport_name());
candidates_to_remove.push_back(candidate);
}
SignalCandidatesRemoved(this, candidates);
SignalCandidatesRemoved(this, candidates_to_remove);
}
void P2PTransportChannel::OnRegatherOnFailedNetworks() {
// Only re-gather when the current session is in the CLEARED state (i.e., not
// running or stopped). It is only possible to enter this state when we gather
// continually, so there is an implicit check on continual gathering here.
if (!allocator_sessions_.empty() && allocator_session()->IsCleared()) {
allocator_session()->RegatherOnFailedNetworks();
}
thread()->PostDelayed(RTC_FROM_HERE,
*config_.regather_on_failed_networks_interval, this,
MSG_REGATHER_ON_FAILED_NETWORKS);
}
void P2PTransportChannel::OnPortPruned(PortAllocatorSession* session,

View File

@ -268,11 +268,15 @@ class P2PTransportChannel : public TransportChannelImpl,
void AddConnection(Connection* connection);
void OnPortReady(PortAllocatorSession *session, PortInterface* port);
// TODO(honghaiz): Merge the two methods OnPortsRemoved and OnPortPruned but
// still log the reason of removing.
void OnPortsRemoved(PortAllocatorSession* session,
const std::vector<PortInterface*>& ports);
void OnPortPruned(PortAllocatorSession* session, PortInterface* port);
// Returns true if the port is found and removed from |ports_|.
bool RemovePort(PortInterface* port);
void OnCandidatesReady(PortAllocatorSession *session,
const std::vector<Candidate>& candidates);
void OnCandidatesRemoved(PortAllocatorSession* session,
const std::vector<Candidate>& candidates);
void OnCandidatesAllocationDone(PortAllocatorSession* session);
void OnUnknownAddress(PortInterface* port,
const rtc::SocketAddress& addr,
@ -280,8 +284,13 @@ class P2PTransportChannel : public TransportChannelImpl,
IceMessage* stun_msg,
const std::string& remote_username,
bool port_muxed);
// When a port is destroyed, remove it from both lists |ports_|
// and |removed_ports_|.
void OnPortDestroyed(PortInterface* port);
void OnPortNetworkInactive(PortInterface* port);
// When removing a port, move it from |ports_| to |removed_ports_|.
// Returns true if the port is found and removed from |ports_|.
bool RemovePort(PortInterface* port);
void OnRoleConflict(PortInterface* port);
void OnConnectionStateChange(Connection* connection);
@ -295,6 +304,7 @@ class P2PTransportChannel : public TransportChannelImpl,
void OnMessage(rtc::Message* pmsg) override;
void OnCheckAndPing();
void OnRegatherOnFailedNetworks();
// Returns true if the new_connection should be selected for transmission.
bool ShouldSwitchSelectedConnection(Connection* new_connection) const;

View File

@ -105,12 +105,13 @@ static const uint64_t kHighTiebreaker = 22222;
enum { MSG_ADD_CANDIDATES, MSG_REMOVE_CANDIDATES };
cricket::IceConfig CreateIceConfig(int receiving_timeout,
bool gather_continually,
int backup_ping_interval = -1) {
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.gather_continually = gather_continually;
config.continual_gathering_policy = continual_gathering_policy;
config.backup_connection_ping_interval = backup_ping_interval;
return config;
}
@ -412,6 +413,7 @@ class P2PTransportChannelTestBase : public testing::Test,
}
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;
@ -1543,7 +1545,7 @@ TEST_F(P2PTransportChannelTest, TestContinualGathering) {
SetAllocationStepDelay(0, kDefaultStepDelay);
SetAllocationStepDelay(1, kDefaultStepDelay);
CreateChannels(1);
IceConfig config = CreateIceConfig(1000, true);
IceConfig config = CreateIceConfig(1000, GATHER_CONTINUALLY);
ep1_ch1()->SetIceConfig(config);
// By default, ep2 does not gather continually.
@ -1820,6 +1822,37 @@ TEST_F(P2PTransportChannelSameNatTest, TestConesBehindSameCone) {
// 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;
}
const 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* best_connection = channel->best_connection();
for (cricket::Connection* conn : channel->connections()) {
if (conn != best_connection) {
conn->Destroy();
}
}
}
};
// Test that we can establish connectivity when both peers are multihomed.
@ -1856,7 +1889,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControlledSide) {
RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
// Make the receiving timeout shorter for testing.
IceConfig config = CreateIceConfig(1000, false);
IceConfig config = CreateIceConfig(1000, GATHER_ONCE);
ep1_ch1()->SetIceConfig(config);
ep2_ch1()->SetIceConfig(config);
@ -1909,7 +1942,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControllingSide) {
RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
// Make the receiving timeout shorter for testing.
IceConfig config = CreateIceConfig(1000, false);
IceConfig config = CreateIceConfig(1000, GATHER_ONCE);
ep1_ch1()->SetIceConfig(config);
ep2_ch1()->SetIceConfig(config);
@ -1968,6 +2001,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestPreferWifiToWifiConnection) {
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
@ -1998,6 +2032,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestPreferWifiOverCellularNetwork) {
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
@ -2019,7 +2054,8 @@ TEST_F(P2PTransportChannelMultihomedTest, TestPingBackupConnectionRate) {
ep2_ch1()->receiving() && ep2_ch1()->writable(),
1000, 1000);
int backup_ping_interval = 2000;
ep2_ch1()->SetIceConfig(CreateIceConfig(2000, false, backup_ping_interval));
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);
@ -2034,6 +2070,8 @@ TEST_F(P2PTransportChannelMultihomedTest, TestPingBackupConnectionRate) {
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) {
@ -2050,23 +2088,25 @@ TEST_F(P2PTransportChannelMultihomedTest, TestGetState) {
1000);
}
// Tests that when a network interface becomes inactive, if and only if
// Continual Gathering is enabled, the ports associated with that network
// 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.
CreateChannels(1);
ep1_ch1()->SetIceConfig(CreateIceConfig(2000, true));
ep2_ch1()->SetIceConfig(CreateIceConfig(2000, false));
ep1_ch1()->SetIceConfig(CreateIceConfig(2000, GATHER_CONTINUALLY));
ep2_ch1()->SetIceConfig(CreateIceConfig(2000, GATHER_ONCE));
SetAllocatorFlags(0, kOnlyLocalPorts);
SetAllocatorFlags(1, kOnlyLocalPorts);
EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() && ep2_ch1()->writable(),
1000, 1000);
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
@ -2074,29 +2114,76 @@ TEST_F(P2PTransportChannelMultihomedTest, TestNetworkBecomesInactive) {
RemoveAddress(0, kPublicAddrs[0]);
EXPECT_TRUE(ep1_ch1()->ports().empty());
// The remote candidates will be removed eventually.
EXPECT_TRUE_WAIT(ep2_ch1()->remote_candidates().empty(), 1000);
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 port will not be removed
// when the interface is removed and neither the remote candidates on the
// other participant.
// 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]);
rtc::Thread::Current()->ProcessMessages(500);
EXPECT_EQ(num_ports, ep2_ch1()->ports().size());
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);
CreateChannels(1);
// Set continual gathering policy.
ep1_ch1()->SetIceConfig(CreateIceConfig(1000, GATHER_CONTINUALLY));
ep2_ch1()->SetIceConfig(CreateIceConfig(1000, GATHER_CONTINUALLY));
SetAllocatorFlags(0, kOnlyLocalPorts);
SetAllocatorFlags(1, kOnlyLocalPorts);
EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() && ep2_ch1()->writable(),
kDefaultTimeout, kDefaultTimeout);
TODO(pthatcher): Once have a way to handle network interfaces changes
without signalling an ICE restart, put a test like this back. In the
mean time, this test only worked for GICE. With ICE, it's currently
not possible without an ICE restart.
// 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()->best_connection()) != nullptr &&
conn->remote_candidate().address().EqualIPs(wifi[1]),
kDefaultTimeout);
EXPECT_TRUE_WAIT((conn = ep2_ch1()->best_connection()) != nullptr &&
conn->local_candidate().address().EqualIPs(wifi[1]),
kDefaultTimeout);
// Test that we can switch links in a coordinated fashion.
TEST_F(P2PTransportChannelMultihomedTest, TestDrain) {
// 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()->best_connection() && conn->writable(),
kDefaultTimeout);
EXPECT_TRUE_WAIT(
ep2_ch1()->GetState() == cricket::STATE_COMPLETED &&
(conn = GetConnectionWithRemoteAddress(ep2_ch1(), cellular[0])) !=
nullptr &&
conn != ep2_ch1()->best_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.
@ -2105,36 +2192,134 @@ TEST_F(P2PTransportChannelMultihomedTest, TestDrain) {
// Create channels and let them go writable, as usual.
CreateChannels(1);
EXPECT_TRUE_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() && ep2_ch1()->writable(),
1000);
// Set continual gathering policy.
ep1_ch1()->SetIceConfig(CreateIceConfig(1000, GATHER_CONTINUALLY));
ep2_ch1()->SetIceConfig(CreateIceConfig(1000, GATHER_CONTINUALLY));
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]));
// Remove the public interface, add the alternate interface, and allocate
// a new generation of candidates for the new interface (via
// MaybeStartGathering()).
// Add the new address first and then remove the other one.
LOG(LS_INFO) << "Draining...";
AddAddress(1, kAlternateAddrs[1]);
RemoveAddress(1, kPublicAddrs[1]);
ep2_ch1()->MaybeStartGathering();
// We should switch over to use the alternate address after
// an exchange of pings.
EXPECT_TRUE_WAIT(
// 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);
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()->best_connection() && ep2_ch1()->best_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);
// Create channels and let them go writable, as usual.
CreateChannels(1);
// Set continual gathering policy.
IceConfig config = CreateIceConfig(1000, GATHER_CONTINUALLY_AND_RECOVER);
ep1_ch1()->SetIceConfig(config);
ep2_ch1()->SetIceConfig(config);
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() && ep2_ch1()->writable(),
3000, clock);
EXPECT_TRUE(ep1_ch1()->best_connection() && ep2_ch1()->best_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()->best_connection() && ep2_ch1()->best_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.
CreateChannels(1);
IceConfig config = CreateIceConfig(1000, GATHER_CONTINUALLY);
config.regather_on_failed_networks_interval = rtc::Optional<int>(2000);
ep1_ch1()->SetIceConfig(config);
ep2_ch1()->SetIceConfig(config);
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
ep2_ch1()->receiving() &&
ep2_ch1()->writable(),
3000, clock);
EXPECT_TRUE(ep1_ch1()->best_connection() && ep2_ch1()->best_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()->best_connection() && conn->writable(),
5000, clock);
DestroyChannels();
}
// A collection of tests which tests a single P2PTransportChannel by sending
// pings.
class P2PTransportChannelPingTest : public testing::Test,
@ -2624,7 +2809,7 @@ TEST_F(P2PTransportChannelPingTest, TestReceivingStateChange) {
// small.
EXPECT_LE(1000, ch.receiving_timeout());
EXPECT_LE(200, ch.check_receiving_interval());
ch.SetIceConfig(CreateIceConfig(500, false));
ch.SetIceConfig(CreateIceConfig(500, GATHER_ONCE));
EXPECT_EQ(500, ch.receiving_timeout());
EXPECT_EQ(50, ch.check_receiving_interval());
ch.MaybeStartGathering();
@ -3038,7 +3223,7 @@ TEST_F(P2PTransportChannelPingTest, TestDontPruneWhenWeak) {
conn2->SignalNominated(conn2);
EXPECT_TRUE_WAIT(conn1->pruned(), 3000);
ch.SetIceConfig(CreateIceConfig(500, false));
ch.SetIceConfig(CreateIceConfig(500, GATHER_ONCE));
// Wait until conn2 becomes not receiving.
EXPECT_TRUE_WAIT(!conn2->receiving(), 3000);
@ -3104,7 +3289,7 @@ TEST_F(P2PTransportChannelPingTest, TestConnectionPrunedAgain) {
FakePortAllocator pa(rtc::Thread::Current(), nullptr);
P2PTransportChannel ch("test channel", 1, &pa);
PrepareChannel(&ch);
ch.SetIceConfig(CreateIceConfig(1000, false));
ch.SetIceConfig(CreateIceConfig(1000, GATHER_ONCE));
ch.MaybeStartGathering();
ch.AddRemoteCandidate(CreateUdpCandidate(LOCAL_PORT_TYPE, "1.1.1.1", 1, 100));
Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
@ -3178,7 +3363,7 @@ TEST_F(P2PTransportChannelPingTest, TestStopPortAllocatorSessions) {
FakePortAllocator pa(rtc::Thread::Current(), nullptr);
P2PTransportChannel ch("test channel", 1, &pa);
PrepareChannel(&ch);
ch.SetIceConfig(CreateIceConfig(2000, false));
ch.SetIceConfig(CreateIceConfig(2000, GATHER_ONCE));
ch.MaybeStartGathering();
ch.AddRemoteCandidate(CreateUdpCandidate(LOCAL_PORT_TYPE, "1.1.1.1", 1, 100));
Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
@ -3204,17 +3389,15 @@ TEST_F(P2PTransportChannelPingTest, TestStopPortAllocatorSessions) {
EXPECT_TRUE(!ch.allocator_session()->IsGettingPorts());
}
// Test that the ICE role is updated even on ports with inactive networks when
// doing continual gathering. These ports may still have connections that need
// a correct role, in case the network becomes active before the connection is
// destroyed.
TEST_F(P2PTransportChannelPingTest,
TestIceRoleUpdatedOnPortAfterSignalNetworkInactive) {
// Test that the ICE role is updated even on ports that has been removed.
// These ports may still have connections that need a correct role, in case that
// the connections on it may still receive stun pings.
TEST_F(P2PTransportChannelPingTest, TestIceRoleUpdatedOnRemovedPort) {
FakePortAllocator pa(rtc::Thread::Current(), nullptr);
P2PTransportChannel ch("test channel", ICE_CANDIDATE_COMPONENT_DEFAULT, &pa);
// Starts with ICEROLE_CONTROLLING.
PrepareChannel(&ch);
IceConfig config = CreateIceConfig(1000, true);
IceConfig config = CreateIceConfig(1000, GATHER_CONTINUALLY);
ch.SetIceConfig(config);
ch.MaybeStartGathering();
ch.AddRemoteCandidate(CreateUdpCandidate(LOCAL_PORT_TYPE, "1.1.1.1", 1, 1));
@ -3222,9 +3405,10 @@ TEST_F(P2PTransportChannelPingTest,
Connection* conn = WaitForConnectionTo(&ch, "1.1.1.1", 1);
ASSERT_TRUE(conn != nullptr);
// Make the fake port signal that its network is inactive, then change the
// ICE role and expect it to be updated.
conn->port()->SignalNetworkInactive(conn->port());
// Make a fake signal to remove the ports in the p2ptransportchannel. then
// change the ICE role and expect it to be updated.
std::vector<PortInterface*> ports(1, conn->port());
ch.allocator_session()->SignalPortsRemoved(ch.allocator_session(), ports);
ch.SetIceRole(ICEROLE_CONTROLLED);
EXPECT_EQ(ICEROLE_CONTROLLED, conn->port()->GetIceRole());
}

View File

@ -195,7 +195,6 @@ void Port::Construct() {
ice_username_fragment_ = rtc::CreateRandomString(ICE_UFRAG_LENGTH);
password_ = rtc::CreateRandomString(ICE_PWD_LENGTH);
}
network_->SignalInactive.connect(this, &Port::OnNetworkInactive);
network_->SignalTypeChanged.connect(this, &Port::OnNetworkTypeChanged);
network_cost_ = network_->GetCost();
@ -652,11 +651,6 @@ void Port::OnMessage(rtc::Message *pmsg) {
}
}
void Port::OnNetworkInactive(const rtc::Network* network) {
ASSERT(network == network_);
SignalNetworkInactive(this);
}
void Port::OnNetworkTypeChanged(const rtc::Network* network) {
ASSERT(network == network_);

View File

@ -363,8 +363,6 @@ class Port : public PortInterface, public rtc::MessageHandler,
return ice_role_ == ICEROLE_CONTROLLED && connections_.empty();
}
void OnNetworkInactive(const rtc::Network* network);
void OnNetworkTypeChanged(const rtc::Network* network);
rtc::Thread* thread_;

View File

@ -88,6 +88,14 @@ enum {
CF_ALL = 0x7,
};
enum class SessionState {
GATHERING, // Actively allocating ports and gathering candidates.
CLEARED, // Current allocation process has been stopped but may start
// new ones.
STOPPED // This session has completely stopped, no new allocation
// process will be started.
};
// TODO(deadbeef): Rename to TurnCredentials (and username to ufrag).
struct RelayCredentials {
RelayCredentials() {}
@ -159,12 +167,27 @@ class PortAllocatorSession : public sigslot::has_slots<> {
virtual void SetCandidateFilter(uint32_t filter) = 0;
// Starts gathering STUN and Relay configurations.
virtual void StartGettingPorts() = 0;
virtual void StopGettingPorts() = 0;
// Only stop the existing gathering process but may start new ones if needed.
virtual void ClearGettingPorts() = 0;
// Whether the process of getting ports has been stopped.
virtual bool IsGettingPorts() = 0;
virtual void StartGettingPorts() { state_ = SessionState::GATHERING; }
// Completely stops the gathering process and will not start new ones.
virtual void StopGettingPorts() { state_ = SessionState::STOPPED; }
// Only stops the existing gathering process but may start new ones if needed.
virtual void ClearGettingPorts() { state_ = SessionState::CLEARED; }
// Whether the session is actively getting ports.
bool IsGettingPorts() { return state_ == SessionState::GATHERING; }
// Whether it is in the state where the existing gathering process is stopped,
// but new ones may be started (basically after calling ClearGettingPorts).
bool IsCleared() { return state_ == SessionState::CLEARED; }
// Whether the session has completely stopped.
bool IsStopped() { return state_ == SessionState::STOPPED; }
// Re-gathers candidates on networks that do not have any connections. More
// precisely, a network interface may have more than one IP addresses (e.g.,
// IPv4 and IPv6 addresses). Each address subnet will be used to create a
// network. Only if all networks of an interface have no connection, the
// implementation should start re-gathering on all networks of that interface.
virtual void RegatherOnFailedNetworks() {}
// Re-gathers candidates on all networks.
// TODO(honghaiz): Implement this in BasicPortAllocator.
virtual void RegatherOnAllNetworks() {}
// Another way of getting the information provided by the signals below.
//
@ -175,8 +198,17 @@ class PortAllocatorSession : public sigslot::has_slots<> {
virtual bool CandidatesAllocationDone() const = 0;
sigslot::signal2<PortAllocatorSession*, PortInterface*> SignalPortReady;
// Ports should be signaled to be removed when the networks of the ports
// failed (either because the interface is down, or because there is no
// connection on the interface).
sigslot::signal2<PortAllocatorSession*, const std::vector<PortInterface*>&>
SignalPortsRemoved;
sigslot::signal2<PortAllocatorSession*,
const std::vector<Candidate>&> SignalCandidatesReady;
// Candidates should be signaled to be removed when the port that generated
// the candidates is removed.
sigslot::signal2<PortAllocatorSession*, const std::vector<Candidate>&>
SignalCandidatesRemoved;
sigslot::signal1<PortAllocatorSession*> SignalCandidatesAllocationDone;
// A TURN port is pruned if a higher-priority TURN port becomes ready
// (pairable). When it is pruned, it will not be used for creating
@ -220,6 +252,7 @@ class PortAllocatorSession : public sigslot::has_slots<> {
int component_;
std::string ice_ufrag_;
std::string ice_pwd_;
SessionState state_ = SessionState::CLEARED;
// SetIceParameters is an implementation detail which only PortAllocator
// should be able to call.

View File

@ -106,9 +106,6 @@ class PortInterface {
// any usefulness.
sigslot::signal1<PortInterface*> SignalDestroyed;
// Signaled when the network used by this port becomes inactive.
sigslot::signal1<PortInterface*> SignalNetworkInactive;
// Signaled when Port discovers ice role conflict with the peer.
sigslot::signal1<PortInterface*> SignalRoleConflict;

View File

@ -31,6 +31,7 @@
#include <vector>
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/optional.h"
#include "webrtc/p2p/base/candidate.h"
#include "webrtc/p2p/base/p2pconstants.h"
#include "webrtc/p2p/base/sessiondescription.h"
@ -81,6 +82,16 @@ enum IceGatheringState {
kIceGatheringComplete,
};
enum ContinualGatheringPolicy {
// All port allocator sessions will stop after a writable connection is found.
GATHER_ONCE = 0,
// The most recent port allocator session will keep on running.
GATHER_CONTINUALLY,
// The most recent port allocator session will keep on running, and it will
// try to recover connectivity if the channel becomes disconnected.
GATHER_CONTINUALLY_AND_RECOVER,
};
// Stats that we can return about the connections for a transport channel.
// TODO(hta): Rename to ConnectionStats
struct ConnectionInfo {
@ -160,8 +171,13 @@ struct IceConfig {
// Time interval in milliseconds to ping a backup connection when the ICE
// channel is strongly connected.
int backup_connection_ping_interval = -1;
// If true, the most recent port allocator session will keep on running.
bool gather_continually = false;
ContinualGatheringPolicy continual_gathering_policy = GATHER_ONCE;
bool gather_continually() const {
return continual_gathering_policy == GATHER_CONTINUALLY ||
continual_gathering_policy == GATHER_CONTINUALLY_AND_RECOVER;
}
// Whether we should prioritize Relay/Relay candidate when nothing
// is writable yet.
@ -174,22 +190,29 @@ struct IceConfig {
// candidate pairs will succeed, even before a binding response is received.
bool presume_writable_when_fully_relayed = false;
// Interval to check on all networks and to perform ICE regathering on any
// active network having no connection on it.
rtc::Optional<int> regather_on_failed_networks_interval;
IceConfig() {}
IceConfig(int receiving_timeout_ms,
int backup_connection_ping_interval,
bool gather_continually,
ContinualGatheringPolicy gathering_policy,
bool prioritize_most_likely_candidate_pairs,
int stable_writable_connection_ping_interval_ms,
bool presume_writable_when_fully_relayed)
bool presume_writable_when_fully_relayed,
int regather_on_failed_networks_interval_ms)
: receiving_timeout(receiving_timeout_ms),
backup_connection_ping_interval(backup_connection_ping_interval),
gather_continually(gather_continually),
continual_gathering_policy(gathering_policy),
prioritize_most_likely_candidate_pairs(
prioritize_most_likely_candidate_pairs),
stable_writable_connection_ping_interval(
stable_writable_connection_ping_interval_ms),
presume_writable_when_fully_relayed(
presume_writable_when_fully_relayed) {}
presume_writable_when_fully_relayed),
regather_on_failed_networks_interval(
regather_on_failed_networks_interval_ms) {}
};
bool BadTransportDescription(const std::string& desc, std::string* err_desc);

View File

@ -137,11 +137,12 @@ class TransportControllerTest : public testing::Test,
channel2->SetConnectionCount(1);
}
cricket::IceConfig CreateIceConfig(int receiving_timeout,
bool gather_continually) {
cricket::IceConfig CreateIceConfig(
int receiving_timeout,
cricket::ContinualGatheringPolicy continual_gathering_policy) {
cricket::IceConfig config;
config.receiving_timeout = receiving_timeout;
config.gather_continually = gather_continually;
config.continual_gathering_policy = continual_gathering_policy;
return config;
}
@ -204,10 +205,13 @@ TEST_F(TransportControllerTest, TestSetIceConfig) {
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
ASSERT_NE(nullptr, channel1);
transport_controller_->SetIceConfig(CreateIceConfig(1000, true));
transport_controller_->SetIceConfig(
CreateIceConfig(1000, cricket::GATHER_CONTINUALLY));
EXPECT_EQ(1000, channel1->receiving_timeout());
EXPECT_TRUE(channel1->gather_continually());
transport_controller_->SetIceConfig(
CreateIceConfig(1000, cricket::GATHER_CONTINUALLY_AND_RECOVER));
// Test that value stored in controller is applied to new channels.
FakeTransportChannel* channel2 = CreateChannel("video", 1);
ASSERT_NE(nullptr, channel2);

View File

@ -184,7 +184,6 @@ BasicPortAllocatorSession::BasicPortAllocatorSession(
socket_factory_(allocator->socket_factory()),
allocation_started_(false),
network_manager_started_(false),
running_(false),
allocation_sequences_created_(false),
prune_turn_ports_(allocator->prune_turn_ports()) {
allocator_->network_manager()->SignalNetworksChanged.connect(
@ -239,27 +238,84 @@ void BasicPortAllocatorSession::SetCandidateFilter(uint32_t filter) {
void BasicPortAllocatorSession::StartGettingPorts() {
network_thread_ = rtc::Thread::Current();
PortAllocatorSession::StartGettingPorts();
if (!socket_factory_) {
owned_socket_factory_.reset(
new rtc::BasicPacketSocketFactory(network_thread_));
socket_factory_ = owned_socket_factory_.get();
}
running_ = true;
network_thread_->Post(RTC_FROM_HERE, this, MSG_CONFIG_START);
}
void BasicPortAllocatorSession::StopGettingPorts() {
ASSERT(rtc::Thread::Current() == network_thread_);
running_ = false;
network_thread_->Post(RTC_FROM_HERE, this, MSG_CONFIG_STOP);
ClearGettingPorts();
// Note: this must be called after ClearGettingPorts because both may set the
// session state and we should set the state to STOPPED.
PortAllocatorSession::StopGettingPorts();
}
void BasicPortAllocatorSession::ClearGettingPorts() {
ASSERT(rtc::Thread::Current() == network_thread_);
network_thread_->Clear(this, MSG_ALLOCATE);
for (uint32_t i = 0; i < sequences_.size(); ++i)
for (uint32_t i = 0; i < sequences_.size(); ++i) {
sequences_[i]->Stop();
}
PortAllocatorSession::ClearGettingPorts();
}
std::vector<rtc::Network*> BasicPortAllocatorSession::GetFailedNetworks() {
std::vector<rtc::Network*> networks = GetNetworks();
// A network interface may have both IPv4 and IPv6 networks. Only if
// neither of the networks has any connections, the network interface
// is considered failed and need to be regathered on.
std::set<std::string> networks_with_connection;
for (const PortData& data : ports_) {
Port* port = data.port();
if (!port->connections().empty()) {
networks_with_connection.insert(port->Network()->name());
}
}
networks.erase(
std::remove_if(networks.begin(), networks.end(),
[networks_with_connection](rtc::Network* network) {
// If a network does not have any connection, it is
// considered failed.
return networks_with_connection.find(network->name()) !=
networks_with_connection.end();
}),
networks.end());
return networks;
}
void BasicPortAllocatorSession::RegatherOnFailedNetworks() {
// Find the list of networks that have no connection.
std::vector<rtc::Network*> failed_networks = GetFailedNetworks();
if (failed_networks.empty()) {
return;
}
// Mark a sequence as "network failed" if its network is in the list of failed
// networks, so that it won't be considered as equivalent when the session
// regathers ports and candidates.
for (AllocationSequence* sequence : sequences_) {
if (!sequence->network_failed() &&
std::find(failed_networks.begin(), failed_networks.end(),
sequence->network()) != failed_networks.end()) {
sequence->set_network_failed();
}
}
// Remove ports from being used locally and send signaling to remove
// the candidates on the remote side.
RemovePortsAndCandidates(failed_networks);
if (allocation_started_ && network_manager_started_) {
DoAllocate();
}
}
std::vector<PortInterface*> BasicPortAllocatorSession::ReadyPorts() const {
@ -278,22 +334,28 @@ std::vector<Candidate> BasicPortAllocatorSession::ReadyCandidates() const {
if (!data.ready()) {
continue;
}
for (const Candidate& candidate : data.port()->Candidates()) {
if (!CheckCandidateFilter(candidate)) {
continue;
}
ProtocolType pvalue;
if (!StringToProto(candidate.protocol().c_str(), &pvalue) ||
!data.sequence()->ProtocolEnabled(pvalue)) {
continue;
}
candidates.push_back(SanitizeRelatedAddress(candidate));
}
GetCandidatesFromPort(data, &candidates);
}
return candidates;
}
void BasicPortAllocatorSession::GetCandidatesFromPort(
const PortData& data,
std::vector<Candidate>* candidates) const {
RTC_CHECK(candidates != nullptr);
for (const Candidate& candidate : data.port()->Candidates()) {
if (!CheckCandidateFilter(candidate)) {
continue;
}
ProtocolType pvalue;
if (!StringToProto(candidate.protocol().c_str(), &pvalue) ||
!data.sequence()->ProtocolEnabled(pvalue)) {
continue;
}
candidates->push_back(SanitizeRelatedAddress(candidate));
}
}
Candidate BasicPortAllocatorSession::SanitizeRelatedAddress(
const Candidate& c) const {
Candidate copy = c;
@ -437,9 +499,8 @@ void BasicPortAllocatorSession::OnAllocate() {
allocation_started_ = true;
}
void BasicPortAllocatorSession::GetNetworks(
std::vector<rtc::Network*>* networks) {
networks->clear();
std::vector<rtc::Network*> BasicPortAllocatorSession::GetNetworks() {
std::vector<rtc::Network*> networks;
rtc::NetworkManager* network_manager = allocator_->network_manager();
ASSERT(network_manager != nullptr);
// If the network permission state is BLOCKED, we just act as if the flag has
@ -453,37 +514,37 @@ void BasicPortAllocatorSession::GetNetworks(
// traffic by OS is also used here to avoid any local or public IP leakage
// during stun process.
if (flags() & PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION) {
network_manager->GetAnyAddressNetworks(networks);
network_manager->GetAnyAddressNetworks(&networks);
} else {
network_manager->GetNetworks(networks);
network_manager->GetNetworks(&networks);
}
networks->erase(std::remove_if(networks->begin(), networks->end(),
[this](rtc::Network* network) {
return allocator_->network_ignore_mask() &
network->type();
}),
networks->end());
networks.erase(std::remove_if(networks.begin(), networks.end(),
[this](rtc::Network* network) {
return allocator_->network_ignore_mask() &
network->type();
}),
networks.end());
if (flags() & PORTALLOCATOR_DISABLE_COSTLY_NETWORKS) {
uint16_t lowest_cost = rtc::kNetworkCostMax;
for (rtc::Network* network : *networks) {
for (rtc::Network* network : networks) {
lowest_cost = std::min<uint16_t>(lowest_cost, network->GetCost());
}
networks->erase(std::remove_if(networks->begin(), networks->end(),
[lowest_cost](rtc::Network* network) {
return network->GetCost() >
lowest_cost + rtc::kNetworkCostLow;
}),
networks->end());
networks.erase(std::remove_if(networks.begin(), networks.end(),
[lowest_cost](rtc::Network* network) {
return network->GetCost() >
lowest_cost + rtc::kNetworkCostLow;
}),
networks.end());
}
return networks;
}
// For each network, see if we have a sequence that covers it already. If not,
// create a new sequence to create the appropriate ports.
void BasicPortAllocatorSession::DoAllocate() {
bool done_signal_needed = false;
std::vector<rtc::Network*> networks;
GetNetworks(&networks);
std::vector<rtc::Network*> networks = GetNetworks();
if (networks.empty()) {
LOG(LS_WARNING) << "Machine has no networks; no ports will be allocated";
@ -528,8 +589,9 @@ void BasicPortAllocatorSession::DoAllocate() {
done_signal_needed = true;
sequence->SignalPortAllocationComplete.connect(
this, &BasicPortAllocatorSession::OnPortAllocationComplete);
if (running_)
if (!IsStopped()) {
sequence->Start();
}
sequences_.push_back(sequence);
}
}
@ -539,17 +601,19 @@ void BasicPortAllocatorSession::DoAllocate() {
}
void BasicPortAllocatorSession::OnNetworksChanged() {
std::vector<rtc::Network*> networks;
GetNetworks(&networks);
std::vector<rtc::Network*> networks = GetNetworks();
std::vector<rtc::Network*> failed_networks;
for (AllocationSequence* sequence : sequences_) {
// Remove the network from the allocation sequence if it is not in
// Mark the sequence as "network failed" if its network is not in
// |networks|.
if (!sequence->network_removed() &&
if (!sequence->network_failed() &&
std::find(networks.begin(), networks.end(), sequence->network()) ==
networks.end()) {
sequence->OnNetworkRemoved();
sequence->OnNetworkFailed();
failed_networks.push_back(sequence->network());
}
}
RemovePortsAndCandidates(failed_networks);
network_manager_started_ = true;
if (allocation_started_)
@ -847,6 +911,32 @@ BasicPortAllocatorSession::PortData* BasicPortAllocatorSession::FindPort(
return NULL;
}
// Removes ports and candidates created on a given list of networks.
void BasicPortAllocatorSession::RemovePortsAndCandidates(
const std::vector<rtc::Network*>& networks) {
std::vector<PortInterface*> ports_to_remove;
std::vector<Candidate> candidates_to_remove;
for (PortData& data : ports_) {
if (std::find(networks.begin(), networks.end(),
data.sequence()->network()) == networks.end()) {
continue;
}
ports_to_remove.push_back(data.port());
if (data.has_pairable_candidate()) {
GetCandidatesFromPort(data, &candidates_to_remove);
// Mark the port as having no pairable candidates so that its candidates
// won't be removed multiple times.
data.set_has_pairable_candidate(false);
}
}
if (!ports_to_remove.empty()) {
SignalPortsRemoved(this, ports_to_remove);
}
if (!candidates_to_remove.empty()) {
SignalCandidatesRemoved(this, candidates_to_remove);
}
}
// AllocationSequence
AllocationSequence::AllocationSequence(BasicPortAllocatorSession* session,
@ -884,10 +974,11 @@ void AllocationSequence::Clear() {
turn_ports_.clear();
}
void AllocationSequence::OnNetworkRemoved() {
// Stop the allocation sequence if its network is gone.
void AllocationSequence::OnNetworkFailed() {
RTC_DCHECK(!network_failed_);
network_failed_ = true;
// Stop the allocation sequence if its network failed.
Stop();
network_removed_ = true;
}
AllocationSequence::~AllocationSequence() {
@ -896,8 +987,8 @@ AllocationSequence::~AllocationSequence() {
void AllocationSequence::DisableEquivalentPhases(rtc::Network* network,
PortConfiguration* config, uint32_t* flags) {
if (network_removed_) {
// If the network of this allocation sequence has ever gone away,
if (network_failed_) {
// If the network of this allocation sequence has ever become failed,
// it won't be equivalent to the new network.
return;
}

View File

@ -47,7 +47,7 @@ class BasicPortAllocator : public PortAllocator {
int network_ignore_mask() const { return network_ignore_mask_; }
rtc::NetworkManager* network_manager() { return network_manager_; }
rtc::NetworkManager* network_manager() const { return network_manager_; }
// If socket_factory() is set to NULL each PortAllocatorSession
// creates its own socket factory.
@ -92,11 +92,11 @@ class BasicPortAllocatorSession : public PortAllocatorSession,
void StartGettingPorts() override;
void StopGettingPorts() override;
void ClearGettingPorts() override;
bool IsGettingPorts() override { return running_; }
// These will all be cricket::Ports.
std::vector<PortInterface*> ReadyPorts() const override;
std::vector<Candidate> ReadyCandidates() const override;
bool CandidatesAllocationDone() const override;
void RegatherOnFailedNetworks() override;
protected:
void UpdateIceParametersInternal() override;
@ -156,8 +156,8 @@ class BasicPortAllocatorSession : public PortAllocatorSession,
};
Port* port_ = nullptr;
AllocationSequence* sequence_ = nullptr;
State state_ = STATE_INPROGRESS;
bool has_pairable_candidate_ = false;
State state_ = STATE_INPROGRESS;
};
void OnConfigReady(PortConfiguration* config);
@ -180,7 +180,8 @@ class BasicPortAllocatorSession : public PortAllocatorSession,
void MaybeSignalCandidatesAllocationDone();
void OnPortAllocationComplete(AllocationSequence* seq);
PortData* FindPort(Port* port);
void GetNetworks(std::vector<rtc::Network*>* networks);
std::vector<rtc::Network*> GetNetworks();
std::vector<rtc::Network*> GetFailedNetworks();
bool CheckCandidateFilter(const Candidate& c) const;
bool CandidatePairable(const Candidate& c, const Port* port) const;
@ -188,6 +189,12 @@ class BasicPortAllocatorSession : public PortAllocatorSession,
// in order to avoid leaking any information.
Candidate SanitizeRelatedAddress(const Candidate& c) const;
// Removes the ports and candidates on given networks.
void RemovePortsAndCandidates(const std::vector<rtc::Network*>& networks);
// Gets filtered and sanitized candidates generated from a port and
// append to |candidates|.
void GetCandidatesFromPort(const PortData& data,
std::vector<Candidate>* candidates) const;
Port* GetBestTurnPortForNetwork(const std::string& network_name) const;
// Returns true if at least one TURN port is pruned.
bool PruneTurnPorts(Port* newly_pairable_turn_port);
@ -198,7 +205,6 @@ class BasicPortAllocatorSession : public PortAllocatorSession,
rtc::PacketSocketFactory* socket_factory_;
bool allocation_started_;
bool network_manager_started_;
bool running_; // set when StartGetAllPorts is called
bool allocation_sequences_created_;
std::vector<PortConfiguration*> configs_;
std::vector<AllocationSequence*> sequences_;
@ -271,11 +277,13 @@ class AllocationSequence : public rtc::MessageHandler,
~AllocationSequence();
bool Init();
void Clear();
void OnNetworkRemoved();
void OnNetworkFailed();
State state() const { return state_; }
const rtc::Network* network() const { return network_; }
bool network_removed() const { return network_removed_; }
rtc::Network* network() const { return network_; }
bool network_failed() const { return network_failed_; }
void set_network_failed() { network_failed_ = true; }
// Disables the phases for a new sequence that this one already covers for an
// equivalent network setup.
@ -325,7 +333,7 @@ class AllocationSequence : public rtc::MessageHandler,
void OnPortDestroyed(PortInterface* port);
BasicPortAllocatorSession* session_;
bool network_removed_ = false;
bool network_failed_ = false;
rtc::Network* network_;
rtc::IPAddress ip_;
PortConfiguration* config_;