connection.cc revision a016312b7470e124774aec197e3b43bc795637c3
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/logging.h"
12#include "shill/resolver.h"
13#include "shill/routing_table.h"
14#include "shill/rtnl_handler.h"
15
16using base::Bind;
17using base::Closure;
18using base::Unretained;
19using std::deque;
20using std::string;
21using std::vector;
22
23namespace shill {
24
25// static
26const uint32 Connection::kDefaultMetric = 1;
27// static
28const uint32 Connection::kNonDefaultMetricBase = 10;
29
30Connection::Binder::Binder(const string &name,
31                           const Closure &disconnect_callback)
32    : name_(name),
33      client_disconnect_callback_(disconnect_callback) {}
34
35Connection::Binder::~Binder() {
36  Attach(NULL);
37}
38
39void Connection::Binder::Attach(const ConnectionRefPtr &to_connection) {
40  if (connection_) {
41    connection_->DetachBinder(this);
42    LOG(INFO) << name_ << ": unbound from connection: "
43              << connection_->interface_name();
44    connection_.reset();
45  }
46  if (to_connection) {
47    connection_ = to_connection->weak_ptr_factory_.GetWeakPtr();
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_.reset();
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                       bool is_short_dns_timeout_enabled)
69    : weak_ptr_factory_(this),
70      is_default_(false),
71      has_broadcast_domain_(false),
72      routing_request_count_(0),
73      interface_index_(interface_index),
74      interface_name_(interface_name),
75      technology_(technology),
76      local_(IPAddress::kFamilyUnknown),
77      gateway_(IPAddress::kFamilyUnknown),
78      lower_binder_(
79          interface_name_,
80          // Connection owns a single instance of |lower_binder_| so it's safe
81          // to use an Unretained callback.
82          Bind(&Connection::OnLowerDisconnect, Unretained(this))),
83      dns_timeout_parameters_(Resolver::kDefaultTimeout),
84      device_info_(device_info),
85      resolver_(Resolver::GetInstance()),
86      routing_table_(RoutingTable::GetInstance()),
87      rtnl_handler_(RTNLHandler::GetInstance()) {
88  SLOG(Connection, 2) << __func__ << "(" << interface_index << ", "
89                      << interface_name << ", "
90                      << Technology::NameFromIdentifier(technology) << ")";
91  if (is_short_dns_timeout_enabled) {
92    dns_timeout_parameters_ = Resolver::kShortTimeout;
93  }
94}
95
96Connection::~Connection() {
97  SLOG(Connection, 2) << __func__ << " " << interface_name_;
98
99  NotifyBindersOnDisconnect();
100
101  DCHECK(!routing_request_count_);
102  routing_table_->FlushRoutes(interface_index_);
103  routing_table_->FlushRoutesWithTag(interface_index_);
104  device_info_->FlushAddresses(interface_index_);
105}
106
107void Connection::UpdateFromIPConfig(const IPConfigRefPtr &config) {
108  SLOG(Connection, 2) << __func__ << " " << interface_name_;
109
110  const IPConfig::Properties &properties = config->properties();
111  if (!properties.trusted_ip.empty() && !PinHostRoute(properties)) {
112    LOG(ERROR) << "Unable to pin host route to " << properties.trusted_ip;
113    return;
114  }
115
116  IPAddress local(properties.address_family);
117  if (!local.SetAddressFromString(properties.address)) {
118    LOG(ERROR) << "Local address " << properties.address << " is invalid";
119    return;
120  }
121  local.set_prefix(properties.subnet_prefix);
122
123  IPAddress broadcast(properties.address_family);
124  if (properties.broadcast_address.empty()) {
125    if (properties.peer_address.empty()) {
126      LOG(WARNING) << "Broadcast address is not set.  Using default.";
127      broadcast = local.GetDefaultBroadcast();
128    }
129  } else if (!broadcast.SetAddressFromString(properties.broadcast_address)) {
130    LOG(ERROR) << "Broadcast address " << properties.broadcast_address
131               << " is invalid";
132    return;
133  }
134
135  IPAddress peer(properties.address_family);
136  if (!properties.peer_address.empty() &&
137      !peer.SetAddressFromString(properties.peer_address)) {
138    LOG(ERROR) << "Peer address " << properties.peer_address
139               << " is invalid";
140    return;
141  }
142
143  IPAddress gateway(properties.address_family);
144  if (!properties.gateway.empty() &&
145      !gateway.SetAddressFromString(properties.gateway)) {
146    LOG(ERROR) << "Gateway address " << properties.peer_address
147               << " is invalid";
148    return;
149  }
150
151  if (!FixGatewayReachability(&local, &peer, gateway)) {
152    LOG(WARNING) << "Expect limited network connectivity.";
153  }
154
155  if (device_info_->HasOtherAddress(interface_index_, local)) {
156    // The address has changed for this interface.  We need to flush
157    // everything and start over.
158    LOG(INFO) << __func__ << ": Flushing old addresses and routes.";
159    routing_table_->FlushRoutes(interface_index_);
160    device_info_->FlushAddresses(interface_index_);
161  }
162
163  LOG(INFO) << __func__ << ": Installing with parameters:"
164            << " local=" << local.ToString()
165            << " broadcast=" << broadcast.ToString()
166            << " peer=" << peer.ToString()
167            << " gateway=" << gateway.ToString();
168  rtnl_handler_->AddInterfaceAddress(interface_index_, local, broadcast, peer);
169
170  if (gateway.IsValid()) {
171    routing_table_->SetDefaultRoute(interface_index_, gateway,
172                                    GetMetric(is_default_));
173  }
174
175  // Install any explicitly configured routes at the default metric.
176  routing_table_->ConfigureRoutes(interface_index_, config, kDefaultMetric);
177
178  if (properties.blackhole_ipv6) {
179    routing_table_->CreateBlackholeRoute(interface_index_,
180                                         IPAddress::kFamilyIPv6,
181                                         kDefaultMetric);
182  }
183
184  // Save a copy of the last non-null DNS config.
185  if (!config->properties().dns_servers.empty()) {
186    dns_servers_ = config->properties().dns_servers;
187  }
188
189  if (!config->properties().domain_search.empty()) {
190    dns_domain_search_ = config->properties().domain_search;
191  }
192
193  if (!config->properties().domain_name.empty()) {
194    dns_domain_name_ = config->properties().domain_name;
195  }
196
197  ipconfig_rpc_identifier_ = config->GetRpcIdentifier();
198
199  if (is_default_) {
200    PushDNSConfig();
201  }
202
203  local_ = local;
204  gateway_ = gateway;
205  has_broadcast_domain_ = !peer.IsValid();
206}
207
208void Connection::SetIsDefault(bool is_default) {
209  SLOG(Connection, 2) << __func__ << " " << interface_name_
210                      << " (index " << interface_index_ << ") "
211                      << is_default_ << " -> " << is_default;
212  if (is_default == is_default_) {
213    return;
214  }
215
216  routing_table_->SetDefaultMetric(interface_index_, GetMetric(is_default));
217
218  is_default_ = is_default;
219
220  if (is_default) {
221    PushDNSConfig();
222    DeviceRefPtr device = device_info_->GetDevice(interface_index_);
223    if (device) {
224      device->RequestPortalDetection();
225    }
226  }
227  routing_table_->FlushCache();
228}
229
230void Connection::PushDNSConfig() {
231  vector<string> domain_search = dns_domain_search_;
232  if (domain_search.empty() && !dns_domain_name_.empty()) {
233    SLOG(Connection, 2) << "Setting domain search to domain name "
234                        << dns_domain_name_;
235    domain_search.push_back(dns_domain_name_ + ".");
236  }
237  resolver_->SetDNSFromLists(dns_servers_, domain_search,
238                             dns_timeout_parameters_);
239}
240
241void Connection::RequestRouting() {
242  if (routing_request_count_++ == 0) {
243    DeviceRefPtr device = device_info_->GetDevice(interface_index_);
244    DCHECK(device.get());
245    if (!device.get()) {
246      LOG(ERROR) << "Device is NULL!";
247      return;
248    }
249    device->DisableReversePathFilter();
250  }
251}
252
253void Connection::ReleaseRouting() {
254  DCHECK(routing_request_count_ > 0);
255  if (--routing_request_count_ == 0) {
256    DeviceRefPtr device = device_info_->GetDevice(interface_index_);
257    DCHECK(device.get());
258    if (!device.get()) {
259      LOG(ERROR) << "Device is NULL!";
260      return;
261    }
262    device->EnableReversePathFilter();
263
264    // Clear any cached routes that might have accumulated while reverse-path
265    // filtering was disabled.
266    routing_table_->FlushCache();
267  }
268}
269
270bool Connection::RequestHostRoute(const IPAddress &address) {
271  // Set the prefix to be the entire address size.
272  IPAddress address_prefix(address);
273  address_prefix.set_prefix(address_prefix.GetLength() * 8);
274
275  // Do not set interface_index_ since this may not be the default route through
276  // which this destination can be found.  However, we should tag the created
277  // route with our interface index so we can clean this route up when this
278  // connection closes.  Also, add route query callback to determine the lower
279  // connection and bind to it.
280  if (!routing_table_->RequestRouteToHost(
281          address_prefix,
282          -1,
283          interface_index_,
284          Bind(&Connection::OnRouteQueryResponse,
285               weak_ptr_factory_.GetWeakPtr()))) {
286    LOG(ERROR) << "Could not request route to " << address.ToString();
287    return false;
288  }
289
290  return true;
291}
292
293// static
294bool Connection::FixGatewayReachability(IPAddress *local,
295                                        IPAddress *peer,
296                                        const IPAddress &gateway) {
297  if (!gateway.IsValid()) {
298    LOG(WARNING) << "No gateway address was provided for this connection.";
299    return false;
300  }
301
302  if (peer->IsValid()) {
303    if (gateway.Equals(*peer)) {
304      return true;
305    }
306    LOG(WARNING) << "Gateway address "
307                 << gateway.ToString()
308                 << " does not match peer address "
309                 << peer->ToString();
310    return false;
311  }
312
313  if (local->CanReachAddress(gateway)) {
314    return true;
315  }
316
317  LOG(WARNING) << "Gateway "
318               << gateway.ToString()
319               << " is unreachable from local address/prefix "
320               << local->ToString() << "/" << local->prefix();
321
322  bool found_new_prefix = false;
323  size_t original_prefix = local->prefix();
324  // Only try to expand the netmask if the configured prefix is
325  // less than "all ones".  This special-cases the "all-ones"
326  // prefix as a forced conversion to point-to-point networking.
327  if (local->prefix() < IPAddress::GetMaxPrefixLength(local->family())) {
328    size_t prefix = original_prefix - 1;
329    for (; prefix >= local->GetMinPrefixLength(); --prefix) {
330      local->set_prefix(prefix);
331      if (local->CanReachAddress(gateway)) {
332        found_new_prefix = true;
333        break;
334      }
335    }
336  }
337
338  if (!found_new_prefix) {
339    // Restore the original prefix since we cannot find a better one.
340    local->set_prefix(original_prefix);
341    DCHECK(!peer->IsValid());
342    LOG(WARNING) << "Assuming point-to-point configuration.";
343    *peer = gateway;
344    return true;
345  }
346
347  LOG(WARNING) << "Mitigating this by setting local prefix to "
348               << local->prefix();
349  return true;
350}
351
352uint32 Connection::GetMetric(bool is_default) {
353  // If this is not the default route, assign a metric based on the interface
354  // index.  This way all non-default routes (even to the same gateway IP) end
355  // up with unique metrics so they do not collide.
356  return is_default ? kDefaultMetric : kNonDefaultMetricBase + interface_index_;
357}
358
359bool Connection::PinHostRoute(const IPConfig::Properties &properties) {
360  SLOG(Connection, 2) << __func__;
361  if (properties.gateway.empty() || properties.trusted_ip.empty()) {
362    LOG_IF(ERROR, properties.gateway.empty())
363        << "No gateway -- unable to pin host route.";
364    LOG_IF(ERROR, properties.trusted_ip.empty())
365        << "No trusted IP -- unable to pin host route.";
366    return false;
367  }
368
369  IPAddress trusted_ip(properties.address_family);
370  if (!trusted_ip.SetAddressFromString(properties.trusted_ip)) {
371    LOG(ERROR) << "Failed to parse trusted_ip "
372               << properties.trusted_ip << "; ignored.";
373    return false;
374  }
375
376  return RequestHostRoute(trusted_ip);
377}
378
379void Connection::OnRouteQueryResponse(int interface_index,
380                                      const RoutingTableEntry &entry) {
381  SLOG(Connection, 2) << __func__ << "(" << interface_index << ", "
382                      << entry.tag << ")" << " @ " << interface_name_;
383  lower_binder_.Attach(NULL);
384  DeviceRefPtr device = device_info_->GetDevice(interface_index);
385  if (!device) {
386    LOG(ERROR) << "Unable to lookup device for index " << interface_index;
387    return;
388  }
389  ConnectionRefPtr connection = device->connection();
390  if (!connection) {
391    LOG(ERROR) << "Device " << interface_index << " has no connection.";
392    return;
393  }
394  lower_binder_.Attach(connection);
395  connection->CreateGatewayRoute();
396}
397
398bool Connection::CreateGatewayRoute() {
399  // Ensure that the gateway for the lower connection remains reachable,
400  // since we may create routes that conflict with it.
401  if (!has_broadcast_domain_) {
402    return false;
403  }
404  // It is not worth keeping track of this route, since it is benign,
405  // and only pins persistent state that was already true of the connection.
406  // If DHCP parameters change later (without the connection having been
407  // destroyed and recreated), the binding processes will likely terminate
408  // and restart, causing a new link route to be created.
409  return routing_table_->CreateLinkRoute(interface_index_, local_, gateway_);
410}
411
412void Connection::OnLowerDisconnect() {
413  SLOG(Connection, 2) << __func__ << " @ " << interface_name_;
414  // Ensures that |this| instance doesn't get destroyed in the middle of
415  // notifying the binders. This method needs to be separate from
416  // NotifyBindersOnDisconnect because the latter may be invoked by Connection's
417  // destructor when |this| instance's reference count is already 0.
418  ConnectionRefPtr connection(this);
419  connection->NotifyBindersOnDisconnect();
420}
421
422void Connection::NotifyBindersOnDisconnect() {
423  // Note that this method may be invoked by the destructor.
424  SLOG(Connection, 2) << __func__ << " @ " << interface_name_;
425
426  // Unbinds the lower connection before notifying the binders. This ensures
427  // correct behavior in case of circular binding.
428  lower_binder_.Attach(NULL);
429  while (!binders_.empty()) {
430    // Pop the binder first and then notify it to ensure that each binder is
431    // notified only once.
432    Binder *binder = binders_.front();
433    binders_.pop_front();
434    binder->OnDisconnect();
435  }
436}
437
438void Connection::AttachBinder(Binder *binder) {
439  SLOG(Connection, 2) << __func__ << "(" << binder->name() << ")" << " @ "
440                      << interface_name_;
441  binders_.push_back(binder);
442}
443
444void Connection::DetachBinder(Binder *binder) {
445  SLOG(Connection, 2) << __func__ << "(" << binder->name() << ")" << " @ "
446                      << interface_name_;
447  for (deque<Binder *>::iterator it = binders_.begin();
448       it != binders_.end(); ++it) {
449    if (binder == *it) {
450      binders_.erase(it);
451      return;
452    }
453  }
454}
455
456}  // namespace shill
457