blob: d258dd798acc3317b130bcaec15e560fc2f2a183 [file] [log] [blame]
mukesh agrawalddc378f2012-02-17 18:26:20 -08001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Paul Stewartdd60e452011-08-08 11:38:36 -07002// 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
Paul Stewart9a908082011-08-31 12:18:48 -07007#include <arpa/inet.h>
8#include <linux/rtnetlink.h>
9
10#include "shill/device_info.h"
Paul Stewartdd60e452011-08-08 11:38:36 -070011#include "shill/resolver.h"
12#include "shill/routing_table.h"
13#include "shill/rtnl_handler.h"
Ben Chanfad4a0b2012-04-18 15:49:59 -070014#include "shill/scope_logger.h"
Paul Stewartdd60e452011-08-08 11:38:36 -070015
Darin Petkov13e6d552012-05-09 14:22:23 +020016using base::Bind;
17using base::Closure;
18using base::Unretained;
19using std::deque;
Paul Stewartdd60e452011-08-08 11:38:36 -070020using std::string;
21
22namespace shill {
23
24// static
25const uint32 Connection::kDefaultMetric = 1;
26// static
Paul Stewart7cfca042011-12-08 14:18:17 -080027const uint32 Connection::kNonDefaultMetricBase = 10;
Paul Stewartdd60e452011-08-08 11:38:36 -070028
Darin Petkov13e6d552012-05-09 14:22:23 +020029Connection::Binder::Binder(const string &name,
30 const Closure &disconnect_callback)
31 : name_(name),
Darin Petkov13e6d552012-05-09 14:22:23 +020032 client_disconnect_callback_(disconnect_callback) {}
33
34Connection::Binder::~Binder() {
35 Attach(NULL);
36}
37
Darin Petkovef1f9fe2012-05-11 16:51:52 +020038void Connection::Binder::Attach(const ConnectionRefPtr &to_connection) {
Darin Petkov13e6d552012-05-09 14:22:23 +020039 if (connection_) {
40 connection_->DetachBinder(this);
41 LOG(INFO) << name_ << ": unbound from connection: "
42 << connection_->interface_name();
Darin Petkovef1f9fe2012-05-11 16:51:52 +020043 connection_.reset();
Darin Petkov13e6d552012-05-09 14:22:23 +020044 }
Darin Petkovef1f9fe2012-05-11 16:51:52 +020045 if (to_connection) {
46 connection_ = to_connection->weak_ptr_factory_.GetWeakPtr();
Darin Petkov13e6d552012-05-09 14:22:23 +020047 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();
Darin Petkovef1f9fe2012-05-11 16:51:52 +020056 connection_.reset();
Darin Petkov13e6d552012-05-09 14:22:23 +020057 if (!client_disconnect_callback_.is_null()) {
58 SLOG(Connection, 2) << "Running client disconnect callback.";
59 client_disconnect_callback_.Run();
60 }
61}
62
Paul Stewart9a908082011-08-31 12:18:48 -070063Connection::Connection(int interface_index,
64 const std::string& interface_name,
Paul Stewarte00600e2012-03-16 07:08:00 -070065 Technology::Identifier technology,
Paul Stewart9a908082011-08-31 12:18:48 -070066 const DeviceInfo *device_info)
Darin Petkov13e6d552012-05-09 14:22:23 +020067 : weak_ptr_factory_(this),
68 is_default_(false),
Paul Stewart4a6748d2012-07-17 14:31:36 -070069 has_broadcast_domain_(false),
Paul Stewartc8f4bef2011-12-13 09:45:51 -080070 routing_request_count_(0),
Paul Stewartdd60e452011-08-08 11:38:36 -070071 interface_index_(interface_index),
72 interface_name_(interface_name),
Paul Stewarte00600e2012-03-16 07:08:00 -070073 technology_(technology),
Paul Stewart4a6748d2012-07-17 14:31:36 -070074 local_(IPAddress::kFamilyUnknown),
75 gateway_(IPAddress::kFamilyUnknown),
Darin Petkov13e6d552012-05-09 14:22:23 +020076 lower_binder_(
77 interface_name_,
78 // Connection owns a single instance of |lower_binder_| so it's safe
79 // to use an Unretained callback.
80 Bind(&Connection::OnLowerDisconnect, Unretained(this))),
Paul Stewart9a908082011-08-31 12:18:48 -070081 device_info_(device_info),
Paul Stewartdd60e452011-08-08 11:38:36 -070082 resolver_(Resolver::GetInstance()),
83 routing_table_(RoutingTable::GetInstance()),
84 rtnl_handler_(RTNLHandler::GetInstance()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -070085 SLOG(Connection, 2) << __func__ << "(" << interface_index << ", "
86 << interface_name << ", "
87 << Technology::NameFromIdentifier(technology) << ")";
Paul Stewartdd60e452011-08-08 11:38:36 -070088}
89
90Connection::~Connection() {
Ben Chanfad4a0b2012-04-18 15:49:59 -070091 SLOG(Connection, 2) << __func__ << " " << interface_name_;
Paul Stewart9a908082011-08-31 12:18:48 -070092
Darin Petkov13e6d552012-05-09 14:22:23 +020093 NotifyBindersOnDisconnect();
94
Paul Stewartc8f4bef2011-12-13 09:45:51 -080095 DCHECK(!routing_request_count_);
Thieu Lefb46caf2012-03-08 11:57:15 -080096 routing_table_->FlushRoutes(interface_index_);
Paul Stewarte93b0382012-04-24 13:11:28 -070097 routing_table_->FlushRoutesWithTag(interface_index_);
Paul Stewart9a908082011-08-31 12:18:48 -070098 device_info_->FlushAddresses(interface_index_);
Paul Stewartdd60e452011-08-08 11:38:36 -070099}
100
101void Connection::UpdateFromIPConfig(const IPConfigRefPtr &config) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700102 SLOG(Connection, 2) << __func__ << " " << interface_name_;
Paul Stewarte6132022011-08-16 09:11:02 -0700103
Paul Stewart9a908082011-08-31 12:18:48 -0700104 const IPConfig::Properties &properties = config->properties();
Paul Stewarte93b0382012-04-24 13:11:28 -0700105 if (!properties.trusted_ip.empty() && !PinHostRoute(properties)) {
106 LOG(ERROR) << "Unable to pin host route to " << properties.trusted_ip;
107 return;
108 }
109
Paul Stewart9a908082011-08-31 12:18:48 -0700110 IPAddress local(properties.address_family);
111 if (!local.SetAddressFromString(properties.address)) {
112 LOG(ERROR) << "Local address " << properties.address << " is invalid";
113 return;
114 }
Paul Stewart48100b02012-03-19 07:53:52 -0700115 local.set_prefix(properties.subnet_prefix);
Paul Stewart9a908082011-08-31 12:18:48 -0700116
117 IPAddress broadcast(properties.address_family);
Paul Stewart1062d9d2012-04-27 10:42:27 -0700118 if (properties.broadcast_address.empty()) {
Paul Stewartfe1c0e12012-04-30 19:57:04 -0700119 if (properties.peer_address.empty()) {
Paul Stewart1062d9d2012-04-27 10:42:27 -0700120 LOG(WARNING) << "Broadcast address is not set. Using default.";
Paul Stewartfe1c0e12012-04-30 19:57:04 -0700121 broadcast = local.GetDefaultBroadcast();
Paul Stewart1062d9d2012-04-27 10:42:27 -0700122 }
123 } else if (!broadcast.SetAddressFromString(properties.broadcast_address)) {
Paul Stewart9a908082011-08-31 12:18:48 -0700124 LOG(ERROR) << "Broadcast address " << properties.broadcast_address
125 << " is invalid";
126 return;
127 }
128
Paul Stewart48100b02012-03-19 07:53:52 -0700129 IPAddress peer(properties.address_family);
130 if (!properties.peer_address.empty() &&
131 !peer.SetAddressFromString(properties.peer_address)) {
132 LOG(ERROR) << "Peer address " << properties.peer_address
133 << " is invalid";
134 return;
135 }
136
Paul Stewarte78ec542012-06-08 18:28:50 -0700137 IPAddress gateway(properties.address_family);
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700138 if (!properties.gateway.empty() &&
Paul Stewarte78ec542012-06-08 18:28:50 -0700139 !gateway.SetAddressFromString(properties.gateway)) {
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700140 LOG(ERROR) << "Gateway address " << properties.peer_address
141 << " is invalid";
142 return;
143 }
144
Paul Stewarte78ec542012-06-08 18:28:50 -0700145 if (!FixGatewayReachability(&local, &peer, gateway)) {
Paul Stewart53a30382012-04-26 09:06:59 -0700146 LOG(WARNING) << "Expect limited network connectivity.";
147 }
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700148
Paul Stewart05a42c22012-08-02 16:47:21 -0700149 if (device_info_->HasOtherAddress(interface_index_, local)) {
150 // The address has changed for this interface. We need to flush
151 // everything and start over.
152 LOG(INFO) << __func__ << ": Flushing old addresses and routes.";
153 routing_table_->FlushRoutes(interface_index_);
154 device_info_->FlushAddresses(interface_index_);
155 }
156
Paul Stewarte78ec542012-06-08 18:28:50 -0700157 LOG(INFO) << __func__ << ": Installing with parameters:"
158 << " local=" << local.ToString()
159 << " broadcast=" << broadcast.ToString()
160 << " peer=" << peer.ToString()
161 << " gateway=" << gateway.ToString();
Paul Stewart48100b02012-03-19 07:53:52 -0700162 rtnl_handler_->AddInterfaceAddress(interface_index_, local, broadcast, peer);
Paul Stewartdd60e452011-08-08 11:38:36 -0700163
Paul Stewarte78ec542012-06-08 18:28:50 -0700164 if (gateway.IsValid()) {
165 routing_table_->SetDefaultRoute(interface_index_, gateway,
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700166 GetMetric(is_default_));
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700167 }
Paul Stewartdd60e452011-08-08 11:38:36 -0700168
Paul Stewart3f68bb12012-03-15 13:33:10 -0700169 // Install any explicitly configured routes at the default metric.
170 routing_table_->ConfigureRoutes(interface_index_, config, kDefaultMetric);
171
Paul Stewartdd60e452011-08-08 11:38:36 -0700172 // Save a copy of the last non-null DNS config
173 if (!config->properties().dns_servers.empty()) {
174 dns_servers_ = config->properties().dns_servers;
175 dns_domain_search_ = config->properties().domain_search;
176 }
177
Paul Stewart10241e32012-04-23 18:15:06 -0700178 ipconfig_rpc_identifier_ = config->GetRpcIdentifier();
179
Paul Stewartdd60e452011-08-08 11:38:36 -0700180 if (is_default_) {
181 resolver_->SetDNSFromIPConfig(config);
182 }
Paul Stewart4a6748d2012-07-17 14:31:36 -0700183
184 local_ = local;
185 gateway_ = gateway;
186 has_broadcast_domain_ = !peer.IsValid();
Paul Stewartdd60e452011-08-08 11:38:36 -0700187}
188
Paul Stewartc1dec4d2011-12-08 15:25:28 -0800189void Connection::SetIsDefault(bool is_default) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700190 SLOG(Connection, 2) << __func__ << " " << interface_name_
191 << " (index " << interface_index_ << ") "
192 << is_default_ << " -> " << is_default;
Paul Stewartdd60e452011-08-08 11:38:36 -0700193 if (is_default == is_default_) {
194 return;
195 }
196
Paul Stewart7cfca042011-12-08 14:18:17 -0800197 routing_table_->SetDefaultMetric(interface_index_, GetMetric(is_default));
Paul Stewartdd60e452011-08-08 11:38:36 -0700198
Paul Stewartc681fa02012-03-02 19:40:04 -0800199 is_default_ = is_default;
200
Paul Stewartdd60e452011-08-08 11:38:36 -0700201 if (is_default) {
202 resolver_->SetDNSFromLists(dns_servers_, dns_domain_search_);
Paul Stewartc681fa02012-03-02 19:40:04 -0800203 DeviceRefPtr device = device_info_->GetDevice(interface_index_);
204 if (device) {
205 device->RequestPortalDetection();
206 }
Paul Stewartdd60e452011-08-08 11:38:36 -0700207 }
Paul Stewarte78ec542012-06-08 18:28:50 -0700208 routing_table_->FlushCache();
Paul Stewartdd60e452011-08-08 11:38:36 -0700209}
210
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800211void Connection::RequestRouting() {
212 if (routing_request_count_++ == 0) {
213 DeviceRefPtr device = device_info_->GetDevice(interface_index_);
214 DCHECK(device.get());
215 if (!device.get()) {
216 LOG(ERROR) << "Device is NULL!";
217 return;
218 }
219 device->DisableReversePathFilter();
220 }
221}
222
223void Connection::ReleaseRouting() {
224 DCHECK(routing_request_count_ > 0);
225 if (--routing_request_count_ == 0) {
226 DeviceRefPtr device = device_info_->GetDevice(interface_index_);
227 DCHECK(device.get());
228 if (!device.get()) {
229 LOG(ERROR) << "Device is NULL!";
230 return;
231 }
232 device->EnableReversePathFilter();
233
234 // Clear any cached routes that might have accumulated while reverse-path
235 // filtering was disabled.
236 routing_table_->FlushCache();
237 }
238}
239
Paul Stewartf748a362012-03-07 12:01:20 -0800240bool Connection::RequestHostRoute(const IPAddress &address) {
241 // Set the prefix to be the entire address size.
242 IPAddress address_prefix(address);
243 address_prefix.set_prefix(address_prefix.GetLength() * 8);
244
Darin Petkov13e6d552012-05-09 14:22:23 +0200245 // Do not set interface_index_ since this may not be the default route through
246 // which this destination can be found. However, we should tag the created
247 // route with our interface index so we can clean this route up when this
248 // connection closes. Also, add route query callback to determine the lower
249 // connection and bind to it.
250 if (!routing_table_->RequestRouteToHost(
251 address_prefix,
252 -1,
253 interface_index_,
Darin Petkov5eb05422012-05-11 15:45:25 +0200254 Bind(&Connection::OnRouteQueryResponse,
255 weak_ptr_factory_.GetWeakPtr()))) {
Paul Stewartf748a362012-03-07 12:01:20 -0800256 LOG(ERROR) << "Could not request route to " << address.ToString();
257 return false;
258 }
259
260 return true;
261}
262
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700263// static
Paul Stewart53a30382012-04-26 09:06:59 -0700264bool Connection::FixGatewayReachability(IPAddress *local,
Paul Stewart49258292012-05-26 06:37:14 -0700265 IPAddress *peer,
266 const IPAddress &gateway) {
Paul Stewart53a30382012-04-26 09:06:59 -0700267 if (!gateway.IsValid()) {
268 LOG(WARNING) << "No gateway address was provided for this connection.";
269 return false;
270 }
271
Paul Stewart49258292012-05-26 06:37:14 -0700272 if (peer->IsValid()) {
273 if (gateway.Equals(*peer)) {
Paul Stewart53a30382012-04-26 09:06:59 -0700274 return true;
275 }
276 LOG(WARNING) << "Gateway address "
277 << gateway.ToString()
278 << " does not match peer address "
Paul Stewart49258292012-05-26 06:37:14 -0700279 << peer->ToString();
Paul Stewart53a30382012-04-26 09:06:59 -0700280 return false;
281 }
282
283 if (local->CanReachAddress(gateway)) {
284 return true;
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700285 }
286
287 LOG(WARNING) << "Gateway "
288 << gateway.ToString()
289 << " is unreachable from local address/prefix "
290 << local->ToString() << "/" << local->prefix();
291
Paul Stewart2aa5d7d2012-06-21 22:16:54 -0700292 bool found_new_prefix = false;
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700293 size_t original_prefix = local->prefix();
Paul Stewart2aa5d7d2012-06-21 22:16:54 -0700294 // Only try to expand the netmask if the configured prefix is
295 // less than "all ones". This special-cases the "all-ones"
296 // prefix as a forced conversion to point-to-point networking.
297 if (local->prefix() < IPAddress::GetMaxPrefixLength(local->family())) {
298 size_t prefix = original_prefix - 1;
299 for (; prefix >= local->GetMinPrefixLength(); --prefix) {
300 local->set_prefix(prefix);
301 if (local->CanReachAddress(gateway)) {
302 found_new_prefix = true;
303 break;
304 }
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700305 }
306 }
307
Paul Stewart2aa5d7d2012-06-21 22:16:54 -0700308 if (!found_new_prefix) {
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700309 // Restore the original prefix since we cannot find a better one.
310 local->set_prefix(original_prefix);
Paul Stewart49258292012-05-26 06:37:14 -0700311 DCHECK(!peer->IsValid());
312 LOG(WARNING) << "Assuming point-to-point configuration.";
313 *peer = gateway;
314 return true;
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700315 }
Paul Stewart53a30382012-04-26 09:06:59 -0700316
Paul Stewart2aa5d7d2012-06-21 22:16:54 -0700317 LOG(WARNING) << "Mitigating this by setting local prefix to "
318 << local->prefix();
Paul Stewart53a30382012-04-26 09:06:59 -0700319 return true;
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700320}
321
Paul Stewart7cfca042011-12-08 14:18:17 -0800322uint32 Connection::GetMetric(bool is_default) {
323 // If this is not the default route, assign a metric based on the interface
324 // index. This way all non-default routes (even to the same gateway IP) end
325 // up with unique metrics so they do not collide.
326 return is_default ? kDefaultMetric : kNonDefaultMetricBase + interface_index_;
327}
328
Paul Stewarte93b0382012-04-24 13:11:28 -0700329bool Connection::PinHostRoute(const IPConfig::Properties &properties) {
330 SLOG(Connection, 2) << __func__;
331 if (properties.gateway.empty() || properties.trusted_ip.empty()) {
Darin Petkove8587e32012-07-02 13:56:07 +0200332 LOG_IF(ERROR, properties.gateway.empty())
333 << "No gateway -- unable to pin host route.";
334 LOG_IF(ERROR, properties.trusted_ip.empty())
335 << "No trusted IP -- unable to pin host route.";
Paul Stewarte93b0382012-04-24 13:11:28 -0700336 return false;
337 }
338
339 IPAddress trusted_ip(properties.address_family);
340 if (!trusted_ip.SetAddressFromString(properties.trusted_ip)) {
341 LOG(ERROR) << "Failed to parse trusted_ip "
342 << properties.trusted_ip << "; ignored.";
343 return false;
344 }
345
346 return RequestHostRoute(trusted_ip);
347}
348
Darin Petkov13e6d552012-05-09 14:22:23 +0200349void Connection::OnRouteQueryResponse(int interface_index,
350 const RoutingTableEntry &entry) {
351 SLOG(Connection, 2) << __func__ << "(" << interface_index << ", "
Darin Petkov5eb05422012-05-11 15:45:25 +0200352 << entry.tag << ")" << " @ " << interface_name_;
Darin Petkov13e6d552012-05-09 14:22:23 +0200353 lower_binder_.Attach(NULL);
354 DeviceRefPtr device = device_info_->GetDevice(interface_index);
355 if (!device) {
356 LOG(ERROR) << "Unable to lookup device for index " << interface_index;
357 return;
358 }
359 ConnectionRefPtr connection = device->connection();
360 if (!connection) {
361 LOG(ERROR) << "Device " << interface_index << " has no connection.";
362 return;
363 }
364 lower_binder_.Attach(connection);
Paul Stewart4a6748d2012-07-17 14:31:36 -0700365 connection->CreateGatewayRoute();
366}
367
368bool Connection::CreateGatewayRoute() {
369 // Ensure that the gateway for the lower connection remains reachable,
370 // since we may create routes that conflict with it.
371 if (!has_broadcast_domain_) {
372 return false;
373 }
374 // It is not worth keeping track of this route, since it is benign,
375 // and only pins persistent state that was already true of the connection.
376 // If DHCP parameters change later (without the connection having been
377 // destroyed and recreated), the binding processes will likely terminate
378 // and restart, causing a new link route to be created.
379 return routing_table_->CreateLinkRoute(interface_index_, local_, gateway_);
Darin Petkov13e6d552012-05-09 14:22:23 +0200380}
381
382void Connection::OnLowerDisconnect() {
Darin Petkov5eb05422012-05-11 15:45:25 +0200383 SLOG(Connection, 2) << __func__ << " @ " << interface_name_;
Darin Petkov13e6d552012-05-09 14:22:23 +0200384 // Ensures that |this| instance doesn't get destroyed in the middle of
385 // notifying the binders. This method needs to be separate from
386 // NotifyBindersOnDisconnect because the latter may be invoked by Connection's
387 // destructor when |this| instance's reference count is already 0.
388 ConnectionRefPtr connection(this);
389 connection->NotifyBindersOnDisconnect();
390}
391
392void Connection::NotifyBindersOnDisconnect() {
393 // Note that this method may be invoked by the destructor.
Darin Petkov5eb05422012-05-11 15:45:25 +0200394 SLOG(Connection, 2) << __func__ << " @ " << interface_name_;
Darin Petkov13e6d552012-05-09 14:22:23 +0200395
396 // Unbinds the lower connection before notifying the binders. This ensures
397 // correct behavior in case of circular binding.
398 lower_binder_.Attach(NULL);
399 while (!binders_.empty()) {
400 // Pop the binder first and then notify it to ensure that each binder is
401 // notified only once.
402 Binder *binder = binders_.front();
403 binders_.pop_front();
404 binder->OnDisconnect();
405 }
406}
407
408void Connection::AttachBinder(Binder *binder) {
Darin Petkov5eb05422012-05-11 15:45:25 +0200409 SLOG(Connection, 2) << __func__ << "(" << binder->name() << ")" << " @ "
410 << interface_name_;
Darin Petkov13e6d552012-05-09 14:22:23 +0200411 binders_.push_back(binder);
412}
413
414void Connection::DetachBinder(Binder *binder) {
Darin Petkov5eb05422012-05-11 15:45:25 +0200415 SLOG(Connection, 2) << __func__ << "(" << binder->name() << ")" << " @ "
416 << interface_name_;
Darin Petkov13e6d552012-05-09 14:22:23 +0200417 for (deque<Binder *>::iterator it = binders_.begin();
418 it != binders_.end(); ++it) {
419 if (binder == *it) {
420 binders_.erase(it);
421 return;
422 }
423 }
424}
425
Paul Stewartdd60e452011-08-08 11:38:36 -0700426} // namespace shill