connection.cc revision a016312b7470e124774aec197e3b43bc795637c3
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 if (!properties.trusted_ip.empty() && !PinHostRoute(properties)) { 112 LOG(ERROR) << "Unable to pin host route to " << properties.trusted_ip; 113 return; 114 } 115 116 IPAddress local(properties.address_family); 117 if (!local.SetAddressFromString(properties.address)) { 118 LOG(ERROR) << "Local address " << properties.address << " is invalid"; 119 return; 120 } 121 local.set_prefix(properties.subnet_prefix); 122 123 IPAddress broadcast(properties.address_family); 124 if (properties.broadcast_address.empty()) { 125 if (properties.peer_address.empty()) { 126 LOG(WARNING) << "Broadcast address is not set. Using default."; 127 broadcast = local.GetDefaultBroadcast(); 128 } 129 } else if (!broadcast.SetAddressFromString(properties.broadcast_address)) { 130 LOG(ERROR) << "Broadcast address " << properties.broadcast_address 131 << " is invalid"; 132 return; 133 } 134 135 IPAddress peer(properties.address_family); 136 if (!properties.peer_address.empty() && 137 !peer.SetAddressFromString(properties.peer_address)) { 138 LOG(ERROR) << "Peer address " << properties.peer_address 139 << " is invalid"; 140 return; 141 } 142 143 IPAddress gateway(properties.address_family); 144 if (!properties.gateway.empty() && 145 !gateway.SetAddressFromString(properties.gateway)) { 146 LOG(ERROR) << "Gateway address " << properties.peer_address 147 << " is invalid"; 148 return; 149 } 150 151 if (!FixGatewayReachability(&local, &peer, gateway)) { 152 LOG(WARNING) << "Expect limited network connectivity."; 153 } 154 155 if (device_info_->HasOtherAddress(interface_index_, local)) { 156 // The address has changed for this interface. We need to flush 157 // everything and start over. 158 LOG(INFO) << __func__ << ": Flushing old addresses and routes."; 159 routing_table_->FlushRoutes(interface_index_); 160 device_info_->FlushAddresses(interface_index_); 161 } 162 163 LOG(INFO) << __func__ << ": Installing with parameters:" 164 << " local=" << local.ToString() 165 << " broadcast=" << broadcast.ToString() 166 << " peer=" << peer.ToString() 167 << " gateway=" << gateway.ToString(); 168 rtnl_handler_->AddInterfaceAddress(interface_index_, local, broadcast, peer); 169 170 if (gateway.IsValid()) { 171 routing_table_->SetDefaultRoute(interface_index_, gateway, 172 GetMetric(is_default_)); 173 } 174 175 // Install any explicitly configured routes at the default metric. 176 routing_table_->ConfigureRoutes(interface_index_, config, kDefaultMetric); 177 178 if (properties.blackhole_ipv6) { 179 routing_table_->CreateBlackholeRoute(interface_index_, 180 IPAddress::kFamilyIPv6, 181 kDefaultMetric); 182 } 183 184 // Save a copy of the last non-null DNS config. 185 if (!config->properties().dns_servers.empty()) { 186 dns_servers_ = config->properties().dns_servers; 187 } 188 189 if (!config->properties().domain_search.empty()) { 190 dns_domain_search_ = config->properties().domain_search; 191 } 192 193 if (!config->properties().domain_name.empty()) { 194 dns_domain_name_ = config->properties().domain_name; 195 } 196 197 ipconfig_rpc_identifier_ = config->GetRpcIdentifier(); 198 199 if (is_default_) { 200 PushDNSConfig(); 201 } 202 203 local_ = local; 204 gateway_ = gateway; 205 has_broadcast_domain_ = !peer.IsValid(); 206} 207 208void Connection::SetIsDefault(bool is_default) { 209 SLOG(Connection, 2) << __func__ << " " << interface_name_ 210 << " (index " << interface_index_ << ") " 211 << is_default_ << " -> " << is_default; 212 if (is_default == is_default_) { 213 return; 214 } 215 216 routing_table_->SetDefaultMetric(interface_index_, GetMetric(is_default)); 217 218 is_default_ = is_default; 219 220 if (is_default) { 221 PushDNSConfig(); 222 DeviceRefPtr device = device_info_->GetDevice(interface_index_); 223 if (device) { 224 device->RequestPortalDetection(); 225 } 226 } 227 routing_table_->FlushCache(); 228} 229 230void Connection::PushDNSConfig() { 231 vector<string> domain_search = dns_domain_search_; 232 if (domain_search.empty() && !dns_domain_name_.empty()) { 233 SLOG(Connection, 2) << "Setting domain search to domain name " 234 << dns_domain_name_; 235 domain_search.push_back(dns_domain_name_ + "."); 236 } 237 resolver_->SetDNSFromLists(dns_servers_, domain_search, 238 dns_timeout_parameters_); 239} 240 241void Connection::RequestRouting() { 242 if (routing_request_count_++ == 0) { 243 DeviceRefPtr device = device_info_->GetDevice(interface_index_); 244 DCHECK(device.get()); 245 if (!device.get()) { 246 LOG(ERROR) << "Device is NULL!"; 247 return; 248 } 249 device->DisableReversePathFilter(); 250 } 251} 252 253void Connection::ReleaseRouting() { 254 DCHECK(routing_request_count_ > 0); 255 if (--routing_request_count_ == 0) { 256 DeviceRefPtr device = device_info_->GetDevice(interface_index_); 257 DCHECK(device.get()); 258 if (!device.get()) { 259 LOG(ERROR) << "Device is NULL!"; 260 return; 261 } 262 device->EnableReversePathFilter(); 263 264 // Clear any cached routes that might have accumulated while reverse-path 265 // filtering was disabled. 266 routing_table_->FlushCache(); 267 } 268} 269 270bool Connection::RequestHostRoute(const IPAddress &address) { 271 // Set the prefix to be the entire address size. 272 IPAddress address_prefix(address); 273 address_prefix.set_prefix(address_prefix.GetLength() * 8); 274 275 // Do not set interface_index_ since this may not be the default route through 276 // which this destination can be found. However, we should tag the created 277 // route with our interface index so we can clean this route up when this 278 // connection closes. Also, add route query callback to determine the lower 279 // connection and bind to it. 280 if (!routing_table_->RequestRouteToHost( 281 address_prefix, 282 -1, 283 interface_index_, 284 Bind(&Connection::OnRouteQueryResponse, 285 weak_ptr_factory_.GetWeakPtr()))) { 286 LOG(ERROR) << "Could not request route to " << address.ToString(); 287 return false; 288 } 289 290 return true; 291} 292 293// static 294bool Connection::FixGatewayReachability(IPAddress *local, 295 IPAddress *peer, 296 const IPAddress &gateway) { 297 if (!gateway.IsValid()) { 298 LOG(WARNING) << "No gateway address was provided for this connection."; 299 return false; 300 } 301 302 if (peer->IsValid()) { 303 if (gateway.Equals(*peer)) { 304 return true; 305 } 306 LOG(WARNING) << "Gateway address " 307 << gateway.ToString() 308 << " does not match peer address " 309 << peer->ToString(); 310 return false; 311 } 312 313 if (local->CanReachAddress(gateway)) { 314 return true; 315 } 316 317 LOG(WARNING) << "Gateway " 318 << gateway.ToString() 319 << " is unreachable from local address/prefix " 320 << local->ToString() << "/" << local->prefix(); 321 322 bool found_new_prefix = false; 323 size_t original_prefix = local->prefix(); 324 // Only try to expand the netmask if the configured prefix is 325 // less than "all ones". This special-cases the "all-ones" 326 // prefix as a forced conversion to point-to-point networking. 327 if (local->prefix() < IPAddress::GetMaxPrefixLength(local->family())) { 328 size_t prefix = original_prefix - 1; 329 for (; prefix >= local->GetMinPrefixLength(); --prefix) { 330 local->set_prefix(prefix); 331 if (local->CanReachAddress(gateway)) { 332 found_new_prefix = true; 333 break; 334 } 335 } 336 } 337 338 if (!found_new_prefix) { 339 // Restore the original prefix since we cannot find a better one. 340 local->set_prefix(original_prefix); 341 DCHECK(!peer->IsValid()); 342 LOG(WARNING) << "Assuming point-to-point configuration."; 343 *peer = gateway; 344 return true; 345 } 346 347 LOG(WARNING) << "Mitigating this by setting local prefix to " 348 << local->prefix(); 349 return true; 350} 351 352uint32 Connection::GetMetric(bool is_default) { 353 // If this is not the default route, assign a metric based on the interface 354 // index. This way all non-default routes (even to the same gateway IP) end 355 // up with unique metrics so they do not collide. 356 return is_default ? kDefaultMetric : kNonDefaultMetricBase + interface_index_; 357} 358 359bool Connection::PinHostRoute(const IPConfig::Properties &properties) { 360 SLOG(Connection, 2) << __func__; 361 if (properties.gateway.empty() || properties.trusted_ip.empty()) { 362 LOG_IF(ERROR, properties.gateway.empty()) 363 << "No gateway -- unable to pin host route."; 364 LOG_IF(ERROR, properties.trusted_ip.empty()) 365 << "No trusted IP -- unable to pin host route."; 366 return false; 367 } 368 369 IPAddress trusted_ip(properties.address_family); 370 if (!trusted_ip.SetAddressFromString(properties.trusted_ip)) { 371 LOG(ERROR) << "Failed to parse trusted_ip " 372 << properties.trusted_ip << "; ignored."; 373 return false; 374 } 375 376 return RequestHostRoute(trusted_ip); 377} 378 379void Connection::OnRouteQueryResponse(int interface_index, 380 const RoutingTableEntry &entry) { 381 SLOG(Connection, 2) << __func__ << "(" << interface_index << ", " 382 << entry.tag << ")" << " @ " << interface_name_; 383 lower_binder_.Attach(NULL); 384 DeviceRefPtr device = device_info_->GetDevice(interface_index); 385 if (!device) { 386 LOG(ERROR) << "Unable to lookup device for index " << interface_index; 387 return; 388 } 389 ConnectionRefPtr connection = device->connection(); 390 if (!connection) { 391 LOG(ERROR) << "Device " << interface_index << " has no connection."; 392 return; 393 } 394 lower_binder_.Attach(connection); 395 connection->CreateGatewayRoute(); 396} 397 398bool Connection::CreateGatewayRoute() { 399 // Ensure that the gateway for the lower connection remains reachable, 400 // since we may create routes that conflict with it. 401 if (!has_broadcast_domain_) { 402 return false; 403 } 404 // It is not worth keeping track of this route, since it is benign, 405 // and only pins persistent state that was already true of the connection. 406 // If DHCP parameters change later (without the connection having been 407 // destroyed and recreated), the binding processes will likely terminate 408 // and restart, causing a new link route to be created. 409 return routing_table_->CreateLinkRoute(interface_index_, local_, gateway_); 410} 411 412void Connection::OnLowerDisconnect() { 413 SLOG(Connection, 2) << __func__ << " @ " << interface_name_; 414 // Ensures that |this| instance doesn't get destroyed in the middle of 415 // notifying the binders. This method needs to be separate from 416 // NotifyBindersOnDisconnect because the latter may be invoked by Connection's 417 // destructor when |this| instance's reference count is already 0. 418 ConnectionRefPtr connection(this); 419 connection->NotifyBindersOnDisconnect(); 420} 421 422void Connection::NotifyBindersOnDisconnect() { 423 // Note that this method may be invoked by the destructor. 424 SLOG(Connection, 2) << __func__ << " @ " << interface_name_; 425 426 // Unbinds the lower connection before notifying the binders. This ensures 427 // correct behavior in case of circular binding. 428 lower_binder_.Attach(NULL); 429 while (!binders_.empty()) { 430 // Pop the binder first and then notify it to ensure that each binder is 431 // notified only once. 432 Binder *binder = binders_.front(); 433 binders_.pop_front(); 434 binder->OnDisconnect(); 435 } 436} 437 438void Connection::AttachBinder(Binder *binder) { 439 SLOG(Connection, 2) << __func__ << "(" << binder->name() << ")" << " @ " 440 << interface_name_; 441 binders_.push_back(binder); 442} 443 444void Connection::DetachBinder(Binder *binder) { 445 SLOG(Connection, 2) << __func__ << "(" << binder->name() << ")" << " @ " 446 << interface_name_; 447 for (deque<Binder *>::iterator it = binders_.begin(); 448 it != binders_.end(); ++it) { 449 if (binder == *it) { 450 binders_.erase(it); 451 return; 452 } 453 } 454} 455 456} // namespace shill 457