blob: 81fb324d1f14bbaa8e04c055d86865daa906c158 [file] [log] [blame]
Jonas Oreland09c452e2019-11-20 09:01:02 +01001/*
2 * Copyright 2019 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "p2p/base/basic_ice_controller.h"
12
13namespace {
14
15// The minimum improvement in RTT that justifies a switch.
16const int kMinImprovement = 10;
17
18bool IsRelayRelay(const cricket::Connection* conn) {
19 return conn->local_candidate().type() == cricket::RELAY_PORT_TYPE &&
20 conn->remote_candidate().type() == cricket::RELAY_PORT_TYPE;
21}
22
23bool IsUdp(const cricket::Connection* conn) {
24 return conn->local_candidate().relay_protocol() == cricket::UDP_PROTOCOL_NAME;
25}
26
27// TODO(qingsi) Use an enum to replace the following constants for all
28// comparision results.
29static constexpr int a_is_better = 1;
30static constexpr int b_is_better = -1;
31static constexpr int a_and_b_equal = 0;
32
33bool LocalCandidateUsesPreferredNetwork(
34 const cricket::Connection* conn,
35 absl::optional<rtc::AdapterType> network_preference) {
Jonas Oreland98e745b2019-11-27 11:02:45 +010036 rtc::AdapterType network_type = conn->network()->type();
Jonas Oreland09c452e2019-11-20 09:01:02 +010037 return network_preference.has_value() && (network_type == network_preference);
38}
39
40int CompareCandidatePairsByNetworkPreference(
41 const cricket::Connection* a,
42 const cricket::Connection* b,
43 absl::optional<rtc::AdapterType> network_preference) {
44 bool a_uses_preferred_network =
45 LocalCandidateUsesPreferredNetwork(a, network_preference);
46 bool b_uses_preferred_network =
47 LocalCandidateUsesPreferredNetwork(b, network_preference);
48 if (a_uses_preferred_network && !b_uses_preferred_network) {
49 return a_is_better;
50 } else if (!a_uses_preferred_network && b_uses_preferred_network) {
51 return b_is_better;
52 }
53 return a_and_b_equal;
54}
55
56} // namespace
57
58namespace cricket {
59
Jonas Oreland2f74d5f2019-11-22 07:53:22 +010060BasicIceController::BasicIceController(const IceControllerFactoryArgs& args)
61 : ice_transport_state_func_(args.ice_transport_state_func),
62 ice_role_func_(args.ice_role_func),
63 is_connection_pruned_func_(args.is_connection_pruned_func),
64 field_trials_(args.ice_field_trials) {}
Jonas Oreland09c452e2019-11-20 09:01:02 +010065
66BasicIceController::~BasicIceController() {}
67
68void BasicIceController::SetIceConfig(const IceConfig& config) {
69 config_ = config;
70}
71
72void BasicIceController::SetSelectedConnection(
73 const Connection* selected_connection) {
74 selected_connection_ = selected_connection;
75}
76
77void BasicIceController::AddConnection(const Connection* connection) {
78 connections_.push_back(connection);
79 unpinged_connections_.insert(connection);
80}
81
82void BasicIceController::OnConnectionDestroyed(const Connection* connection) {
83 pinged_connections_.erase(connection);
84 unpinged_connections_.erase(connection);
85 connections_.erase(absl::c_find(connections_, connection));
86}
87
88bool BasicIceController::HasPingableConnection() const {
89 int64_t now = rtc::TimeMillis();
90 return absl::c_any_of(connections_, [this, now](const Connection* c) {
91 return IsPingable(c, now);
92 });
93}
94
Jonas Oreland2f3c0192020-03-26 12:59:44 +010095IceControllerInterface::PingResult BasicIceController::SelectConnectionToPing(
Jonas Oreland09c452e2019-11-20 09:01:02 +010096 int64_t last_ping_sent_ms) {
97 // When the selected connection is not receiving or not writable, or any
98 // active connection has not been pinged enough times, use the weak ping
99 // interval.
100 bool need_more_pings_at_weak_interval =
101 absl::c_any_of(connections_, [](const Connection* conn) {
102 return conn->active() &&
103 conn->num_pings_sent() < MIN_PINGS_AT_WEAK_PING_INTERVAL;
104 });
105 int ping_interval = (weak() || need_more_pings_at_weak_interval)
106 ? weak_ping_interval()
107 : strong_ping_interval();
108
109 const Connection* conn = nullptr;
110 if (rtc::TimeMillis() >= last_ping_sent_ms + ping_interval) {
111 conn = FindNextPingableConnection();
112 }
Jonas Oreland43336002020-03-26 20:59:03 +0100113 PingResult res(conn, std::min(ping_interval, check_receiving_interval()));
114 return res;
Jonas Oreland09c452e2019-11-20 09:01:02 +0100115}
116
117void BasicIceController::MarkConnectionPinged(const Connection* conn) {
118 if (conn && pinged_connections_.insert(conn).second) {
119 unpinged_connections_.erase(conn);
120 }
121}
122
123// Returns the next pingable connection to ping.
124const Connection* BasicIceController::FindNextPingableConnection() {
125 int64_t now = rtc::TimeMillis();
126
127 // Rule 1: Selected connection takes priority over non-selected ones.
128 if (selected_connection_ && selected_connection_->connected() &&
129 selected_connection_->writable() &&
130 WritableConnectionPastPingInterval(selected_connection_, now)) {
131 return selected_connection_;
132 }
133
134 // Rule 2: If the channel is weak, we need to find a new writable and
135 // receiving connection, probably on a different network. If there are lots of
136 // connections, it may take several seconds between two pings for every
137 // non-selected connection. This will cause the receiving state of those
138 // connections to be false, and thus they won't be selected. This is
139 // problematic for network fail-over. We want to make sure at least one
140 // connection per network is pinged frequently enough in order for it to be
141 // selectable. So we prioritize one connection per network.
142 // Rule 2.1: Among such connections, pick the one with the earliest
143 // last-ping-sent time.
144 if (weak()) {
145 std::vector<const Connection*> pingable_selectable_connections;
146 absl::c_copy_if(GetBestWritableConnectionPerNetwork(),
147 std::back_inserter(pingable_selectable_connections),
148 [this, now](const Connection* conn) {
149 return WritableConnectionPastPingInterval(conn, now);
150 });
151 auto iter = absl::c_min_element(
152 pingable_selectable_connections,
153 [](const Connection* conn1, const Connection* conn2) {
154 return conn1->last_ping_sent() < conn2->last_ping_sent();
155 });
156 if (iter != pingable_selectable_connections.end()) {
157 return *iter;
158 }
159 }
160
161 // Rule 3: Triggered checks have priority over non-triggered connections.
162 // Rule 3.1: Among triggered checks, oldest takes precedence.
163 const Connection* oldest_triggered_check =
164 FindOldestConnectionNeedingTriggeredCheck(now);
165 if (oldest_triggered_check) {
166 return oldest_triggered_check;
167 }
168
169 // Rule 4: Unpinged connections have priority over pinged ones.
170 RTC_CHECK(connections_.size() ==
171 pinged_connections_.size() + unpinged_connections_.size());
172 // If there are unpinged and pingable connections, only ping those.
173 // Otherwise, treat everything as unpinged.
174 // TODO(honghaiz): Instead of adding two separate vectors, we can add a state
175 // "pinged" to filter out unpinged connections.
176 if (absl::c_none_of(unpinged_connections_,
177 [this, now](const Connection* conn) {
178 return this->IsPingable(conn, now);
179 })) {
180 unpinged_connections_.insert(pinged_connections_.begin(),
181 pinged_connections_.end());
182 pinged_connections_.clear();
183 }
184
185 // Among un-pinged pingable connections, "more pingable" takes precedence.
186 std::vector<const Connection*> pingable_connections;
187 absl::c_copy_if(
188 unpinged_connections_, std::back_inserter(pingable_connections),
189 [this, now](const Connection* conn) { return IsPingable(conn, now); });
190 auto iter = absl::c_max_element(
191 pingable_connections,
192 [this](const Connection* conn1, const Connection* conn2) {
193 // Some implementations of max_element
194 // compare an element with itself.
195 if (conn1 == conn2) {
196 return false;
197 }
198 return MorePingable(conn1, conn2) == conn2;
199 });
200 if (iter != pingable_connections.end()) {
201 return *iter;
202 }
203 return nullptr;
204}
205
206// Find "triggered checks". We ping first those connections that have
207// received a ping but have not sent a ping since receiving it
208// (last_ping_received > last_ping_sent). But we shouldn't do
209// triggered checks if the connection is already writable.
210const Connection* BasicIceController::FindOldestConnectionNeedingTriggeredCheck(
211 int64_t now) {
212 const Connection* oldest_needing_triggered_check = nullptr;
213 for (auto* conn : connections_) {
214 if (!IsPingable(conn, now)) {
215 continue;
216 }
217 bool needs_triggered_check =
218 (!conn->writable() &&
219 conn->last_ping_received() > conn->last_ping_sent());
220 if (needs_triggered_check &&
221 (!oldest_needing_triggered_check ||
222 (conn->last_ping_received() <
223 oldest_needing_triggered_check->last_ping_received()))) {
224 oldest_needing_triggered_check = conn;
225 }
226 }
227
228 if (oldest_needing_triggered_check) {
229 RTC_LOG(LS_INFO) << "Selecting connection for triggered check: "
230 << oldest_needing_triggered_check->ToString();
231 }
232 return oldest_needing_triggered_check;
233}
234
235bool BasicIceController::WritableConnectionPastPingInterval(
236 const Connection* conn,
237 int64_t now) const {
238 int interval = CalculateActiveWritablePingInterval(conn, now);
239 return conn->last_ping_sent() + interval <= now;
240}
241
242int BasicIceController::CalculateActiveWritablePingInterval(
243 const Connection* conn,
244 int64_t now) const {
245 // Ping each connection at a higher rate at least
246 // MIN_PINGS_AT_WEAK_PING_INTERVAL times.
247 if (conn->num_pings_sent() < MIN_PINGS_AT_WEAK_PING_INTERVAL) {
248 return weak_ping_interval();
249 }
250
251 int stable_interval =
252 config_.stable_writable_connection_ping_interval_or_default();
253 int weak_or_stablizing_interval = std::min(
254 stable_interval, WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL);
255 // If the channel is weak or the connection is not stable yet, use the
256 // weak_or_stablizing_interval.
257 return (!weak() && conn->stable(now)) ? stable_interval
258 : weak_or_stablizing_interval;
259}
260
261// Is the connection in a state for us to even consider pinging the other side?
262// We consider a connection pingable even if it's not connected because that's
263// how a TCP connection is kicked into reconnecting on the active side.
264bool BasicIceController::IsPingable(const Connection* conn, int64_t now) const {
265 const Candidate& remote = conn->remote_candidate();
266 // We should never get this far with an empty remote ufrag.
267 RTC_DCHECK(!remote.username().empty());
268 if (remote.username().empty() || remote.password().empty()) {
269 // If we don't have an ICE ufrag and pwd, there's no way we can ping.
270 return false;
271 }
272
273 // A failed connection will not be pinged.
274 if (conn->state() == IceCandidatePairState::FAILED) {
275 return false;
276 }
277
278 // An never connected connection cannot be written to at all, so pinging is
279 // out of the question. However, if it has become WRITABLE, it is in the
280 // reconnecting state so ping is needed.
281 if (!conn->connected() && !conn->writable()) {
282 return false;
283 }
284
285 // If we sent a number of pings wo/ reply, skip sending more
286 // until we get one.
287 if (conn->TooManyOutstandingPings(field_trials_->max_outstanding_pings)) {
288 return false;
289 }
290
291 // If the channel is weakly connected, ping all connections.
292 if (weak()) {
293 return true;
294 }
295
296 // Always ping active connections regardless whether the channel is completed
297 // or not, but backup connections are pinged at a slower rate.
298 if (IsBackupConnection(conn)) {
299 return conn->rtt_samples() == 0 ||
300 (now >= conn->last_ping_response_received() +
301 config_.backup_connection_ping_interval_or_default());
302 }
303 // Don't ping inactive non-backup connections.
304 if (!conn->active()) {
305 return false;
306 }
307
308 // Do ping unwritable, active connections.
309 if (!conn->writable()) {
310 return true;
311 }
312
313 // Ping writable, active connections if it's been long enough since the last
314 // ping.
315 return WritableConnectionPastPingInterval(conn, now);
316}
317
318// A connection is considered a backup connection if the channel state
319// is completed, the connection is not the selected connection and it is active.
320bool BasicIceController::IsBackupConnection(const Connection* conn) const {
321 return ice_transport_state_func_() == IceTransportState::STATE_COMPLETED &&
322 conn != selected_connection_ && conn->active();
323}
324
325const Connection* BasicIceController::MorePingable(const Connection* conn1,
326 const Connection* conn2) {
327 RTC_DCHECK(conn1 != conn2);
328 if (config_.prioritize_most_likely_candidate_pairs) {
329 const Connection* most_likely_to_work_conn = MostLikelyToWork(conn1, conn2);
330 if (most_likely_to_work_conn) {
331 return most_likely_to_work_conn;
332 }
333 }
334
335 const Connection* least_recently_pinged_conn =
336 LeastRecentlyPinged(conn1, conn2);
337 if (least_recently_pinged_conn) {
338 return least_recently_pinged_conn;
339 }
340
341 // During the initial state when nothing has been pinged yet, return the first
Artem Titov2dbb4c92021-07-26 15:12:41 +0200342 // one in the ordered `connections_`.
Jonas Oreland09c452e2019-11-20 09:01:02 +0100343 auto connections = connections_;
344 return *(std::find_if(connections.begin(), connections.end(),
345 [conn1, conn2](const Connection* conn) {
346 return conn == conn1 || conn == conn2;
347 }));
348}
349
350const Connection* BasicIceController::MostLikelyToWork(
351 const Connection* conn1,
352 const Connection* conn2) {
353 bool rr1 = IsRelayRelay(conn1);
354 bool rr2 = IsRelayRelay(conn2);
355 if (rr1 && !rr2) {
356 return conn1;
357 } else if (rr2 && !rr1) {
358 return conn2;
359 } else if (rr1 && rr2) {
360 bool udp1 = IsUdp(conn1);
361 bool udp2 = IsUdp(conn2);
362 if (udp1 && !udp2) {
363 return conn1;
364 } else if (udp2 && udp1) {
365 return conn2;
366 }
367 }
368 return nullptr;
369}
370
371const Connection* BasicIceController::LeastRecentlyPinged(
372 const Connection* conn1,
373 const Connection* conn2) {
374 if (conn1->last_ping_sent() < conn2->last_ping_sent()) {
375 return conn1;
376 }
377 if (conn1->last_ping_sent() > conn2->last_ping_sent()) {
378 return conn2;
379 }
380 return nullptr;
381}
382
Jonas Oreland98e745b2019-11-27 11:02:45 +0100383std::map<const rtc::Network*, const Connection*>
Jonas Oreland09c452e2019-11-20 09:01:02 +0100384BasicIceController::GetBestConnectionByNetwork() const {
Artem Titov2dbb4c92021-07-26 15:12:41 +0200385 // `connections_` has been sorted, so the first one in the list on a given
Jonas Oreland09c452e2019-11-20 09:01:02 +0100386 // network is the best connection on the network, except that the selected
387 // connection is always the best connection on the network.
Jonas Oreland98e745b2019-11-27 11:02:45 +0100388 std::map<const rtc::Network*, const Connection*> best_connection_by_network;
Jonas Oreland09c452e2019-11-20 09:01:02 +0100389 if (selected_connection_) {
Jonas Oreland98e745b2019-11-27 11:02:45 +0100390 best_connection_by_network[selected_connection_->network()] =
Jonas Oreland09c452e2019-11-20 09:01:02 +0100391 selected_connection_;
392 }
Artem Titov2dbb4c92021-07-26 15:12:41 +0200393 // TODO(honghaiz): Need to update this if `connections_` are not sorted.
Jonas Oreland09c452e2019-11-20 09:01:02 +0100394 for (const Connection* conn : connections_) {
Jonas Oreland98e745b2019-11-27 11:02:45 +0100395 const rtc::Network* network = conn->network();
Jonas Oreland09c452e2019-11-20 09:01:02 +0100396 // This only inserts when the network does not exist in the map.
397 best_connection_by_network.insert(std::make_pair(network, conn));
398 }
399 return best_connection_by_network;
400}
401
402std::vector<const Connection*>
403BasicIceController::GetBestWritableConnectionPerNetwork() const {
404 std::vector<const Connection*> connections;
405 for (auto kv : GetBestConnectionByNetwork()) {
406 const Connection* conn = kv.second;
407 if (conn->writable() && conn->connected()) {
408 connections.push_back(conn);
409 }
410 }
411 return connections;
412}
413
414IceControllerInterface::SwitchResult
415BasicIceController::HandleInitialSelectDampening(
416 IceControllerEvent reason,
417 const Connection* new_connection) {
418 if (!field_trials_->initial_select_dampening.has_value() &&
419 !field_trials_->initial_select_dampening_ping_received.has_value()) {
420 // experiment not enabled => select connection.
421 return {new_connection, absl::nullopt};
422 }
423
424 int64_t now = rtc::TimeMillis();
425 int64_t max_delay = 0;
426 if (new_connection->last_ping_received() > 0 &&
427 field_trials_->initial_select_dampening_ping_received.has_value()) {
428 max_delay = *field_trials_->initial_select_dampening_ping_received;
429 } else if (field_trials_->initial_select_dampening.has_value()) {
430 max_delay = *field_trials_->initial_select_dampening;
431 }
432
433 int64_t start_wait =
434 initial_select_timestamp_ms_ == 0 ? now : initial_select_timestamp_ms_;
435 int64_t max_wait_until = start_wait + max_delay;
436
437 if (now >= max_wait_until) {
438 RTC_LOG(LS_INFO) << "reset initial_select_timestamp_ = "
439 << initial_select_timestamp_ms_
440 << " selection delayed by: " << (now - start_wait) << "ms";
441 initial_select_timestamp_ms_ = 0;
442 return {new_connection, absl::nullopt};
443 }
444
445 // We are not yet ready to select first connection...
446 if (initial_select_timestamp_ms_ == 0) {
447 // Set timestamp on first time...
448 // but run the delayed invokation everytime to
449 // avoid possibility that we miss it.
450 initial_select_timestamp_ms_ = now;
451 RTC_LOG(LS_INFO) << "set initial_select_timestamp_ms_ = "
452 << initial_select_timestamp_ms_;
453 }
454
455 int min_delay = max_delay;
456 if (field_trials_->initial_select_dampening.has_value()) {
457 min_delay = std::min(min_delay, *field_trials_->initial_select_dampening);
458 }
459 if (field_trials_->initial_select_dampening_ping_received.has_value()) {
460 min_delay = std::min(
461 min_delay, *field_trials_->initial_select_dampening_ping_received);
462 }
463
464 RTC_LOG(LS_INFO) << "delay initial selection up to " << min_delay << "ms";
Jonas Orelandb5aa0a82019-12-03 09:59:11 +0100465 reason.type = IceControllerEvent::ICE_CONTROLLER_RECHECK;
466 reason.recheck_delay_ms = min_delay;
467 return {absl::nullopt, reason};
Jonas Oreland09c452e2019-11-20 09:01:02 +0100468}
469
470IceControllerInterface::SwitchResult BasicIceController::ShouldSwitchConnection(
471 IceControllerEvent reason,
472 const Connection* new_connection) {
473 if (!ReadyToSend(new_connection) || selected_connection_ == new_connection) {
474 return {absl::nullopt, absl::nullopt};
475 }
476
477 if (selected_connection_ == nullptr) {
478 return HandleInitialSelectDampening(reason, new_connection);
479 }
480
481 // Do not switch to a connection that is not receiving if it is not on a
482 // preferred network or it has higher cost because it may be just spuriously
483 // better.
484 int compare_a_b_by_networks = CompareCandidatePairNetworks(
485 new_connection, selected_connection_, config_.network_preference);
486 if (compare_a_b_by_networks == b_is_better && !new_connection->receiving()) {
487 return {absl::nullopt, absl::nullopt};
488 }
489
490 bool missed_receiving_unchanged_threshold = false;
491 absl::optional<int64_t> receiving_unchanged_threshold(
492 rtc::TimeMillis() - config_.receiving_switching_delay_or_default());
493 int cmp = CompareConnections(selected_connection_, new_connection,
494 receiving_unchanged_threshold,
495 &missed_receiving_unchanged_threshold);
496
Jonas Orelandb5aa0a82019-12-03 09:59:11 +0100497 absl::optional<IceControllerEvent> recheck_event;
Jonas Oreland09c452e2019-11-20 09:01:02 +0100498 if (missed_receiving_unchanged_threshold &&
499 config_.receiving_switching_delay_or_default()) {
500 // If we do not switch to the connection because it missed the receiving
501 // threshold, the new connection is in a better receiving state than the
502 // currently selected connection. So we need to re-check whether it needs
503 // to be switched at a later time.
Jonas Orelandb5aa0a82019-12-03 09:59:11 +0100504 recheck_event = reason;
505 recheck_event->recheck_delay_ms =
506 config_.receiving_switching_delay_or_default();
Jonas Oreland09c452e2019-11-20 09:01:02 +0100507 }
508
509 if (cmp < 0) {
510 return {new_connection, absl::nullopt};
511 } else if (cmp > 0) {
Jonas Orelandb5aa0a82019-12-03 09:59:11 +0100512 return {absl::nullopt, recheck_event};
Jonas Oreland09c452e2019-11-20 09:01:02 +0100513 }
514
515 // If everything else is the same, switch only if rtt has improved by
516 // a margin.
517 if (new_connection->rtt() <= selected_connection_->rtt() - kMinImprovement) {
518 return {new_connection, absl::nullopt};
519 }
520
Jonas Orelandb5aa0a82019-12-03 09:59:11 +0100521 return {absl::nullopt, recheck_event};
Jonas Oreland09c452e2019-11-20 09:01:02 +0100522}
523
524IceControllerInterface::SwitchResult
525BasicIceController::SortAndSwitchConnection(IceControllerEvent reason) {
526 // Find the best alternative connection by sorting. It is important to note
527 // that amongst equal preference, writable connections, this will choose the
528 // one whose estimated latency is lowest. So it is the only one that we
529 // need to consider switching to.
530 // TODO(honghaiz): Don't sort; Just use std::max_element in the right places.
531 absl::c_stable_sort(
532 connections_, [this](const Connection* a, const Connection* b) {
533 int cmp = CompareConnections(a, b, absl::nullopt, nullptr);
534 if (cmp != 0) {
535 return cmp > 0;
536 }
537 // Otherwise, sort based on latency estimate.
538 return a->rtt() < b->rtt();
539 });
540
541 RTC_LOG(LS_VERBOSE) << "Sorting " << connections_.size()
542 << " available connections";
543 for (size_t i = 0; i < connections_.size(); ++i) {
544 RTC_LOG(LS_VERBOSE) << connections_[i]->ToString();
545 }
546
547 const Connection* top_connection =
548 (!connections_.empty()) ? connections_[0] : nullptr;
549
550 return ShouldSwitchConnection(reason, top_connection);
551}
552
553bool BasicIceController::ReadyToSend(const Connection* connection) const {
554 // Note that we allow sending on an unreliable connection, because it's
555 // possible that it became unreliable simply due to bad chance.
556 // So this shouldn't prevent attempting to send media.
557 return connection != nullptr &&
558 (connection->writable() ||
559 connection->write_state() == Connection::STATE_WRITE_UNRELIABLE ||
560 PresumedWritable(connection));
561}
562
563bool BasicIceController::PresumedWritable(const Connection* conn) const {
564 return (conn->write_state() == Connection::STATE_WRITE_INIT &&
565 config_.presume_writable_when_fully_relayed &&
566 conn->local_candidate().type() == RELAY_PORT_TYPE &&
567 (conn->remote_candidate().type() == RELAY_PORT_TYPE ||
568 conn->remote_candidate().type() == PRFLX_PORT_TYPE));
569}
570
571// Compare two connections based on their writing, receiving, and connected
572// states.
573int BasicIceController::CompareConnectionStates(
574 const Connection* a,
575 const Connection* b,
576 absl::optional<int64_t> receiving_unchanged_threshold,
577 bool* missed_receiving_unchanged_threshold) const {
578 // First, prefer a connection that's writable or presumed writable over
579 // one that's not writable.
580 bool a_writable = a->writable() || PresumedWritable(a);
581 bool b_writable = b->writable() || PresumedWritable(b);
582 if (a_writable && !b_writable) {
583 return a_is_better;
584 }
585 if (!a_writable && b_writable) {
586 return b_is_better;
587 }
588
589 // Sort based on write-state. Better states have lower values.
590 if (a->write_state() < b->write_state()) {
591 return a_is_better;
592 }
593 if (b->write_state() < a->write_state()) {
594 return b_is_better;
595 }
596
597 // We prefer a receiving connection to a non-receiving, higher-priority
598 // connection when sorting connections and choosing which connection to
599 // switch to.
600 if (a->receiving() && !b->receiving()) {
601 return a_is_better;
602 }
603 if (!a->receiving() && b->receiving()) {
604 if (!receiving_unchanged_threshold ||
605 (a->receiving_unchanged_since() <= *receiving_unchanged_threshold &&
606 b->receiving_unchanged_since() <= *receiving_unchanged_threshold)) {
607 return b_is_better;
608 }
609 *missed_receiving_unchanged_threshold = true;
610 }
611
612 // WARNING: Some complexity here about TCP reconnecting.
613 // When a TCP connection fails because of a TCP socket disconnecting, the
614 // active side of the connection will attempt to reconnect for 5 seconds while
615 // pretending to be writable (the connection is not set to the unwritable
616 // state). On the passive side, the connection also remains writable even
617 // though it is disconnected, and a new connection is created when the active
618 // side connects. At that point, there are two TCP connections on the passive
619 // side: 1. the old, disconnected one that is pretending to be writable, and
620 // 2. the new, connected one that is maybe not yet writable. For purposes of
621 // pruning, pinging, and selecting the selected connection, we want to treat
622 // the new connection as "better" than the old one. We could add a method
623 // called something like Connection::ImReallyBadEvenThoughImWritable, but that
624 // is equivalent to the existing Connection::connected(), which we already
625 // have. So, in code throughout this file, we'll check whether the connection
626 // is connected() or not, and if it is not, treat it as "worse" than a
627 // connected one, even though it's writable. In the code below, we're doing
628 // so to make sure we treat a new writable connection as better than an old
629 // disconnected connection.
630
631 // In the case where we reconnect TCP connections, the original best
632 // connection is disconnected without changing to WRITE_TIMEOUT. In this case,
633 // the new connection, when it becomes writable, should have higher priority.
634 if (a->write_state() == Connection::STATE_WRITABLE &&
635 b->write_state() == Connection::STATE_WRITABLE) {
636 if (a->connected() && !b->connected()) {
637 return a_is_better;
638 }
639 if (!a->connected() && b->connected()) {
640 return b_is_better;
641 }
642 }
643
644 return 0;
645}
646
647// Compares two connections based only on the candidate and network information.
Artem Titov2dbb4c92021-07-26 15:12:41 +0200648// Returns positive if `a` is better than `b`.
Jonas Oreland09c452e2019-11-20 09:01:02 +0100649int BasicIceController::CompareConnectionCandidates(const Connection* a,
650 const Connection* b) const {
651 int compare_a_b_by_networks =
652 CompareCandidatePairNetworks(a, b, config_.network_preference);
653 if (compare_a_b_by_networks != a_and_b_equal) {
654 return compare_a_b_by_networks;
655 }
656
657 // Compare connection priority. Lower values get sorted last.
658 if (a->priority() > b->priority()) {
659 return a_is_better;
660 }
661 if (a->priority() < b->priority()) {
662 return b_is_better;
663 }
664
665 // If we're still tied at this point, prefer a younger generation.
666 // (Younger generation means a larger generation number).
Jonas Oreland98e745b2019-11-27 11:02:45 +0100667 int cmp = (a->remote_candidate().generation() + a->generation()) -
668 (b->remote_candidate().generation() + b->generation());
Jonas Oreland09c452e2019-11-20 09:01:02 +0100669 if (cmp != 0) {
670 return cmp;
671 }
672
673 // A periodic regather (triggered by the regather_all_networks_interval_range)
674 // will produce candidates that appear the same but would use a new port. We
675 // want to use the new candidates and purge the old candidates as they come
676 // in, so use the fact that the old ports get pruned immediately to rank the
677 // candidates with an active port/remote candidate higher.
678 bool a_pruned = is_connection_pruned_func_(a);
679 bool b_pruned = is_connection_pruned_func_(b);
680 if (!a_pruned && b_pruned) {
681 return a_is_better;
682 }
683 if (a_pruned && !b_pruned) {
684 return b_is_better;
685 }
686
687 // Otherwise, must be equal
688 return 0;
689}
690
691int BasicIceController::CompareConnections(
692 const Connection* a,
693 const Connection* b,
694 absl::optional<int64_t> receiving_unchanged_threshold,
695 bool* missed_receiving_unchanged_threshold) const {
696 RTC_CHECK(a != nullptr);
697 RTC_CHECK(b != nullptr);
698
699 // We prefer to switch to a writable and receiving connection over a
700 // non-writable or non-receiving connection, even if the latter has
701 // been nominated by the controlling side.
702 int state_cmp = CompareConnectionStates(a, b, receiving_unchanged_threshold,
703 missed_receiving_unchanged_threshold);
704 if (state_cmp != 0) {
705 return state_cmp;
706 }
707
708 if (ice_role_func_() == ICEROLE_CONTROLLED) {
709 // Compare the connections based on the nomination states and the last data
710 // received time if this is on the controlled side.
711 if (a->remote_nomination() > b->remote_nomination()) {
712 return a_is_better;
713 }
714 if (a->remote_nomination() < b->remote_nomination()) {
715 return b_is_better;
716 }
717
718 if (a->last_data_received() > b->last_data_received()) {
719 return a_is_better;
720 }
721 if (a->last_data_received() < b->last_data_received()) {
722 return b_is_better;
723 }
724 }
725
726 // Compare the network cost and priority.
727 return CompareConnectionCandidates(a, b);
728}
729
730int BasicIceController::CompareCandidatePairNetworks(
731 const Connection* a,
732 const Connection* b,
733 absl::optional<rtc::AdapterType> network_preference) const {
734 int compare_a_b_by_network_preference =
735 CompareCandidatePairsByNetworkPreference(a, b,
736 config_.network_preference);
737 // The network preference has a higher precedence than the network cost.
738 if (compare_a_b_by_network_preference != a_and_b_equal) {
739 return compare_a_b_by_network_preference;
740 }
741
Jonas Orelandc8fa1ee2021-08-25 08:58:04 +0200742 bool a_vpn = a->network()->IsVpn();
743 bool b_vpn = b->network()->IsVpn();
744 switch (config_.vpn_preference) {
745 case webrtc::VpnPreference::kDefault:
746 break;
747 case webrtc::VpnPreference::kOnlyUseVpn:
748 case webrtc::VpnPreference::kPreferVpn:
749 if (a_vpn && !b_vpn) {
750 return a_is_better;
751 } else if (!a_vpn && b_vpn) {
752 return b_is_better;
753 }
754 break;
755 case webrtc::VpnPreference::kNeverUseVpn:
756 case webrtc::VpnPreference::kAvoidVpn:
757 if (a_vpn && !b_vpn) {
758 return b_is_better;
759 } else if (!a_vpn && b_vpn) {
760 return a_is_better;
761 }
762 break;
763 default:
764 break;
765 }
766
Jonas Oreland09c452e2019-11-20 09:01:02 +0100767 uint32_t a_cost = a->ComputeNetworkCost();
768 uint32_t b_cost = b->ComputeNetworkCost();
769 // Prefer lower network cost.
770 if (a_cost < b_cost) {
771 return a_is_better;
772 }
773 if (a_cost > b_cost) {
774 return b_is_better;
775 }
776 return a_and_b_equal;
777}
778
779std::vector<const Connection*> BasicIceController::PruneConnections() {
780 // We can prune any connection for which there is a connected, writable
781 // connection on the same network with better or equal priority. We leave
782 // those with better priority just in case they become writable later (at
783 // which point, we would prune out the current selected connection). We leave
784 // connections on other networks because they may not be using the same
785 // resources and they may represent very distinct paths over which we can
Artem Titov2dbb4c92021-07-26 15:12:41 +0200786 // switch. If `best_conn_on_network` is not connected, we may be reconnecting
Jonas Oreland09c452e2019-11-20 09:01:02 +0100787 // a TCP connection and should not prune connections in this network.
788 // See the big comment in CompareConnectionStates.
789 //
790 // An exception is made for connections on an "any address" network, meaning
791 // not bound to any specific network interface. We don't want to keep one of
792 // these alive as a backup, since it could be using the same network
793 // interface as the higher-priority, selected candidate pair.
794 std::vector<const Connection*> connections_to_prune;
795 auto best_connection_by_network = GetBestConnectionByNetwork();
796 for (const Connection* conn : connections_) {
797 const Connection* best_conn = selected_connection_;
Jonas Oreland98e745b2019-11-27 11:02:45 +0100798 if (!rtc::IPIsAny(conn->network()->ip())) {
Jonas Oreland09c452e2019-11-20 09:01:02 +0100799 // If the connection is bound to a specific network interface (not an
800 // "any address" network), compare it against the best connection for
801 // that network interface rather than the best connection overall. This
802 // ensures that at least one connection per network will be left
803 // unpruned.
Jonas Oreland98e745b2019-11-27 11:02:45 +0100804 best_conn = best_connection_by_network[conn->network()];
Jonas Oreland09c452e2019-11-20 09:01:02 +0100805 }
806 // Do not prune connections if the connection being compared against is
807 // weak. Otherwise, it may delete connections prematurely.
808 if (best_conn && conn != best_conn && !best_conn->weak() &&
809 CompareConnectionCandidates(best_conn, conn) >= 0) {
810 connections_to_prune.push_back(conn);
811 }
812 }
813 return connections_to_prune;
814}
815
816bool BasicIceController::GetUseCandidateAttr(const Connection* conn,
817 NominationMode mode,
818 IceMode remote_ice_mode) const {
819 switch (mode) {
820 case NominationMode::REGULAR:
821 // TODO(honghaiz): Implement regular nomination.
822 return false;
823 case NominationMode::AGGRESSIVE:
824 if (remote_ice_mode == ICEMODE_LITE) {
825 return GetUseCandidateAttr(conn, NominationMode::REGULAR,
826 remote_ice_mode);
827 }
828 return true;
829 case NominationMode::SEMI_AGGRESSIVE: {
830 // Nominate if
831 // a) Remote is in FULL ICE AND
Artem Titov2dbb4c92021-07-26 15:12:41 +0200832 // a.1) `conn` is the selected connection OR
Jonas Oreland09c452e2019-11-20 09:01:02 +0100833 // a.2) there is no selected connection OR
834 // a.3) the selected connection is unwritable OR
Artem Titov2dbb4c92021-07-26 15:12:41 +0200835 // a.4) `conn` has higher priority than selected_connection.
Jonas Oreland09c452e2019-11-20 09:01:02 +0100836 // b) Remote is in LITE ICE AND
Artem Titov2dbb4c92021-07-26 15:12:41 +0200837 // b.1) `conn` is the selected_connection AND
838 // b.2) `conn` is writable.
Jonas Oreland09c452e2019-11-20 09:01:02 +0100839 bool selected = conn == selected_connection_;
840 if (remote_ice_mode == ICEMODE_LITE) {
841 return selected && conn->writable();
842 }
843 bool better_than_selected =
844 !selected_connection_ || !selected_connection_->writable() ||
845 CompareConnectionCandidates(selected_connection_, conn) < 0;
846 return selected || better_than_selected;
847 }
848 default:
Artem Titovd3251962021-11-15 16:57:07 +0100849 RTC_DCHECK_NOTREACHED();
Jonas Oreland09c452e2019-11-20 09:01:02 +0100850 return false;
851 }
852}
853
854} // namespace cricket