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