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