connection.cc revision 73fcc3f6560240d0d5dde15927cc0b51dc692dd5
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 "shill/device_info.h" 11#include "shill/logging.h" 12#include "shill/resolver.h" 13#include "shill/routing_table.h" 14#include "shill/rtnl_handler.h" 15 16using base::Bind; 17using base::Closure; 18using base::Unretained; 19using std::deque; 20using std::string; 21using std::vector; 22 23namespace shill { 24 25// static 26const uint32 Connection::kDefaultMetric = 1; 27// static 28const uint32 Connection::kNonDefaultMetricBase = 10; 29 30Connection::Binder::Binder(const string &name, 31 const Closure &disconnect_callback) 32 : name_(name), 33 client_disconnect_callback_(disconnect_callback) {} 34 35Connection::Binder::~Binder() { 36 Attach(NULL); 37} 38 39void Connection::Binder::Attach(const ConnectionRefPtr &to_connection) { 40 if (connection_) { 41 connection_->DetachBinder(this); 42 LOG(INFO) << name_ << ": unbound from connection: " 43 << connection_->interface_name(); 44 connection_.reset(); 45 } 46 if (to_connection) { 47 connection_ = to_connection->weak_ptr_factory_.GetWeakPtr(); 48 connection_->AttachBinder(this); 49 LOG(INFO) << name_ << ": bound to connection: " 50 << connection_->interface_name(); 51 } 52} 53 54void Connection::Binder::OnDisconnect() { 55 LOG(INFO) << name_ << ": bound connection disconnected: " 56 << connection_->interface_name(); 57 connection_.reset(); 58 if (!client_disconnect_callback_.is_null()) { 59 SLOG(Connection, 2) << "Running client disconnect callback."; 60 client_disconnect_callback_.Run(); 61 } 62} 63 64Connection::Connection(int interface_index, 65 const std::string& interface_name, 66 Technology::Identifier technology, 67 const DeviceInfo *device_info, 68 bool is_short_dns_timeout_enabled) 69 : weak_ptr_factory_(this), 70 is_default_(false), 71 has_broadcast_domain_(false), 72 routing_request_count_(0), 73 interface_index_(interface_index), 74 interface_name_(interface_name), 75 technology_(technology), 76 local_(IPAddress::kFamilyUnknown), 77 gateway_(IPAddress::kFamilyUnknown), 78 lower_binder_( 79 interface_name_, 80 // Connection owns a single instance of |lower_binder_| so it's safe 81 // to use an Unretained callback. 82 Bind(&Connection::OnLowerDisconnect, Unretained(this))), 83 dns_timeout_parameters_(Resolver::kDefaultTimeout), 84 device_info_(device_info), 85 resolver_(Resolver::GetInstance()), 86 routing_table_(RoutingTable::GetInstance()), 87 rtnl_handler_(RTNLHandler::GetInstance()) { 88 SLOG(Connection, 2) << __func__ << "(" << interface_index << ", " 89 << interface_name << ", " 90 << Technology::NameFromIdentifier(technology) << ")"; 91 if (is_short_dns_timeout_enabled) { 92 dns_timeout_parameters_ = Resolver::kShortTimeout; 93 } 94} 95 96Connection::~Connection() { 97 SLOG(Connection, 2) << __func__ << " " << interface_name_; 98 99 NotifyBindersOnDisconnect(); 100 101 DCHECK(!routing_request_count_); 102 routing_table_->FlushRoutes(interface_index_); 103 routing_table_->FlushRoutesWithTag(interface_index_); 104 device_info_->FlushAddresses(interface_index_); 105} 106 107void Connection::UpdateFromIPConfig(const IPConfigRefPtr &config) { 108 SLOG(Connection, 2) << __func__ << " " << interface_name_; 109 110 const IPConfig::Properties &properties = config->properties(); 111 IPAddress gateway(properties.address_family); 112 if (!properties.gateway.empty() && 113 !gateway.SetAddressFromString(properties.gateway)) { 114 LOG(ERROR) << "Gateway address " << properties.gateway << " is invalid"; 115 return; 116 } 117 118 IPAddress trusted_ip(properties.address_family); 119 if (!properties.trusted_ip.empty()) { 120 if (!trusted_ip.SetAddressFromString(properties.trusted_ip)) { 121 LOG(ERROR) << "Trusted IP address " 122 << properties.trusted_ip << " is invalid"; 123 return; 124 } 125 if (!PinHostRoute(trusted_ip, gateway)) { 126 LOG(ERROR) << "Unable to pin host route to " << properties.trusted_ip; 127 return; 128 } 129 } 130 131 IPAddress local(properties.address_family); 132 if (!local.SetAddressFromString(properties.address)) { 133 LOG(ERROR) << "Local address " << properties.address << " is invalid"; 134 return; 135 } 136 local.set_prefix(properties.subnet_prefix); 137 138 IPAddress broadcast(properties.address_family); 139 if (properties.broadcast_address.empty()) { 140 if (properties.peer_address.empty()) { 141 LOG(WARNING) << "Broadcast address is not set. Using default."; 142 broadcast = local.GetDefaultBroadcast(); 143 } 144 } else if (!broadcast.SetAddressFromString(properties.broadcast_address)) { 145 LOG(ERROR) << "Broadcast address " << properties.broadcast_address 146 << " is invalid"; 147 return; 148 } 149 150 IPAddress peer(properties.address_family); 151 if (!properties.peer_address.empty() && 152 !peer.SetAddressFromString(properties.peer_address)) { 153 LOG(ERROR) << "Peer address " << properties.peer_address 154 << " is invalid"; 155 return; 156 } 157 158 if (!FixGatewayReachability(&local, &peer, &gateway, trusted_ip)) { 159 LOG(WARNING) << "Expect limited network connectivity."; 160 } 161 162 if (device_info_->HasOtherAddress(interface_index_, local)) { 163 // The address has changed for this interface. We need to flush 164 // everything and start over. 165 LOG(INFO) << __func__ << ": Flushing old addresses and routes."; 166 routing_table_->FlushRoutes(interface_index_); 167 device_info_->FlushAddresses(interface_index_); 168 } 169 170 LOG(INFO) << __func__ << ": Installing with parameters:" 171 << " local=" << local.ToString() 172 << " broadcast=" << broadcast.ToString() 173 << " peer=" << peer.ToString() 174 << " gateway=" << gateway.ToString(); 175 rtnl_handler_->AddInterfaceAddress(interface_index_, local, broadcast, peer); 176 177 if (gateway.IsValid()) { 178 routing_table_->SetDefaultRoute(interface_index_, gateway, 179 GetMetric(is_default_)); 180 } 181 182 // Install any explicitly configured routes at the default metric. 183 routing_table_->ConfigureRoutes(interface_index_, config, kDefaultMetric); 184 185 if (properties.blackhole_ipv6) { 186 routing_table_->CreateBlackholeRoute(interface_index_, 187 IPAddress::kFamilyIPv6, 188 kDefaultMetric); 189 } 190 191 // Save a copy of the last non-null DNS config. 192 if (!config->properties().dns_servers.empty()) { 193 dns_servers_ = config->properties().dns_servers; 194 } 195 196 if (!config->properties().domain_search.empty()) { 197 dns_domain_search_ = config->properties().domain_search; 198 } 199 200 if (!config->properties().domain_name.empty()) { 201 dns_domain_name_ = config->properties().domain_name; 202 } 203 204 ipconfig_rpc_identifier_ = config->GetRpcIdentifier(); 205 206 if (is_default_) { 207 PushDNSConfig(); 208 } 209 210 local_ = local; 211 gateway_ = gateway; 212 has_broadcast_domain_ = !peer.IsValid(); 213} 214 215void Connection::SetIsDefault(bool is_default) { 216 SLOG(Connection, 2) << __func__ << " " << interface_name_ 217 << " (index " << interface_index_ << ") " 218 << is_default_ << " -> " << is_default; 219 if (is_default == is_default_) { 220 return; 221 } 222 223 routing_table_->SetDefaultMetric(interface_index_, GetMetric(is_default)); 224 225 is_default_ = is_default; 226 227 if (is_default) { 228 PushDNSConfig(); 229 DeviceRefPtr device = device_info_->GetDevice(interface_index_); 230 if (device) { 231 device->RequestPortalDetection(); 232 } 233 } 234 routing_table_->FlushCache(); 235} 236 237void Connection::PushDNSConfig() { 238 vector<string> domain_search = dns_domain_search_; 239 if (domain_search.empty() && !dns_domain_name_.empty()) { 240 SLOG(Connection, 2) << "Setting domain search to domain name " 241 << dns_domain_name_; 242 domain_search.push_back(dns_domain_name_ + "."); 243 } 244 resolver_->SetDNSFromLists(dns_servers_, domain_search, 245 dns_timeout_parameters_); 246} 247 248void Connection::RequestRouting() { 249 if (routing_request_count_++ == 0) { 250 DeviceRefPtr device = device_info_->GetDevice(interface_index_); 251 DCHECK(device.get()); 252 if (!device.get()) { 253 LOG(ERROR) << "Device is NULL!"; 254 return; 255 } 256 device->DisableReversePathFilter(); 257 } 258} 259 260void Connection::ReleaseRouting() { 261 DCHECK(routing_request_count_ > 0); 262 if (--routing_request_count_ == 0) { 263 DeviceRefPtr device = device_info_->GetDevice(interface_index_); 264 DCHECK(device.get()); 265 if (!device.get()) { 266 LOG(ERROR) << "Device is NULL!"; 267 return; 268 } 269 device->EnableReversePathFilter(); 270 271 // Clear any cached routes that might have accumulated while reverse-path 272 // filtering was disabled. 273 routing_table_->FlushCache(); 274 } 275} 276 277bool Connection::RequestHostRoute(const IPAddress &address) { 278 // Set the prefix to be the entire address size. 279 IPAddress address_prefix(address); 280 address_prefix.set_prefix(address_prefix.GetLength() * 8); 281 282 // Do not set interface_index_ since this may not be the default route through 283 // which this destination can be found. However, we should tag the created 284 // route with our interface index so we can clean this route up when this 285 // connection closes. Also, add route query callback to determine the lower 286 // connection and bind to it. 287 if (!routing_table_->RequestRouteToHost( 288 address_prefix, 289 -1, 290 interface_index_, 291 Bind(&Connection::OnRouteQueryResponse, 292 weak_ptr_factory_.GetWeakPtr()))) { 293 LOG(ERROR) << "Could not request route to " << address.ToString(); 294 return false; 295 } 296 297 return true; 298} 299 300// static 301bool Connection::FixGatewayReachability(IPAddress *local, 302 IPAddress *peer, 303 IPAddress *gateway, 304 const IPAddress &trusted_ip) { 305 if (!gateway->IsValid()) { 306 LOG(WARNING) << "No gateway address was provided for this connection."; 307 return false; 308 } 309 310 if (peer->IsValid()) { 311 if (!gateway->Equals(*peer)) { 312 LOG(WARNING) << "Gateway address " 313 << gateway->ToString() 314 << " does not match peer address " 315 << peer->ToString(); 316 return false; 317 } 318 if (gateway->Equals(trusted_ip)) { 319 // In order to send outgoing traffic in a point-to-point network, 320 // the gateway IP address isn't of significance. As opposed to 321 // broadcast networks, we never ARP for the gateway IP address, 322 // but just send the IP packet addressed to the recipient. As 323 // such, since using the external trusted IP address as the 324 // gateway or peer wreaks havoc on the routing rules, we choose 325 // not to supply a gateway address. Here's an example: 326 // 327 // Client <-> Internet <-> VPN Gateway <-> Internal Network 328 // 192.168.1.2 10.0.1.25 172.16.5.0/24 329 // 330 // In this example, a client connects to a VPN gateway on its 331 // public IP address 10.0.1.25. It gets issued an IP address 332 // from the VPN internal pool. For some VPN gateways, this 333 // results in a pushed-down PPP configuration which specifies: 334 // 335 // Client local address: 172.16.5.13 336 // Client peer address: 10.0.1.25 337 // Client default gateway: 10.0.1.25 338 // 339 // If we take this literally, we need to resolve the fact that 340 // 10.0.1.25 is now listed as the default gateway and interface 341 // peer address for the point-to-point interface. However, in 342 // order to route tunneled packets to the VPN gateway we must 343 // use the external route through the physical interface and 344 // not the tunnel, or else we end up in an infinite loop 345 // re-entering the tunnel trying to route towards the VPN server. 346 // 347 // We can do this by pinning a route, but we would need to wait 348 // for the pinning process to complete before assigning this 349 // address. Currently this process is asynchronous and will 350 // complete only after returning to the event loop. Additionally, 351 // since there's no metric associated with assigning an address 352 // to an interface, it's always possible that having the peer 353 // address of the interface might still trump a host route. 354 // 355 // To solve this problem, we reset the peer and gateway 356 // addresses. Neither is required in order to perform the 357 // underlying routing task. A gateway route can be specified 358 // without an IP endpoint on point-to-point links, and simply 359 // specify the outbound interface index. Similarly, a peer 360 // IP address is not necessary either, and will be assigned 361 // the same IP address as the local IP. This approach 362 // simplifies routing and doesn't change the desired 363 // functional behavior. 364 // 365 LOG(INFO) << "Removing gateway and peer addresses to preserve " 366 << "routability to trusted IP address."; 367 peer->SetAddressToDefault(); 368 gateway->SetAddressToDefault(); 369 } 370 return true; 371 } 372 373 if (local->CanReachAddress(*gateway)) { 374 return true; 375 } 376 377 LOG(WARNING) << "Gateway " 378 << gateway->ToString() 379 << " is unreachable from local address/prefix " 380 << local->ToString() << "/" << local->prefix(); 381 382 bool found_new_prefix = false; 383 size_t original_prefix = local->prefix(); 384 // Only try to expand the netmask if the configured prefix is 385 // less than "all ones". This special-cases the "all-ones" 386 // prefix as a forced conversion to point-to-point networking. 387 if (local->prefix() < IPAddress::GetMaxPrefixLength(local->family())) { 388 size_t prefix = original_prefix - 1; 389 for (; prefix >= local->GetMinPrefixLength(); --prefix) { 390 local->set_prefix(prefix); 391 if (local->CanReachAddress(*gateway)) { 392 found_new_prefix = true; 393 break; 394 } 395 } 396 } 397 398 if (!found_new_prefix) { 399 // Restore the original prefix since we cannot find a better one. 400 local->set_prefix(original_prefix); 401 DCHECK(!peer->IsValid()); 402 LOG(WARNING) << "Assuming point-to-point configuration."; 403 *peer = *gateway; 404 return true; 405 } 406 407 LOG(WARNING) << "Mitigating this by setting local prefix to " 408 << local->prefix(); 409 return true; 410} 411 412uint32 Connection::GetMetric(bool is_default) { 413 // If this is not the default route, assign a metric based on the interface 414 // index. This way all non-default routes (even to the same gateway IP) end 415 // up with unique metrics so they do not collide. 416 return is_default ? kDefaultMetric : kNonDefaultMetricBase + interface_index_; 417} 418 419bool Connection::PinHostRoute(const IPAddress &trusted_ip, 420 const IPAddress &gateway) { 421 SLOG(Connection, 2) << __func__; 422 if (!trusted_ip.IsValid() || !gateway.IsValid()) { 423 LOG_IF(ERROR, !gateway.IsValid()) 424 << "No gateway -- unable to pin host route."; 425 LOG_IF(ERROR, !trusted_ip.IsValid()) 426 << "No trusted IP -- unable to pin host route."; 427 return false; 428 } 429 430 return RequestHostRoute(trusted_ip); 431} 432 433void Connection::OnRouteQueryResponse(int interface_index, 434 const RoutingTableEntry &entry) { 435 SLOG(Connection, 2) << __func__ << "(" << interface_index << ", " 436 << entry.tag << ")" << " @ " << interface_name_; 437 lower_binder_.Attach(NULL); 438 DeviceRefPtr device = device_info_->GetDevice(interface_index); 439 if (!device) { 440 LOG(ERROR) << "Unable to lookup device for index " << interface_index; 441 return; 442 } 443 ConnectionRefPtr connection = device->connection(); 444 if (!connection) { 445 LOG(ERROR) << "Device " << interface_index << " has no connection."; 446 return; 447 } 448 lower_binder_.Attach(connection); 449 connection->CreateGatewayRoute(); 450} 451 452bool Connection::CreateGatewayRoute() { 453 // Ensure that the gateway for the lower connection remains reachable, 454 // since we may create routes that conflict with it. 455 if (!has_broadcast_domain_) { 456 return false; 457 } 458 // It is not worth keeping track of this route, since it is benign, 459 // and only pins persistent state that was already true of the connection. 460 // If DHCP parameters change later (without the connection having been 461 // destroyed and recreated), the binding processes will likely terminate 462 // and restart, causing a new link route to be created. 463 return routing_table_->CreateLinkRoute(interface_index_, local_, gateway_); 464} 465 466void Connection::OnLowerDisconnect() { 467 SLOG(Connection, 2) << __func__ << " @ " << interface_name_; 468 // Ensures that |this| instance doesn't get destroyed in the middle of 469 // notifying the binders. This method needs to be separate from 470 // NotifyBindersOnDisconnect because the latter may be invoked by Connection's 471 // destructor when |this| instance's reference count is already 0. 472 ConnectionRefPtr connection(this); 473 connection->NotifyBindersOnDisconnect(); 474} 475 476void Connection::NotifyBindersOnDisconnect() { 477 // Note that this method may be invoked by the destructor. 478 SLOG(Connection, 2) << __func__ << " @ " << interface_name_; 479 480 // Unbinds the lower connection before notifying the binders. This ensures 481 // correct behavior in case of circular binding. 482 lower_binder_.Attach(NULL); 483 while (!binders_.empty()) { 484 // Pop the binder first and then notify it to ensure that each binder is 485 // notified only once. 486 Binder *binder = binders_.front(); 487 binders_.pop_front(); 488 binder->OnDisconnect(); 489 } 490} 491 492void Connection::AttachBinder(Binder *binder) { 493 SLOG(Connection, 2) << __func__ << "(" << binder->name() << ")" << " @ " 494 << interface_name_; 495 binders_.push_back(binder); 496} 497 498void Connection::DetachBinder(Binder *binder) { 499 SLOG(Connection, 2) << __func__ << "(" << binder->name() << ")" << " @ " 500 << interface_name_; 501 for (deque<Binder *>::iterator it = binders_.begin(); 502 it != binders_.end(); ++it) { 503 if (binder == *it) { 504 binders_.erase(it); 505 return; 506 } 507 } 508} 509 510} // namespace shill 511