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