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 40 using base::Bind; 41 using base::Closure; 42 using base::Unretained; 43 using std::deque; 44 using std::set; 45 using std::string; 46 using std::vector; 47 48 namespace shill { 49 50 namespace Logging { 51 static auto kModuleLogScope = ScopeLogger::kConnection; 52 static string ObjectID(Connection* c) { 53 if (c == nullptr) 54 return "(connection)"; 55 return c->interface_name(); 56 } 57 } 58 59 #if defined(__ANDROID__) 60 namespace { 61 const char* kGoogleDNSServers[] = { 62 "8.8.4.4", 63 "8.8.8.8" 64 }; 65 } // namespace 66 #endif // __ANDROID__ 67 68 // static 69 const uint32_t Connection::kDefaultMetric = 1; 70 // static 71 const uint32_t Connection::kNonDefaultMetricBase = 10; 72 // static 73 const uint32_t Connection::kMarkForUserTraffic = 0x1; 74 // static 75 const uint8_t Connection::kSecondaryTableId = 0x1; 76 77 Connection::Binder::Binder(const string& name, 78 const Closure& disconnect_callback) 79 : name_(name), 80 client_disconnect_callback_(disconnect_callback) {} 81 82 Connection::Binder::~Binder() { 83 Attach(nullptr); 84 } 85 86 void 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 101 void 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 111 Connection::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 146 Connection::~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 158 void 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 302 bool 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 319 bool Connection::TearDownIptableEntries() { 320 return firewall_proxy_ ? firewall_proxy_->RemoveVpnSetup() : true; 321 } 322 323 void 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 345 void Connection::UpdateDNSServers(const vector<string>& dns_servers) { 346 dns_servers_ = dns_servers; 347 PushDNSConfig(); 348 } 349 350 void 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 375 void 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 387 void 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 404 bool 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 424 bool 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 440 string 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 449 bool 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 558 uint32_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 565 bool 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 584 void 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 601 void 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 626 bool 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 647 void 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 657 void 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 673 void Connection::AttachBinder(Binder* binder) { 674 SLOG(this, 2) << __func__ << "(" << binder->name() << ")" << " @ " 675 << interface_name_; 676 binders_.push_back(binder); 677 } 678 679 void 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 690 ConnectionRefPtr 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 710 bool Connection::IsIPv6() { 711 return local_.family() == IPAddress::kFamilyIPv6; 712 } 713 714 } // namespace shill 715