connection.cc revision ef1f9fec08b3a251a84ae3e4ef7ef15fdc2a1544
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/resolver.h" 12#include "shill/routing_table.h" 13#include "shill/rtnl_handler.h" 14#include "shill/scope_logger.h" 15 16using base::Bind; 17using base::Closure; 18using base::Unretained; 19using std::deque; 20using std::string; 21 22namespace shill { 23 24// static 25const uint32 Connection::kDefaultMetric = 1; 26// static 27const uint32 Connection::kNonDefaultMetricBase = 10; 28 29Connection::Binder::Binder(const string &name, 30 const Closure &disconnect_callback) 31 : name_(name), 32 client_disconnect_callback_(disconnect_callback) {} 33 34Connection::Binder::~Binder() { 35 Attach(NULL); 36} 37 38void Connection::Binder::Attach(const ConnectionRefPtr &to_connection) { 39 if (connection_) { 40 connection_->DetachBinder(this); 41 LOG(INFO) << name_ << ": unbound from connection: " 42 << connection_->interface_name(); 43 connection_.reset(); 44 } 45 if (to_connection) { 46 connection_ = to_connection->weak_ptr_factory_.GetWeakPtr(); 47 connection_->AttachBinder(this); 48 LOG(INFO) << name_ << ": bound to connection: " 49 << connection_->interface_name(); 50 } 51} 52 53void Connection::Binder::OnDisconnect() { 54 LOG(INFO) << name_ << ": bound connection disconnected: " 55 << connection_->interface_name(); 56 connection_.reset(); 57 if (!client_disconnect_callback_.is_null()) { 58 SLOG(Connection, 2) << "Running client disconnect callback."; 59 client_disconnect_callback_.Run(); 60 } 61} 62 63Connection::Connection(int interface_index, 64 const std::string& interface_name, 65 Technology::Identifier technology, 66 const DeviceInfo *device_info) 67 : weak_ptr_factory_(this), 68 is_default_(false), 69 routing_request_count_(0), 70 interface_index_(interface_index), 71 interface_name_(interface_name), 72 technology_(technology), 73 lower_binder_( 74 interface_name_, 75 // Connection owns a single instance of |lower_binder_| so it's safe 76 // to use an Unretained callback. 77 Bind(&Connection::OnLowerDisconnect, Unretained(this))), 78 device_info_(device_info), 79 resolver_(Resolver::GetInstance()), 80 routing_table_(RoutingTable::GetInstance()), 81 rtnl_handler_(RTNLHandler::GetInstance()) { 82 SLOG(Connection, 2) << __func__ << "(" << interface_index << ", " 83 << interface_name << ", " 84 << Technology::NameFromIdentifier(technology) << ")"; 85} 86 87Connection::~Connection() { 88 SLOG(Connection, 2) << __func__ << " " << interface_name_; 89 90 NotifyBindersOnDisconnect(); 91 92 DCHECK(!routing_request_count_); 93 routing_table_->FlushRoutes(interface_index_); 94 routing_table_->FlushRoutesWithTag(interface_index_); 95 device_info_->FlushAddresses(interface_index_); 96} 97 98void Connection::UpdateFromIPConfig(const IPConfigRefPtr &config) { 99 SLOG(Connection, 2) << __func__ << " " << interface_name_; 100 101 const IPConfig::Properties &properties = config->properties(); 102 if (!properties.trusted_ip.empty() && !PinHostRoute(properties)) { 103 LOG(ERROR) << "Unable to pin host route to " << properties.trusted_ip; 104 return; 105 } 106 107 IPAddress local(properties.address_family); 108 if (!local.SetAddressFromString(properties.address)) { 109 LOG(ERROR) << "Local address " << properties.address << " is invalid"; 110 return; 111 } 112 local.set_prefix(properties.subnet_prefix); 113 114 IPAddress broadcast(properties.address_family); 115 if (properties.broadcast_address.empty()) { 116 if (properties.peer_address.empty()) { 117 LOG(WARNING) << "Broadcast address is not set. Using default."; 118 broadcast = local.GetDefaultBroadcast(); 119 } 120 } else if (!broadcast.SetAddressFromString(properties.broadcast_address)) { 121 LOG(ERROR) << "Broadcast address " << properties.broadcast_address 122 << " is invalid"; 123 return; 124 } 125 126 IPAddress peer(properties.address_family); 127 if (!properties.peer_address.empty() && 128 !peer.SetAddressFromString(properties.peer_address)) { 129 LOG(ERROR) << "Peer address " << properties.peer_address 130 << " is invalid"; 131 return; 132 } 133 134 IPAddress gateway_address(properties.address_family); 135 if (!properties.gateway.empty() && 136 !gateway_address.SetAddressFromString(properties.gateway)) { 137 LOG(ERROR) << "Gateway address " << properties.peer_address 138 << " is invalid"; 139 return; 140 } 141 142 if (!FixGatewayReachability(&local, gateway_address, peer)) { 143 LOG(WARNING) << "Expect limited network connectivity."; 144 } 145 146 rtnl_handler_->AddInterfaceAddress(interface_index_, local, broadcast, peer); 147 148 if (gateway_address.IsValid()) { 149 routing_table_->SetDefaultRoute(interface_index_, gateway_address, 150 GetMetric(is_default_)); 151 } 152 153 // Install any explicitly configured routes at the default metric. 154 routing_table_->ConfigureRoutes(interface_index_, config, kDefaultMetric); 155 156 // Save a copy of the last non-null DNS config 157 if (!config->properties().dns_servers.empty()) { 158 dns_servers_ = config->properties().dns_servers; 159 dns_domain_search_ = config->properties().domain_search; 160 } 161 162 ipconfig_rpc_identifier_ = config->GetRpcIdentifier(); 163 164 if (is_default_) { 165 resolver_->SetDNSFromIPConfig(config); 166 } 167} 168 169void Connection::SetIsDefault(bool is_default) { 170 SLOG(Connection, 2) << __func__ << " " << interface_name_ 171 << " (index " << interface_index_ << ") " 172 << is_default_ << " -> " << is_default; 173 if (is_default == is_default_) { 174 return; 175 } 176 177 routing_table_->SetDefaultMetric(interface_index_, GetMetric(is_default)); 178 179 is_default_ = is_default; 180 181 if (is_default) { 182 resolver_->SetDNSFromLists(dns_servers_, dns_domain_search_); 183 DeviceRefPtr device = device_info_->GetDevice(interface_index_); 184 if (device) { 185 device->RequestPortalDetection(); 186 } 187 } 188} 189 190void Connection::RequestRouting() { 191 if (routing_request_count_++ == 0) { 192 DeviceRefPtr device = device_info_->GetDevice(interface_index_); 193 DCHECK(device.get()); 194 if (!device.get()) { 195 LOG(ERROR) << "Device is NULL!"; 196 return; 197 } 198 device->DisableReversePathFilter(); 199 } 200} 201 202void Connection::ReleaseRouting() { 203 DCHECK(routing_request_count_ > 0); 204 if (--routing_request_count_ == 0) { 205 DeviceRefPtr device = device_info_->GetDevice(interface_index_); 206 DCHECK(device.get()); 207 if (!device.get()) { 208 LOG(ERROR) << "Device is NULL!"; 209 return; 210 } 211 device->EnableReversePathFilter(); 212 213 // Clear any cached routes that might have accumulated while reverse-path 214 // filtering was disabled. 215 routing_table_->FlushCache(); 216 } 217} 218 219bool Connection::RequestHostRoute(const IPAddress &address) { 220 // Set the prefix to be the entire address size. 221 IPAddress address_prefix(address); 222 address_prefix.set_prefix(address_prefix.GetLength() * 8); 223 224 // Do not set interface_index_ since this may not be the default route through 225 // which this destination can be found. However, we should tag the created 226 // route with our interface index so we can clean this route up when this 227 // connection closes. Also, add route query callback to determine the lower 228 // connection and bind to it. 229 if (!routing_table_->RequestRouteToHost( 230 address_prefix, 231 -1, 232 interface_index_, 233 RoutingTable::Query::Callback( 234 Bind(&Connection::OnRouteQueryResponse, 235 weak_ptr_factory_.GetWeakPtr())))) { 236 LOG(ERROR) << "Could not request route to " << address.ToString(); 237 return false; 238 } 239 240 return true; 241} 242 243// static 244bool Connection::FixGatewayReachability(IPAddress *local, 245 const IPAddress &gateway, 246 const IPAddress &peer) { 247 if (!gateway.IsValid()) { 248 LOG(WARNING) << "No gateway address was provided for this connection."; 249 return false; 250 } 251 252 if (peer.IsValid()) { 253 if (gateway.Equals(peer)) { 254 return true; 255 } 256 LOG(WARNING) << "Gateway address " 257 << gateway.ToString() 258 << " does not match peer address " 259 << peer.ToString(); 260 return false; 261 } 262 263 if (local->CanReachAddress(gateway)) { 264 return true; 265 } 266 267 LOG(WARNING) << "Gateway " 268 << gateway.ToString() 269 << " is unreachable from local address/prefix " 270 << local->ToString() << "/" << local->prefix(); 271 272 size_t original_prefix = local->prefix(); 273 size_t prefix = original_prefix - 1; 274 for (; prefix >= local->GetMinPrefixLength(); --prefix) { 275 local->set_prefix(prefix); 276 if (local->CanReachAddress(gateway)) { 277 break; 278 } 279 } 280 281 if (prefix < local->GetMinPrefixLength()) { 282 // Restore the original prefix since we cannot find a better one. 283 local->set_prefix(original_prefix); 284 return false; 285 } 286 287 LOG(WARNING) << "Mitigating this by setting local prefix to " << prefix; 288 return true; 289} 290 291uint32 Connection::GetMetric(bool is_default) { 292 // If this is not the default route, assign a metric based on the interface 293 // index. This way all non-default routes (even to the same gateway IP) end 294 // up with unique metrics so they do not collide. 295 return is_default ? kDefaultMetric : kNonDefaultMetricBase + interface_index_; 296} 297 298bool Connection::PinHostRoute(const IPConfig::Properties &properties) { 299 SLOG(Connection, 2) << __func__; 300 if (properties.gateway.empty() || properties.trusted_ip.empty()) { 301 return false; 302 } 303 304 IPAddress trusted_ip(properties.address_family); 305 if (!trusted_ip.SetAddressFromString(properties.trusted_ip)) { 306 LOG(ERROR) << "Failed to parse trusted_ip " 307 << properties.trusted_ip << "; ignored."; 308 return false; 309 } 310 311 return RequestHostRoute(trusted_ip); 312} 313 314void Connection::OnRouteQueryResponse(int interface_index, 315 const RoutingTableEntry &entry) { 316 SLOG(Connection, 2) << __func__ << "(" << interface_index << ", " 317 << entry.tag << ")"; 318 lower_binder_.Attach(NULL); 319 DeviceRefPtr device = device_info_->GetDevice(interface_index); 320 if (!device) { 321 LOG(ERROR) << "Unable to lookup device for index " << interface_index; 322 return; 323 } 324 ConnectionRefPtr connection = device->connection(); 325 if (!connection) { 326 LOG(ERROR) << "Device " << interface_index << " has no connection."; 327 return; 328 } 329 lower_binder_.Attach(connection); 330} 331 332void Connection::OnLowerDisconnect() { 333 SLOG(Connection, 2) << __func__ << "(" << interface_name_ << ")"; 334 // Ensures that |this| instance doesn't get destroyed in the middle of 335 // notifying the binders. This method needs to be separate from 336 // NotifyBindersOnDisconnect because the latter may be invoked by Connection's 337 // destructor when |this| instance's reference count is already 0. 338 ConnectionRefPtr connection(this); 339 connection->NotifyBindersOnDisconnect(); 340} 341 342void Connection::NotifyBindersOnDisconnect() { 343 // Note that this method may be invoked by the destructor. 344 SLOG(Connection, 2) << __func__ << "(" << interface_name_ << ")"; 345 346 // Unbinds the lower connection before notifying the binders. This ensures 347 // correct behavior in case of circular binding. 348 lower_binder_.Attach(NULL); 349 while (!binders_.empty()) { 350 // Pop the binder first and then notify it to ensure that each binder is 351 // notified only once. 352 Binder *binder = binders_.front(); 353 binders_.pop_front(); 354 binder->OnDisconnect(); 355 } 356} 357 358void Connection::AttachBinder(Binder *binder) { 359 SLOG(Connection, 2) << __func__ << "(" << interface_name_ << ")"; 360 binders_.push_back(binder); 361} 362 363void Connection::DetachBinder(Binder *binder) { 364 SLOG(Connection, 2) << __func__ << "(" << interface_name_ << ")"; 365 for (deque<Binder *>::iterator it = binders_.begin(); 366 it != binders_.end(); ++it) { 367 if (binder == *it) { 368 binders_.erase(it); 369 return; 370 } 371 } 372} 373 374} // namespace shill 375