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