Ping the premier connection on each network with higher priority.
When the selected connection becomes not receiving and there are many connections,
If we use a round-robin fashion to ping all connections, none of the connections will
be in receiving state for sufficient long time to ensure switching connections promptly.
Triggered check will help in this situation to some extent but it may still fail to switch promptly when there are a lot of connections.
With this CL, if the selected connection is weak, once we find a writable connection on a network we start to ping it with a higher priority to keep it in receiving state.
Plus, if the selected connection is weak, we choose a shorter ping interval (900ms) for all writable connections.
BUG=b/32022719
Review-Url: https://codereview.webrtc.org/2369963004
Cr-Commit-Position: refs/heads/master@{#14991}
diff --git a/webrtc/base/firewallsocketserver.cc b/webrtc/base/firewallsocketserver.cc
index d6d03df..92ac88f 100644
--- a/webrtc/base/firewallsocketserver.cc
+++ b/webrtc/base/firewallsocketserver.cc
@@ -41,13 +41,13 @@
return SendTo(pv, cb, GetRemoteAddress());
}
int SendTo(const void* pv, size_t cb, const SocketAddress& addr) override {
- if (type_ == SOCK_DGRAM) {
- if (!server_->Check(FP_UDP, GetLocalAddress(), addr)) {
- LOG(LS_VERBOSE) << "FirewallSocket outbound UDP packet from "
- << GetLocalAddress().ToSensitiveString() << " to "
- << addr.ToSensitiveString() << " dropped";
- return static_cast<int>(cb);
- }
+ RTC_DCHECK(type_ == SOCK_DGRAM || type_ == SOCK_STREAM);
+ FirewallProtocol protocol = (type_ == SOCK_DGRAM) ? FP_UDP : FP_TCP;
+ if (!server_->Check(protocol, GetLocalAddress(), addr)) {
+ LOG(LS_VERBOSE) << "FirewallSocket outbound packet with type " << type_
+ << " from " << GetLocalAddress().ToSensitiveString()
+ << " to " << addr.ToSensitiveString() << " dropped";
+ return static_cast<int>(cb);
}
return AsyncSocketAdapter::SendTo(pv, cb, addr);
}
diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc
index 6bcf0d7..e1a955c 100644
--- a/webrtc/p2p/base/p2ptransportchannel.cc
+++ b/webrtc/p2p/base/p2ptransportchannel.cc
@@ -11,6 +11,7 @@
#include "webrtc/p2p/base/p2ptransportchannel.h"
#include <algorithm>
+#include <iterator>
#include <set>
#include "webrtc/api/peerconnectioninterface.h"
@@ -66,6 +67,8 @@
// well on a 28.8K modem, which is the slowest connection on which the voice
// quality is reasonable at all.
static const int PING_PACKET_SIZE = 60 * 8;
+
+// The next two ping intervals are at the channel level.
// STRONG_PING_INTERVAL (480ms) is applied when the selected connection is both
// writable and receiving.
static const int STRONG_PING_INTERVAL = 1000 * PING_PACKET_SIZE / 1000;
@@ -73,11 +76,13 @@
// not writable or not receiving.
const int WEAK_PING_INTERVAL = 1000 * PING_PACKET_SIZE / 10000;
-// Writable connections are pinged at a faster rate while stabilizing.
-const int STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL = 900; // ms
-
-// Writable connections are pinged at a slower rate once stabilized.
-const int STABLE_WRITABLE_CONNECTION_PING_INTERVAL = 2500; // ms
+// The next two ping intervals are at the connection level.
+// Writable connections are pinged at a faster rate while the connections are
+// stabilizing or the channel is weak.
+const int WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL = 900; // ms
+// Writable connections are pinged at a slower rate once they are stabilized and
+// the channel is strongly connected.
+const int STRONG_AND_STABLE_WRITABLE_CONNECTION_PING_INTERVAL = 2500; // ms
static const int MIN_CHECK_RECEIVING_INTERVAL = 50; // ms
@@ -116,7 +121,7 @@
DEFAULT_BACKUP_CONNECTION_PING_INTERVAL,
GATHER_ONCE /* continual_gathering_policy */,
false /* prioritize_most_likely_candidate_pairs */,
- STABLE_WRITABLE_CONNECTION_PING_INTERVAL,
+ STRONG_AND_STABLE_WRITABLE_CONNECTION_PING_INTERVAL,
true /* presume_writable_when_fully_relayed */,
DEFAULT_REGATHER_ON_FAILED_NETWORKS_INTERVAL,
RECEIVING_SWITCHING_DELAY) {
@@ -1243,6 +1248,7 @@
// that amongst equal preference, writable connections, this will choose the
// one whose estimated latency is lowest. So it is the only one that we
// need to consider switching to.
+ // TODO(honghaiz): Don't sort; Just use std::max_element in the right places.
std::stable_sort(connections_.begin(), connections_.end(),
[this](const Connection* a, const Connection* b) {
int cmp = CompareConnections(
@@ -1305,6 +1311,37 @@
MaybeStartPinging();
}
+std::map<rtc::Network*, Connection*>
+P2PTransportChannel::GetBestConnectionByNetwork() const {
+ // |connections_| has been sorted, so the first one in the list on a given
+ // network is the best connection on the network, except that the selected
+ // connection is always the best connection on the network.
+ std::map<rtc::Network*, Connection*> best_connection_by_network;
+ if (selected_connection_) {
+ best_connection_by_network[selected_connection_->port()->Network()] =
+ selected_connection_;
+ }
+ // TODO(honghaiz): Need to update this if |connections_| are not sorted.
+ for (Connection* conn : connections_) {
+ rtc::Network* network = conn->port()->Network();
+ // This only inserts when the network does not exist in the map.
+ best_connection_by_network.insert(std::make_pair(network, conn));
+ }
+ return best_connection_by_network;
+}
+
+std::vector<Connection*>
+P2PTransportChannel::GetBestWritableConnectionPerNetwork() const {
+ std::vector<Connection*> connections;
+ for (auto kv : GetBestConnectionByNetwork()) {
+ Connection* conn = kv.second;
+ if (conn->writable() && conn->connected()) {
+ connections.push_back(conn);
+ }
+ }
+ return connections;
+}
+
void P2PTransportChannel::PruneConnections() {
// We can prune any connection for which there is a connected, writable
// connection on the same network with better or equal priority. We leave
@@ -1312,28 +1349,19 @@
// which point, we would prune out the current selected connection). We leave
// connections on other networks because they may not be using the same
// resources and they may represent very distinct paths over which we can
- // switch. If the |premier| connection is not connected, we may be
- // reconnecting a TCP connection and temporarily do not prune connections in
- // this network. See the big comment in CompareConnectionStates.
-
- // Get a list of the networks that we are using.
- std::set<rtc::Network*> networks;
- for (const Connection* conn : connections_) {
- networks.insert(conn->port()->Network());
- }
- for (rtc::Network* network : networks) {
- Connection* premier = GetBestConnectionOnNetwork(network);
- // Do not prune connections if the current selected connection is weak on
- // this network. Otherwise, it may delete connections prematurely.
- if (!premier || premier->weak()) {
- continue;
- }
-
- for (Connection* conn : connections_) {
- if ((conn != premier) && (conn->port()->Network() == network) &&
- (CompareConnectionCandidates(premier, conn) >= 0)) {
- conn->Prune();
- }
+ // switch. If |best_conn_on_network| is not connected, we may be reconnecting
+ // a TCP connection and should not prune connections in this network.
+ // See the big comment in CompareConnectionStates.
+ auto best_connection_by_network = GetBestConnectionByNetwork();
+ for (Connection* conn : connections_) {
+ // Do not prune connections if the current best connection on this network
+ // is weak. Otherwise, it may delete connections prematurely.
+ Connection* best_conn_on_network =
+ best_connection_by_network[conn->port()->Network()];
+ if (best_conn_on_network && conn != best_conn_on_network &&
+ !best_conn_on_network->weak() &&
+ CompareConnectionCandidates(best_conn_on_network, conn) >= 0) {
+ conn->Prune();
}
}
}
@@ -1471,26 +1499,6 @@
PresumedWritable(connection));
}
-// If we have a selected connection, return it, otherwise return top one in the
-// list (later we will mark it best).
-Connection* P2PTransportChannel::GetBestConnectionOnNetwork(
- rtc::Network* network) const {
- // If the selected connection is on this network, then it wins.
- if (selected_connection_ &&
- (selected_connection_->port()->Network() == network)) {
- return selected_connection_;
- }
-
- // Otherwise, we return the top-most in sorted order.
- for (size_t i = 0; i < connections_.size(); ++i) {
- if (connections_[i]->port()->Network() == network) {
- return connections_[i];
- }
- }
-
- return NULL;
-}
-
// Handle any queued up requests
void P2PTransportChannel::OnMessage(rtc::Message *pmsg) {
switch (pmsg->message_id) {
@@ -1592,18 +1600,14 @@
// Ping writable, active connections if it's been long enough since the last
// ping.
- int ping_interval = CalculateActiveWritablePingInterval(conn, now);
- return (now >= conn->last_ping_sent() + ping_interval);
+ return WritableConnectionPastPingInterval(conn, now);
}
-bool P2PTransportChannel::IsSelectedConnectionPingable(int64_t now) {
- if (!selected_connection_ || !selected_connection_->connected() ||
- !selected_connection_->writable()) {
- return false;
- }
-
- int interval = CalculateActiveWritablePingInterval(selected_connection_, now);
- return selected_connection_->last_ping_sent() + interval <= now;
+bool P2PTransportChannel::WritableConnectionPastPingInterval(
+ const Connection* conn,
+ int64_t now) const {
+ int interval = CalculateActiveWritablePingInterval(conn, now);
+ return conn->last_ping_sent() + interval <= now;
}
int P2PTransportChannel::CalculateActiveWritablePingInterval(
@@ -1616,28 +1620,92 @@
}
int stable_interval = config_.stable_writable_connection_ping_interval;
- int stablizing_interval =
- std::min(stable_interval, STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL);
-
- return conn->stable(now) ? stable_interval : stablizing_interval;
+ int weak_or_stablizing_interval = std::min(
+ stable_interval, WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL);
+ // If the channel is weak or the connection is not stable yet, use the
+ // weak_or_stablizing_interval.
+ return (!weak() && conn->stable(now)) ? stable_interval
+ : weak_or_stablizing_interval;
}
-// Returns the next pingable connection to ping. This will be the oldest
-// pingable connection unless we have a connected, writable connection that is
-// past the writable ping interval. When reconnecting a TCP
-// connection, the selected connection is disconnected, although still WRITABLE
-// while reconnecting. The newly created connection should be selected as the
-// ping target to become writable instead. See the big comment in
-// CompareConnectionStates.
+// Returns the next pingable connection to ping.
Connection* P2PTransportChannel::FindNextPingableConnection() {
int64_t now = rtc::TimeMillis();
- Connection* conn_to_ping = nullptr;
- if (IsSelectedConnectionPingable(now)) {
- conn_to_ping = selected_connection_;
- } else {
- conn_to_ping = FindConnectionToPing(now);
+
+ // Rule 1: Selected connection takes priority over non-selected ones.
+ if (selected_connection_ && selected_connection_->connected() &&
+ selected_connection_->writable() &&
+ WritableConnectionPastPingInterval(selected_connection_, now)) {
+ return selected_connection_;
}
- return conn_to_ping;
+
+ // Rule 2: If the channel is weak, we need to find a new writable and
+ // receiving connection, probably on a different network. If there are lots of
+ // connections, it may take several seconds between two pings for every
+ // non-selected connection. This will cause the receiving state of those
+ // connections to be false, and thus they won't be selected. This is
+ // problematic for network fail-over. We want to make sure at least one
+ // connection per network is pinged frequently enough in order for it to be
+ // selectable. So we prioritize one connection per network.
+ // Rule 2.1: Among such connections, pick the one with the earliest
+ // last-ping-sent time.
+ if (weak()) {
+ auto selectable_connections = GetBestWritableConnectionPerNetwork();
+ std::vector<Connection*> pingable_selectable_connections;
+ std::copy_if(selectable_connections.begin(), selectable_connections.end(),
+ std::back_inserter(pingable_selectable_connections),
+ [this, now](Connection* conn) {
+ return WritableConnectionPastPingInterval(conn, now);
+ });
+ auto iter = std::min_element(pingable_selectable_connections.begin(),
+ pingable_selectable_connections.end(),
+ [](Connection* conn1, Connection* conn2) {
+ return conn1->last_ping_sent() <
+ conn2->last_ping_sent();
+ });
+ if (iter != pingable_selectable_connections.end()) {
+ return *iter;
+ }
+ }
+
+ // Rule 3: Triggered checks have priority over non-triggered connections.
+ // Rule 3.1: Among triggered checks, oldest takes precedence.
+ Connection* oldest_triggered_check =
+ FindOldestConnectionNeedingTriggeredCheck(now);
+ if (oldest_triggered_check) {
+ return oldest_triggered_check;
+ }
+
+ // Rule 4: Unpinged connections have priority over pinged ones.
+ RTC_CHECK(connections_.size() ==
+ pinged_connections_.size() + unpinged_connections_.size());
+ // If there are unpinged and pingable connections, only ping those.
+ // Otherwise, treat everything as unpinged.
+ // TODO(honghaiz): Instead of adding two separate vectors, we can add a state
+ // "pinged" to filter out unpinged connections.
+ if (std::find_if(unpinged_connections_.begin(), unpinged_connections_.end(),
+ [this, now](Connection* conn) {
+ return this->IsPingable(conn, now);
+ }) == unpinged_connections_.end()) {
+ unpinged_connections_.insert(pinged_connections_.begin(),
+ pinged_connections_.end());
+ pinged_connections_.clear();
+ }
+
+ // Among un-pinged pingable connections, "more pingable" takes precedence.
+ std::vector<Connection*> pingable_connections;
+ std::copy_if(unpinged_connections_.begin(), unpinged_connections_.end(),
+ std::back_inserter(pingable_connections),
+ [this, now](Connection* conn) { return IsPingable(conn, now); });
+ auto iter =
+ std::max_element(pingable_connections.begin(), pingable_connections.end(),
+ [this, now](Connection* conn1, Connection* conn2) {
+ return MorePingable(conn1, conn2) == conn2;
+ });
+ if (iter != pingable_connections.end()) {
+ return *iter;
+ }
+ return nullptr;
}
void P2PTransportChannel::MarkConnectionPinged(Connection* conn) {
@@ -1749,8 +1817,9 @@
unpinged_connections_.erase(*iter);
connections_.erase(iter);
- LOG_J(LS_INFO, this) << "Removed connection ("
- << static_cast<int>(connections_.size()) << " remaining)";
+ LOG_J(LS_INFO, this) << "Removed connection " << std::hex << connection
+ << std::dec << " (" << connections_.size()
+ << " remaining)";
// If this is currently the selected connection, then we need to pick a new
// one. The call to SortConnectionsAndUpdateState will pick a new one. It
@@ -1878,7 +1947,7 @@
// Find "triggered checks". We ping first those connections that have
// received a ping but have not sent a ping since receiving it
-// (last_received_ping > last_sent_ping). But we shouldn't do
+// (last_ping_received > last_ping_sent). But we shouldn't do
// triggered checks if the connection is already writable.
Connection* P2PTransportChannel::FindOldestConnectionNeedingTriggeredCheck(
int64_t now) {
@@ -1905,39 +1974,6 @@
return oldest_needing_triggered_check;
}
-Connection* P2PTransportChannel::FindConnectionToPing(int64_t now) {
- RTC_CHECK(connections_.size() ==
- pinged_connections_.size() + unpinged_connections_.size());
-
- // If there is nothing pingable in the |unpinged_connections_|, copy
- // over from |pinged_connections_|. We do this here such that the
- // new connection will take precedence.
- if (std::find_if(unpinged_connections_.begin(), unpinged_connections_.end(),
- [this, now](Connection* conn) {
- return this->IsPingable(conn, now);
- }) == unpinged_connections_.end()) {
- unpinged_connections_.insert(pinged_connections_.begin(),
- pinged_connections_.end());
- pinged_connections_.clear();
- }
-
- Connection* conn_to_ping = FindOldestConnectionNeedingTriggeredCheck(now);
- if (conn_to_ping) {
- return conn_to_ping;
- }
-
- for (Connection* conn : unpinged_connections_) {
- if (!IsPingable(conn, now)) {
- continue;
- }
- if (!conn_to_ping ||
- SelectMostPingableConnection(conn_to_ping, conn) == conn) {
- conn_to_ping = conn;
- }
- }
- return conn_to_ping;
-}
-
Connection* P2PTransportChannel::MostLikelyToWork(Connection* conn1,
Connection* conn2) {
bool rr1 = IsRelayRelay(conn1);
@@ -1969,9 +2005,8 @@
return nullptr;
}
-Connection* P2PTransportChannel::SelectMostPingableConnection(
- Connection* conn1,
- Connection* conn2) {
+Connection* P2PTransportChannel::MorePingable(Connection* conn1,
+ Connection* conn2) {
RTC_DCHECK(conn1 != conn2);
if (config_.prioritize_most_likely_candidate_pairs) {
Connection* most_likely_to_work_conn = MostLikelyToWork(conn1, conn2);
diff --git a/webrtc/p2p/base/p2ptransportchannel.h b/webrtc/p2p/base/p2ptransportchannel.h
index 672abb7..cdb8300 100644
--- a/webrtc/p2p/base/p2ptransportchannel.h
+++ b/webrtc/p2p/base/p2ptransportchannel.h
@@ -43,8 +43,8 @@
enum class IceRestartState { CONNECTING, CONNECTED, DISCONNECTED, MAX_VALUE };
extern const int WEAK_PING_INTERVAL;
-extern const int STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL;
-extern const int STABLE_WRITABLE_CONNECTION_PING_INTERVAL;
+extern const int WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL;
+extern const int STRONG_AND_STABLE_WRITABLE_CONNECTION_PING_INTERVAL;
static const int MIN_PINGS_AT_WEAK_PING_INTERVAL = 3;
// Adds the port on which the candidate originated.
@@ -261,7 +261,10 @@
void RememberRemoteCandidate(const Candidate& remote_candidate,
PortInterface* origin_port);
bool IsPingable(const Connection* conn, int64_t now) const;
- bool IsSelectedConnectionPingable(int64_t now);
+ // Whether a writable connection is past its ping interval and needs to be
+ // pinged again.
+ bool WritableConnectionPastPingInterval(const Connection* conn,
+ int64_t now) const;
int CalculateActiveWritablePingInterval(const Connection* conn,
int64_t now) const;
void PingConnection(Connection* conn);
@@ -317,16 +320,16 @@
// Returns true if the new_connection is selected for transmission.
bool MaybeSwitchSelectedConnection(Connection* new_connection,
const std::string& reason);
-
+ // Gets the best connection for each network.
+ std::map<rtc::Network*, Connection*> GetBestConnectionByNetwork() const;
+ std::vector<Connection*> GetBestWritableConnectionPerNetwork() const;
void PruneConnections();
bool IsBackupConnection(const Connection* conn) const;
- Connection* FindConnectionToPing(int64_t now);
Connection* FindOldestConnectionNeedingTriggeredCheck(int64_t now);
// Between |conn1| and |conn2|, this function returns the one which should
// be pinged first.
- Connection* SelectMostPingableConnection(Connection* conn1,
- Connection* conn2);
+ Connection* MorePingable(Connection* conn1, Connection* conn2);
// Select the connection which is Relay/Relay. If both of them are,
// UDP relay protocol takes precedence.
Connection* MostLikelyToWork(Connection* conn1, Connection* conn2);
diff --git a/webrtc/p2p/base/p2ptransportchannel_unittest.cc b/webrtc/p2p/base/p2ptransportchannel_unittest.cc
index 79b51b1..54fc347 100644
--- a/webrtc/p2p/base/p2ptransportchannel_unittest.cc
+++ b/webrtc/p2p/base/p2ptransportchannel_unittest.cc
@@ -55,11 +55,13 @@
// 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)
-};
+ SocketAddress("2600:0:1000:1b03:2e41:38ff:fea6:f2a4", 0)};
// For configuring multihomed clients.
static const SocketAddress kAlternateAddrs[2] = {
SocketAddress("101.101.101.101", 0), SocketAddress("202.202.202.202", 0)};
+static const SocketAddress kIPv6AlternateAddrs[2] = {
+ SocketAddress("2401:4030:1:2c00:be30:abcd:efab:cdef", 0),
+ SocketAddress("2601:0:1000:1b03:2e41:38ff:fea6:f2a4", 0)};
// Addresses for HTTP proxy servers.
static const SocketAddress kHttpsProxyAddrs[2] =
{ SocketAddress("11.11.11.1", 443), SocketAddress("22.22.22.1", 443) };
@@ -429,7 +431,7 @@
return NULL;
}
}
- PortAllocator* GetAllocator(int endpoint) {
+ BasicPortAllocator* GetAllocator(int endpoint) {
return GetEndpoint(endpoint)->allocator_.get();
}
webrtc::FakeMetricsObserver* GetMetricsObserver(int endpoint) {
@@ -2087,10 +2089,10 @@
// 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 Connection* GetConnectionWithRemoteAddress(
+ P2PTransportChannel* channel,
const SocketAddress& address) {
- for (cricket::Connection* conn : channel->connections()) {
+ for (Connection* conn : channel->connections()) {
if (conn->remote_candidate().address().EqualIPs(address)) {
return conn;
}
@@ -2098,10 +2100,9 @@
return nullptr;
}
- cricket::Connection* GetConnectionWithLocalAddress(
- cricket::P2PTransportChannel* channel,
- const SocketAddress& address) {
- for (cricket::Connection* conn : channel->connections()) {
+ Connection* GetConnectionWithLocalAddress(P2PTransportChannel* channel,
+ const SocketAddress& address) {
+ for (Connection* conn : channel->connections()) {
if (conn->local_candidate().address().EqualIPs(address)) {
return conn;
}
@@ -2109,10 +2110,21 @@
return nullptr;
}
- void DestroyAllButBestConnection(cricket::P2PTransportChannel* channel) {
- const cricket::Connection* selected_connection =
- channel->selected_connection();
- for (cricket::Connection* conn : channel->connections()) {
+ Connection* GetConnection(P2PTransportChannel* channel,
+ const SocketAddress& local,
+ const SocketAddress& remote) {
+ for (Connection* conn : channel->connections()) {
+ if (conn->local_candidate().address().EqualIPs(local) &&
+ conn->remote_candidate().address().EqualIPs(remote)) {
+ return conn;
+ }
+ }
+ return nullptr;
+ }
+
+ void DestroyAllButBestConnection(P2PTransportChannel* channel) {
+ const Connection* selected_connection = channel->selected_connection();
+ for (Connection* conn : channel->connections()) {
if (conn != selected_connection) {
conn->Destroy();
}
@@ -2234,6 +2246,99 @@
DestroyChannels();
}
+// Tests that we can quickly switch links if an interface goes down when
+// there are many connections.
+TEST_F(P2PTransportChannelMultihomedTest, TestFailoverWithManyConnections) {
+ rtc::ScopedFakeClock clock;
+ test_turn_server()->AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP);
+ RelayServerConfig turn_server(RELAY_TURN);
+ turn_server.credentials = kRelayCredentials;
+ turn_server.ports.push_back(
+ ProtocolAddress(kTurnTcpIntAddr, PROTO_TCP, false));
+ GetAllocator(0)->AddTurnServer(turn_server);
+ GetAllocator(1)->AddTurnServer(turn_server);
+ // Enable IPv6
+ SetAllocatorFlags(0, PORTALLOCATOR_ENABLE_IPV6);
+ SetAllocatorFlags(1, PORTALLOCATOR_ENABLE_IPV6);
+ SetAllocationStepDelay(0, kMinimumStepDelay);
+ SetAllocationStepDelay(1, kMinimumStepDelay);
+
+ auto& wifi = kPublicAddrs;
+ auto& cellular = kAlternateAddrs;
+ auto& wifiIpv6 = kIPv6PublicAddrs;
+ auto& cellularIpv6 = kIPv6AlternateAddrs;
+ AddAddress(0, wifi[0], "wifi0", rtc::ADAPTER_TYPE_WIFI);
+ AddAddress(0, wifiIpv6[0], "wifi0", rtc::ADAPTER_TYPE_WIFI);
+ AddAddress(0, cellular[0], "cellular0", rtc::ADAPTER_TYPE_CELLULAR);
+ AddAddress(0, cellularIpv6[0], "cellular0", rtc::ADAPTER_TYPE_CELLULAR);
+ AddAddress(1, wifi[1], "wifi1", rtc::ADAPTER_TYPE_WIFI);
+ AddAddress(1, wifiIpv6[1], "wifi1", rtc::ADAPTER_TYPE_WIFI);
+ AddAddress(1, cellular[1], "cellular1", rtc::ADAPTER_TYPE_CELLULAR);
+ AddAddress(1, cellularIpv6[1], "cellular1", rtc::ADAPTER_TYPE_CELLULAR);
+
+ // Set smaller delay on the TCP TURN server so that TCP TURN candidates
+ // will be created in time.
+ virtual_socket_server()->SetDelayOnAddress(kTurnTcpIntAddr, 1);
+ virtual_socket_server()->SetDelayOnAddress(kTurnUdpExtAddr, 1);
+ virtual_socket_server()->set_delay_mean(500);
+ virtual_socket_server()->UpdateDelayDistribution();
+
+ // Make the receiving timeout shorter for testing.
+ IceConfig config = CreateIceConfig(1000, GATHER_CONTINUALLY);
+ // Create channels and let them go writable, as usual.
+ CreateChannels(config, config, true /* ice_renomination */);
+ EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
+ ep2_ch1()->receiving() &&
+ ep2_ch1()->writable(),
+ kMediumTimeout, clock);
+ EXPECT_TRUE_SIMULATED_WAIT(
+ ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection() &&
+ LocalCandidate(ep1_ch1())->address().EqualIPs(wifiIpv6[0]) &&
+ RemoteCandidate(ep1_ch1())->address().EqualIPs(wifiIpv6[1]),
+ kMediumTimeout, clock);
+
+ // Blackhole any traffic to or from the wifi on endpoint 1.
+ LOG(LS_INFO) << "Failing over...";
+ fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, wifi[0]);
+ fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, wifiIpv6[0]);
+
+ // The selected connections may switch, so keep references to them.
+ const Connection* selected_connection1 = ep1_ch1()->selected_connection();
+ const Connection* selected_connection2 = ep2_ch1()->selected_connection();
+ EXPECT_TRUE_SIMULATED_WAIT(
+ !selected_connection1->receiving() && !selected_connection2->receiving(),
+ kMediumTimeout, clock);
+
+ // Per-network best connections will be pinged at relatively higher rate when
+ // the selected connection becomes not receiving.
+ Connection* per_network_best_connection1 =
+ GetConnection(ep1_ch1(), cellularIpv6[0], wifiIpv6[1]);
+ ASSERT_NE(nullptr, per_network_best_connection1);
+ int64_t last_ping_sent1 = per_network_best_connection1->last_ping_sent();
+ int num_pings_sent1 = per_network_best_connection1->num_pings_sent();
+ EXPECT_TRUE_SIMULATED_WAIT(
+ num_pings_sent1 < per_network_best_connection1->num_pings_sent(),
+ kMediumTimeout, clock);
+ int64_t ping_interval1 =
+ (per_network_best_connection1->last_ping_sent() - last_ping_sent1) /
+ (per_network_best_connection1->num_pings_sent() - num_pings_sent1);
+ constexpr int SCHEDULING_DELAY = 200;
+ EXPECT_LT(
+ ping_interval1,
+ WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL + SCHEDULING_DELAY);
+
+ // It should switch over to use the cellular IPv6 addr on endpoint 1 before
+ // it timed out on writing.
+ EXPECT_TRUE_SIMULATED_WAIT(
+ ep1_ch1()->selected_connection()->receiving() &&
+ ep2_ch1()->selected_connection()->receiving() &&
+ RemoteCandidate(ep2_ch1())->address().EqualIPs(cellularIpv6[0]) &&
+ LocalCandidate(ep1_ch1())->address().EqualIPs(cellularIpv6[0]),
+ kMediumTimeout, clock);
+
+ DestroyChannels();
+}
+
// Test that when the controlling side switches the selected connection,
// the nomination of the selected connection on the controlled side will
// increase.
@@ -2650,7 +2755,7 @@
// 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;
+ const Connection* conn;
EXPECT_TRUE_WAIT((conn = ep1_ch1()->selected_connection()) != nullptr &&
conn->remote_candidate().address().EqualIPs(wifi[1]),
kDefaultTimeout);
@@ -2662,13 +2767,13 @@
// 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 &&
+ ep1_ch1()->GetState() == 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 &&
+ ep2_ch1()->GetState() == STATE_COMPLETED &&
(conn = GetConnectionWithRemoteAddress(ep2_ch1(), cellular[0])) !=
nullptr &&
conn != ep2_ch1()->selected_connection() && conn->receiving(),
@@ -2805,7 +2910,7 @@
EXPECT_TRUE_SIMULATED_WAIT(
GetConnectionWithLocalAddress(ep1_ch1(), cellular[0]) == nullptr,
kDefaultTimeout, clock);
- const cricket::Connection* conn;
+ const Connection* conn;
EXPECT_TRUE_SIMULATED_WAIT(
(conn = GetConnectionWithLocalAddress(ep1_ch1(), cellular[0])) !=
nullptr &&
@@ -3063,9 +3168,11 @@
SIMULATED_WAIT(conn->num_pings_sent() == ping_sent_before + 1, kMediumTimeout,
clock);
ping_interval_ms = (clock.TimeNanos() - start) / rtc::kNumNanosecsPerMillisec;
- EXPECT_GE(ping_interval_ms, STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL);
- EXPECT_LE(ping_interval_ms,
- STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL + SCHEDULING_RANGE);
+ EXPECT_GE(ping_interval_ms,
+ WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL);
+ EXPECT_LE(
+ ping_interval_ms,
+ WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL + SCHEDULING_RANGE);
// Stabilized.
@@ -3079,9 +3186,11 @@
SIMULATED_WAIT(conn->num_pings_sent() == ping_sent_before + 1, kMediumTimeout,
clock);
ping_interval_ms = (clock.TimeNanos() - start) / rtc::kNumNanosecsPerMillisec;
- EXPECT_GE(ping_interval_ms, STABLE_WRITABLE_CONNECTION_PING_INTERVAL);
- EXPECT_LE(ping_interval_ms,
- STABLE_WRITABLE_CONNECTION_PING_INTERVAL + SCHEDULING_RANGE);
+ EXPECT_GE(ping_interval_ms,
+ STRONG_AND_STABLE_WRITABLE_CONNECTION_PING_INTERVAL);
+ EXPECT_LE(
+ ping_interval_ms,
+ STRONG_AND_STABLE_WRITABLE_CONNECTION_PING_INTERVAL + SCHEDULING_RANGE);
// Destabilized.
@@ -3102,15 +3211,17 @@
SIMULATED_WAIT(conn->num_pings_sent() == ping_sent_before + 1, kMediumTimeout,
clock);
// The interval is expected to be
- // STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL.
+ // WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL.
start = clock.TimeNanos();
ping_sent_before = conn->num_pings_sent();
SIMULATED_WAIT(conn->num_pings_sent() == ping_sent_before + 1, kMediumTimeout,
clock);
ping_interval_ms = (clock.TimeNanos() - start) / rtc::kNumNanosecsPerMillisec;
- EXPECT_GE(ping_interval_ms, STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL);
- EXPECT_LE(ping_interval_ms,
- STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL + SCHEDULING_RANGE);
+ EXPECT_GE(ping_interval_ms,
+ WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL);
+ EXPECT_LE(
+ ping_interval_ms,
+ WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL + SCHEDULING_RANGE);
}
// Test that we start pinging as soon as we have a connection and remote ICE