1//
2// Copyright (C) 2012 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "shill/connection.h"
18
19#include <arpa/inet.h>
20#include <linux/rtnetlink.h>
21
22#include <set>
23
24#include <base/strings/stringprintf.h>
25
26#include "shill/control_interface.h"
27#include "shill/device_info.h"
28#include "shill/firewall_proxy_interface.h"
29#include "shill/logging.h"
30#include "shill/net/rtnl_handler.h"
31#include "shill/routing_table.h"
32
33#if !defined(__ANDROID__)
34#include "shill/resolver.h"
35#else
36#include "shill/dns_server_proxy.h"
37#include "shill/dns_server_proxy_factory.h"
38#endif  // __ANDROID__
39
40using base::Bind;
41using base::Closure;
42using base::Unretained;
43using std::deque;
44using std::set;
45using std::string;
46using std::vector;
47
48namespace shill {
49
50namespace Logging {
51static auto kModuleLogScope = ScopeLogger::kConnection;
52static string ObjectID(Connection* c) {
53  if (c == nullptr)
54    return "(connection)";
55  return c->interface_name();
56}
57}
58
59#if defined(__ANDROID__)
60namespace {
61const char* kGoogleDNSServers[] = {
62    "8.8.4.4",
63    "8.8.8.8"
64};
65}  // namespace
66#endif  // __ANDROID__
67
68// static
69const uint32_t Connection::kDefaultMetric = 1;
70// static
71const uint32_t Connection::kNonDefaultMetricBase = 10;
72// static
73const uint32_t Connection::kMarkForUserTraffic = 0x1;
74// static
75const uint8_t Connection::kSecondaryTableId = 0x1;
76
77Connection::Binder::Binder(const string& name,
78                           const Closure& disconnect_callback)
79    : name_(name),
80      client_disconnect_callback_(disconnect_callback) {}
81
82Connection::Binder::~Binder() {
83  Attach(nullptr);
84}
85
86void Connection::Binder::Attach(const ConnectionRefPtr& to_connection) {
87  if (connection_) {
88    connection_->DetachBinder(this);
89    LOG(INFO) << name_ << ": unbound from connection: "
90              << connection_->interface_name();
91    connection_.reset();
92  }
93  if (to_connection) {
94    connection_ = to_connection->weak_ptr_factory_.GetWeakPtr();
95    connection_->AttachBinder(this);
96    LOG(INFO) << name_ << ": bound to connection: "
97              << connection_->interface_name();
98  }
99}
100
101void Connection::Binder::OnDisconnect() {
102  LOG(INFO) << name_ << ": bound connection disconnected: "
103            << connection_->interface_name();
104  connection_.reset();
105  if (!client_disconnect_callback_.is_null()) {
106    SLOG(connection_.get(), 2) << "Running client disconnect callback.";
107    client_disconnect_callback_.Run();
108  }
109}
110
111Connection::Connection(int interface_index,
112                       const std::string& interface_name,
113                       Technology::Identifier technology,
114                       const DeviceInfo* device_info,
115                       ControlInterface* control_interface)
116    : weak_ptr_factory_(this),
117      is_default_(false),
118      has_broadcast_domain_(false),
119      routing_request_count_(0),
120      interface_index_(interface_index),
121      interface_name_(interface_name),
122      technology_(technology),
123      user_traffic_only_(false),
124      table_id_(RT_TABLE_MAIN),
125      local_(IPAddress::kFamilyUnknown),
126      gateway_(IPAddress::kFamilyUnknown),
127      lower_binder_(
128          interface_name_,
129          // Connection owns a single instance of |lower_binder_| so it's safe
130          // to use an Unretained callback.
131          Bind(&Connection::OnLowerDisconnect, Unretained(this))),
132      device_info_(device_info),
133#if !defined(__ANDROID__)
134      resolver_(Resolver::GetInstance()),
135#else
136      dns_server_proxy_factory_(DNSServerProxyFactory::GetInstance()),
137#endif  // __ANDROID__
138      routing_table_(RoutingTable::GetInstance()),
139      rtnl_handler_(RTNLHandler::GetInstance()),
140      control_interface_(control_interface) {
141  SLOG(this, 2) << __func__ << "(" << interface_index << ", "
142                << interface_name << ", "
143                << Technology::NameFromIdentifier(technology) << ")";
144}
145
146Connection::~Connection() {
147  SLOG(this, 2) << __func__ << " " << interface_name_;
148
149  NotifyBindersOnDisconnect();
150
151  DCHECK(!routing_request_count_);
152  routing_table_->FlushRoutes(interface_index_);
153  routing_table_->FlushRoutesWithTag(interface_index_);
154  device_info_->FlushAddresses(interface_index_);
155  TearDownIptableEntries();
156}
157
158void Connection::UpdateFromIPConfig(const IPConfigRefPtr& config) {
159  SLOG(this, 2) << __func__ << " " << interface_name_;
160
161  const IPConfig::Properties& properties = config->properties();
162  user_traffic_only_ = properties.user_traffic_only;
163  table_id_ = user_traffic_only_ ? kSecondaryTableId : (uint8_t)RT_TABLE_MAIN;
164
165  IPAddress gateway(properties.address_family);
166  if (!properties.gateway.empty() &&
167      !gateway.SetAddressFromString(properties.gateway)) {
168    LOG(ERROR) << "Gateway address " << properties.gateway << " is invalid";
169    return;
170  }
171
172  excluded_ips_cidr_ = properties.exclusion_list;
173
174  IPAddress trusted_ip(properties.address_family);
175  if (!excluded_ips_cidr_.empty()) {
176    const std::string first_excluded_ip = excluded_ips_cidr_[0];
177    excluded_ips_cidr_.erase(excluded_ips_cidr_.begin());
178    // A VPN connection can currently be bound to exactly one lower connection
179    // such as eth0 or wan0. The excluded IPs are pinned to the gateway of
180    // that connection. Setting up the routing table this way ensures that when
181    // the lower connection goes offline, the associated entries in the routing
182    // table are removed. On the flip side, when there are multiple connections
183    // such as eth0 and wan0 and some IPs can be reached quickly over one
184    // connection and the others over a different connection, all routes are
185    // still pinned to a connection.
186    //
187    // The optimal connection to reach the first excluded IP is found below.
188    // When this is found the route for the remaining excluded IPs are pinned in
189    // the method PinPendingRoutes below.
190    if (!trusted_ip.SetAddressAndPrefixFromString(first_excluded_ip)) {
191      LOG(ERROR) << "Trusted IP address "
192                 << first_excluded_ip << " is invalid";
193      return;
194    }
195    if (!PinHostRoute(trusted_ip, gateway)) {
196      LOG(ERROR) << "Unable to pin host route to " << first_excluded_ip;
197      return;
198    }
199  }
200
201  IPAddress local(properties.address_family);
202  if (!local.SetAddressFromString(properties.address)) {
203    LOG(ERROR) << "Local address " << properties.address << " is invalid";
204    return;
205  }
206  local.set_prefix(properties.subnet_prefix);
207
208  IPAddress broadcast(properties.address_family);
209  if (properties.broadcast_address.empty()) {
210    if (properties.peer_address.empty()) {
211      LOG(WARNING) << "Broadcast address is not set.  Using default.";
212      broadcast = local.GetDefaultBroadcast();
213    }
214  } else if (!broadcast.SetAddressFromString(properties.broadcast_address)) {
215    LOG(ERROR) << "Broadcast address " << properties.broadcast_address
216               << " is invalid";
217    return;
218  }
219
220  IPAddress peer(properties.address_family);
221  if (!properties.peer_address.empty() &&
222      !peer.SetAddressFromString(properties.peer_address)) {
223    LOG(ERROR) << "Peer address " << properties.peer_address
224               << " is invalid";
225    return;
226  }
227
228  if (!FixGatewayReachability(local, &peer, &gateway, trusted_ip)) {
229    LOG(WARNING) << "Expect limited network connectivity.";
230  }
231
232  if (device_info_->HasOtherAddress(interface_index_, local)) {
233    // The address has changed for this interface.  We need to flush
234    // everything and start over.
235    LOG(INFO) << __func__ << ": Flushing old addresses and routes.";
236    routing_table_->FlushRoutes(interface_index_);
237    device_info_->FlushAddresses(interface_index_);
238  }
239
240  LOG(INFO) << __func__ << ": Installing with parameters:"
241            << " local=" << local.ToString()
242            << " broadcast=" << broadcast.ToString()
243            << " peer=" << peer.ToString()
244            << " gateway=" << gateway.ToString();
245  rtnl_handler_->AddInterfaceAddress(interface_index_, local, broadcast, peer);
246
247  if (gateway.IsValid() && properties.default_route) {
248    routing_table_->SetDefaultRoute(interface_index_, gateway,
249                                    GetMetric(is_default_),
250                                    table_id_);
251  }
252
253  if (user_traffic_only_) {
254    SetupIptableEntries();
255  }
256
257  // Install any explicitly configured routes at the default metric.
258  routing_table_->ConfigureRoutes(interface_index_, config, kDefaultMetric,
259                                  table_id_);
260
261  SetMTU(properties.mtu);
262
263  if (properties.blackhole_ipv6) {
264    routing_table_->CreateBlackholeRoute(interface_index_,
265                                         IPAddress::kFamilyIPv6,
266                                         kDefaultMetric,
267                                         table_id_);
268  }
269
270  // Save a copy of the last non-null DNS config.
271  if (!config->properties().dns_servers.empty()) {
272    dns_servers_ = config->properties().dns_servers;
273  }
274
275#if defined(__ANDROID__)
276  // Default to Google's DNS server if it is not provided through DHCP.
277  if (config->properties().dns_servers.empty()) {
278    LOG(INFO) << "Default to use Google DNS servers";
279    dns_servers_ =
280        vector<string>(std::begin(kGoogleDNSServers),
281                       std::end(kGoogleDNSServers));
282  }
283#endif  // __ANDROID__
284
285  if (!config->properties().domain_search.empty()) {
286    dns_domain_search_ = config->properties().domain_search;
287  }
288
289  if (!config->properties().domain_name.empty()) {
290    dns_domain_name_ = config->properties().domain_name;
291  }
292
293  ipconfig_rpc_identifier_ = config->GetRpcIdentifier();
294
295  PushDNSConfig();
296
297  local_ = local;
298  gateway_ = gateway;
299  has_broadcast_domain_ = !peer.IsValid();
300}
301
302bool Connection::SetupIptableEntries() {
303  if (!firewall_proxy_) {
304    firewall_proxy_.reset(control_interface_->CreateFirewallProxy());
305  }
306
307  std::vector<std::string> user_names;
308  user_names.push_back("chronos");
309  user_names.push_back("debugd");
310
311  if (!firewall_proxy_->RequestVpnSetup(user_names, interface_name_)) {
312    LOG(ERROR) << "VPN iptables setup request failed.";
313    return false;
314  }
315
316  return true;
317}
318
319bool Connection::TearDownIptableEntries() {
320  return firewall_proxy_ ? firewall_proxy_->RemoveVpnSetup() : true;
321}
322
323void Connection::SetIsDefault(bool is_default) {
324  SLOG(this, 2) << __func__ << " " << interface_name_
325                << " (index " << interface_index_ << ") "
326                << is_default_ << " -> " << is_default;
327  if (is_default == is_default_) {
328    return;
329  }
330
331  routing_table_->SetDefaultMetric(interface_index_, GetMetric(is_default));
332
333  is_default_ = is_default;
334
335  PushDNSConfig();
336  if (is_default) {
337    DeviceRefPtr device = device_info_->GetDevice(interface_index_);
338    if (device) {
339      device->RequestPortalDetection();
340    }
341  }
342  routing_table_->FlushCache();
343}
344
345void Connection::UpdateDNSServers(const vector<string>& dns_servers) {
346  dns_servers_ = dns_servers;
347  PushDNSConfig();
348}
349
350void Connection::PushDNSConfig() {
351  if (!is_default_) {
352#if defined(__ANDROID__)
353    // Stop DNS server proxy to avoid having multiple instances of it running.
354    // Only run DNS server proxy for the current default connection.
355    dns_server_proxy_.reset();
356#endif  // __ANDROID__
357    return;
358  }
359
360  vector<string> domain_search = dns_domain_search_;
361  if (domain_search.empty() && !dns_domain_name_.empty()) {
362    SLOG(this, 2) << "Setting domain search to domain name "
363                  << dns_domain_name_;
364    domain_search.push_back(dns_domain_name_ + ".");
365  }
366#if !defined(__ANDROID__)
367  resolver_->SetDNSFromLists(dns_servers_, domain_search);
368#else
369  dns_server_proxy_.reset(
370      dns_server_proxy_factory_->CreateDNSServerProxy(dns_servers_));
371  dns_server_proxy_->Start();
372#endif  // __ANDROID__
373}
374
375void Connection::RequestRouting() {
376  if (routing_request_count_++ == 0) {
377    DeviceRefPtr device = device_info_->GetDevice(interface_index_);
378    DCHECK(device.get());
379    if (!device.get()) {
380      LOG(ERROR) << "Device is NULL!";
381      return;
382    }
383    device->SetLooseRouting(true);
384  }
385}
386
387void Connection::ReleaseRouting() {
388  DCHECK_GT(routing_request_count_, 0);
389  if (--routing_request_count_ == 0) {
390    DeviceRefPtr device = device_info_->GetDevice(interface_index_);
391    DCHECK(device.get());
392    if (!device.get()) {
393      LOG(ERROR) << "Device is NULL!";
394      return;
395    }
396    device->SetLooseRouting(false);
397
398    // Clear any cached routes that might have accumulated while reverse-path
399    // filtering was disabled.
400    routing_table_->FlushCache();
401  }
402}
403
404bool Connection::RequestHostRoute(const IPAddress& address) {
405  // Do not set interface_index_ since this may not be the default route through
406  // which this destination can be found.  However, we should tag the created
407  // route with our interface index so we can clean this route up when this
408  // connection closes.  Also, add route query callback to determine the lower
409  // connection and bind to it.
410  if (!routing_table_->RequestRouteToHost(
411          address,
412          -1,
413          interface_index_,
414          Bind(&Connection::OnRouteQueryResponse,
415               weak_ptr_factory_.GetWeakPtr()),
416          table_id_)) {
417    LOG(ERROR) << "Could not request route to " << address.ToString();
418    return false;
419  }
420
421  return true;
422}
423
424bool Connection::PinPendingRoutes(int interface_index,
425                                  RoutingTableEntry entry) {
426  // The variable entry is locally modified, hence is passed by value in the
427  // second argument above.
428  for (auto excluded_ip = excluded_ips_cidr_.begin();
429       excluded_ip != excluded_ips_cidr_.end(); ++excluded_ip) {
430    if (!entry.dst.SetAddressAndPrefixFromString(*excluded_ip) ||
431        !entry.dst.IsValid() ||
432        !routing_table_->AddRoute(interface_index, entry)) {
433      LOG(ERROR) << "Unable to setup route for " << *excluded_ip << ".";
434    }
435  }
436
437  return true;
438}
439
440string Connection::GetSubnetName() const {
441  if (!local().IsValid()) {
442    return "";
443  }
444  return base::StringPrintf("%s/%d",
445                            local().GetNetworkPart().ToString().c_str(),
446                            local().prefix());
447}
448
449bool Connection::FixGatewayReachability(const IPAddress& local,
450                                        IPAddress* peer,
451                                        IPAddress* gateway,
452                                        const IPAddress& trusted_ip) {
453  SLOG(nullptr, 2) << __func__
454      << " local " << local.ToString()
455      << ", peer " << peer->ToString()
456      << ", gateway " << gateway->ToString()
457      << ", trusted_ip " << trusted_ip.ToString();
458  if (!gateway->IsValid()) {
459    LOG(WARNING) << "No gateway address was provided for this connection.";
460    return false;
461  }
462
463  if (peer->IsValid()) {
464    if (!gateway->HasSameAddressAs(*peer)) {
465      LOG(WARNING) << "Gateway address "
466                   << gateway->ToString()
467                   << " does not match peer address "
468                   << peer->ToString();
469      return false;
470    }
471    if (gateway->HasSameAddressAs(trusted_ip)) {
472      // In order to send outgoing traffic in a point-to-point network,
473      // the gateway IP address isn't of significance.  As opposed to
474      // broadcast networks, we never ARP for the gateway IP address,
475      // but just send the IP packet addressed to the recipient.  As
476      // such, since using the external trusted IP address as the
477      // gateway or peer wreaks havoc on the routing rules, we choose
478      // not to supply a gateway address.  Here's an example:
479      //
480      //     Client    <->  Internet  <->  VPN Gateway  <->  Internal Network
481      //   192.168.1.2                      10.0.1.25         172.16.5.0/24
482      //
483      // In this example, a client connects to a VPN gateway on its
484      // public IP address 10.0.1.25.  It gets issued an IP address
485      // from the VPN internal pool.  For some VPN gateways, this
486      // results in a pushed-down PPP configuration which specifies:
487      //
488      //    Client local address:   172.16.5.13
489      //    Client peer address:    10.0.1.25
490      //    Client default gateway: 10.0.1.25
491      //
492      // If we take this literally, we need to resolve the fact that
493      // 10.0.1.25 is now listed as the default gateway and interface
494      // peer address for the point-to-point interface.  However, in
495      // order to route tunneled packets to the VPN gateway we must
496      // use the external route through the physical interface and
497      // not the tunnel, or else we end up in an infinite loop
498      // re-entering the tunnel trying to route towards the VPN server.
499      //
500      // We can do this by pinning a route, but we would need to wait
501      // for the pinning process to complete before assigning this
502      // address.  Currently this process is asynchronous and will
503      // complete only after returning to the event loop.  Additionally,
504      // since there's no metric associated with assigning an address
505      // to an interface, it's always possible that having the peer
506      // address of the interface might still trump a host route.
507      //
508      // To solve this problem, we reset the peer and gateway
509      // addresses.  Neither is required in order to perform the
510      // underlying routing task.  A gateway route can be specified
511      // without an IP endpoint on point-to-point links, and simply
512      // specify the outbound interface index.  Similarly, a peer
513      // IP address is not necessary either, and will be assigned
514      // the same IP address as the local IP.  This approach
515      // simplifies routing and doesn't change the desired
516      // functional behavior.
517      //
518      LOG(INFO) << "Removing gateway and peer addresses to preserve "
519                << "routability to trusted IP address.";
520      peer->SetAddressToDefault();
521      gateway->SetAddressToDefault();
522    }
523    return true;
524  }
525
526  if (local.CanReachAddress(*gateway)) {
527    return true;
528  }
529
530  LOG(WARNING) << "Gateway "
531               << gateway->ToString()
532               << " is unreachable from local address/prefix "
533               << local.ToString() << "/" << local.prefix();
534
535  IPAddress gateway_with_max_prefix(*gateway);
536  gateway_with_max_prefix.set_prefix(
537      IPAddress::GetMaxPrefixLength(gateway_with_max_prefix.family()));
538  IPAddress default_address(gateway->family());
539  RoutingTableEntry entry(gateway_with_max_prefix,
540                          default_address,
541                          default_address,
542                          0,
543                          RT_SCOPE_LINK,
544                          false,
545                          table_id_,
546                          RoutingTableEntry::kDefaultTag);
547
548  if (!routing_table_->AddRoute(interface_index_, entry)) {
549    LOG(ERROR) << "Unable to add link-scoped route to gateway.";
550    return false;
551  }
552
553  LOG(WARNING) << "Mitigating this by creating a link route to the gateway.";
554
555  return true;
556}
557
558uint32_t Connection::GetMetric(bool is_default) {
559  // If this is not the default route, assign a metric based on the interface
560  // index.  This way all non-default routes (even to the same gateway IP) end
561  // up with unique metrics so they do not collide.
562  return is_default ? kDefaultMetric : kNonDefaultMetricBase + interface_index_;
563}
564
565bool Connection::PinHostRoute(const IPAddress& trusted_ip,
566                              const IPAddress& gateway) {
567  SLOG(this, 2) << __func__;
568  if (!trusted_ip.IsValid()) {
569    LOG(ERROR) << "No trusted IP -- unable to pin host route.";
570    return false;
571  }
572
573  if (!gateway.IsValid()) {
574    // Although we cannot pin a host route, we are also not going to create
575    // a gateway route that will interfere with our primary connection, so
576    // it is okay to return success here.
577    LOG(WARNING) << "No gateway -- unable to pin host route.";
578    return true;
579  }
580
581  return RequestHostRoute(trusted_ip);
582}
583
584void Connection::SetMTU(int32_t mtu) {
585  SLOG(this, 2) << __func__ << " " << mtu;
586  // Make sure the MTU value is valid.
587  if (mtu == IPConfig::kUndefinedMTU) {
588    mtu = IPConfig::kDefaultMTU;
589  } else {
590    int min_mtu = IsIPv6() ? IPConfig::kMinIPv6MTU : IPConfig::kMinIPv4MTU;
591    if (mtu < min_mtu) {
592      SLOG(this, 2) << __func__ << " MTU " << mtu
593                    << " is too small; adjusting up to " << min_mtu;
594      mtu = min_mtu;
595    }
596  }
597
598  rtnl_handler_->SetInterfaceMTU(interface_index_, mtu);
599}
600
601void Connection::OnRouteQueryResponse(int interface_index,
602                                      const RoutingTableEntry& entry) {
603  SLOG(this, 2) << __func__ << "(" << interface_index << ", "
604                << entry.tag << ")" << " @ " << interface_name_;
605  lower_binder_.Attach(nullptr);
606  DeviceRefPtr device = device_info_->GetDevice(interface_index);
607  if (!device) {
608    LOG(ERROR) << "Unable to lookup device for index " << interface_index;
609    return;
610  }
611  ConnectionRefPtr connection = device->connection();
612  if (!connection) {
613    LOG(ERROR) << "Device " << interface_index << " has no connection.";
614    return;
615  }
616  if (connection == this) {
617    LOG(ERROR) << "Avoiding a connection bind loop for " << interface_name();
618    return;
619  }
620  lower_binder_.Attach(connection);
621  connection->CreateGatewayRoute();
622  device->OnConnectionUpdated();
623  PinPendingRoutes(interface_index, entry);
624}
625
626bool Connection::CreateGatewayRoute() {
627  // Ensure that the gateway for the lower connection remains reachable,
628  // since we may create routes that conflict with it.
629  if (!has_broadcast_domain_) {
630    return false;
631  }
632
633  // If there is no gateway, don't try to create a route to it.
634  if (!gateway_.IsValid()) {
635    return false;
636  }
637
638  // It is not worth keeping track of this route, since it is benign,
639  // and only pins persistent state that was already true of the connection.
640  // If DHCP parameters change later (without the connection having been
641  // destroyed and recreated), the binding processes will likely terminate
642  // and restart, causing a new link route to be created.
643  return routing_table_->CreateLinkRoute(interface_index_, local_, gateway_,
644                                         table_id_);
645}
646
647void Connection::OnLowerDisconnect() {
648  SLOG(this, 2) << __func__ << " @ " << interface_name_;
649  // Ensures that |this| instance doesn't get destroyed in the middle of
650  // notifying the binders. This method needs to be separate from
651  // NotifyBindersOnDisconnect because the latter may be invoked by Connection's
652  // destructor when |this| instance's reference count is already 0.
653  ConnectionRefPtr connection(this);
654  connection->NotifyBindersOnDisconnect();
655}
656
657void Connection::NotifyBindersOnDisconnect() {
658  // Note that this method may be invoked by the destructor.
659  SLOG(this, 2) << __func__ << " @ " << interface_name_;
660
661  // Unbinds the lower connection before notifying the binders. This ensures
662  // correct behavior in case of circular binding.
663  lower_binder_.Attach(nullptr);
664  while (!binders_.empty()) {
665    // Pop the binder first and then notify it to ensure that each binder is
666    // notified only once.
667    Binder* binder = binders_.front();
668    binders_.pop_front();
669    binder->OnDisconnect();
670  }
671}
672
673void Connection::AttachBinder(Binder* binder) {
674  SLOG(this, 2) << __func__ << "(" << binder->name() << ")" << " @ "
675                            << interface_name_;
676  binders_.push_back(binder);
677}
678
679void Connection::DetachBinder(Binder* binder) {
680  SLOG(this, 2) << __func__ << "(" << binder->name() << ")" << " @ "
681                            << interface_name_;
682  for (auto it = binders_.begin(); it != binders_.end(); ++it) {
683    if (binder == *it) {
684      binders_.erase(it);
685      return;
686    }
687  }
688}
689
690ConnectionRefPtr Connection::GetCarrierConnection() {
691  SLOG(this, 2) << __func__ << " @ " << interface_name_;
692  set<Connection*> visited;
693  ConnectionRefPtr carrier = this;
694  while (carrier->GetLowerConnection()) {
695    if (ContainsKey(visited, carrier.get())) {
696      LOG(ERROR) << "Circular connection chain starting at: "
697                 << carrier->interface_name();
698      // If a loop is detected return a NULL value to signal that the carrier
699      // connection is unknown.
700      return nullptr;
701    }
702    visited.insert(carrier.get());
703    carrier = carrier->GetLowerConnection();
704  }
705  SLOG(this, 2) << "Carrier connection: " << carrier->interface_name()
706                << " @ " << interface_name_;
707  return carrier;
708}
709
710bool Connection::IsIPv6() {
711  return local_.family() == IPAddress::kFamilyIPv6;
712}
713
714}  // namespace shill
715