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