connection.cc revision 73fcc3f6560240d0d5dde15927cc0b51dc692dd5
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  IPAddress gateway(properties.address_family);
112  if (!properties.gateway.empty() &&
113      !gateway.SetAddressFromString(properties.gateway)) {
114    LOG(ERROR) << "Gateway address " << properties.gateway << " is invalid";
115    return;
116  }
117
118  IPAddress trusted_ip(properties.address_family);
119  if (!properties.trusted_ip.empty()) {
120    if (!trusted_ip.SetAddressFromString(properties.trusted_ip)) {
121      LOG(ERROR) << "Trusted IP address "
122                 << properties.trusted_ip << " is invalid";
123      return;
124    }
125    if (!PinHostRoute(trusted_ip, gateway)) {
126      LOG(ERROR) << "Unable to pin host route to " << properties.trusted_ip;
127      return;
128    }
129  }
130
131  IPAddress local(properties.address_family);
132  if (!local.SetAddressFromString(properties.address)) {
133    LOG(ERROR) << "Local address " << properties.address << " is invalid";
134    return;
135  }
136  local.set_prefix(properties.subnet_prefix);
137
138  IPAddress broadcast(properties.address_family);
139  if (properties.broadcast_address.empty()) {
140    if (properties.peer_address.empty()) {
141      LOG(WARNING) << "Broadcast address is not set.  Using default.";
142      broadcast = local.GetDefaultBroadcast();
143    }
144  } else if (!broadcast.SetAddressFromString(properties.broadcast_address)) {
145    LOG(ERROR) << "Broadcast address " << properties.broadcast_address
146               << " is invalid";
147    return;
148  }
149
150  IPAddress peer(properties.address_family);
151  if (!properties.peer_address.empty() &&
152      !peer.SetAddressFromString(properties.peer_address)) {
153    LOG(ERROR) << "Peer address " << properties.peer_address
154               << " is invalid";
155    return;
156  }
157
158  if (!FixGatewayReachability(&local, &peer, &gateway, trusted_ip)) {
159    LOG(WARNING) << "Expect limited network connectivity.";
160  }
161
162  if (device_info_->HasOtherAddress(interface_index_, local)) {
163    // The address has changed for this interface.  We need to flush
164    // everything and start over.
165    LOG(INFO) << __func__ << ": Flushing old addresses and routes.";
166    routing_table_->FlushRoutes(interface_index_);
167    device_info_->FlushAddresses(interface_index_);
168  }
169
170  LOG(INFO) << __func__ << ": Installing with parameters:"
171            << " local=" << local.ToString()
172            << " broadcast=" << broadcast.ToString()
173            << " peer=" << peer.ToString()
174            << " gateway=" << gateway.ToString();
175  rtnl_handler_->AddInterfaceAddress(interface_index_, local, broadcast, peer);
176
177  if (gateway.IsValid()) {
178    routing_table_->SetDefaultRoute(interface_index_, gateway,
179                                    GetMetric(is_default_));
180  }
181
182  // Install any explicitly configured routes at the default metric.
183  routing_table_->ConfigureRoutes(interface_index_, config, kDefaultMetric);
184
185  if (properties.blackhole_ipv6) {
186    routing_table_->CreateBlackholeRoute(interface_index_,
187                                         IPAddress::kFamilyIPv6,
188                                         kDefaultMetric);
189  }
190
191  // Save a copy of the last non-null DNS config.
192  if (!config->properties().dns_servers.empty()) {
193    dns_servers_ = config->properties().dns_servers;
194  }
195
196  if (!config->properties().domain_search.empty()) {
197    dns_domain_search_ = config->properties().domain_search;
198  }
199
200  if (!config->properties().domain_name.empty()) {
201    dns_domain_name_ = config->properties().domain_name;
202  }
203
204  ipconfig_rpc_identifier_ = config->GetRpcIdentifier();
205
206  if (is_default_) {
207    PushDNSConfig();
208  }
209
210  local_ = local;
211  gateway_ = gateway;
212  has_broadcast_domain_ = !peer.IsValid();
213}
214
215void Connection::SetIsDefault(bool is_default) {
216  SLOG(Connection, 2) << __func__ << " " << interface_name_
217                      << " (index " << interface_index_ << ") "
218                      << is_default_ << " -> " << is_default;
219  if (is_default == is_default_) {
220    return;
221  }
222
223  routing_table_->SetDefaultMetric(interface_index_, GetMetric(is_default));
224
225  is_default_ = is_default;
226
227  if (is_default) {
228    PushDNSConfig();
229    DeviceRefPtr device = device_info_->GetDevice(interface_index_);
230    if (device) {
231      device->RequestPortalDetection();
232    }
233  }
234  routing_table_->FlushCache();
235}
236
237void Connection::PushDNSConfig() {
238  vector<string> domain_search = dns_domain_search_;
239  if (domain_search.empty() && !dns_domain_name_.empty()) {
240    SLOG(Connection, 2) << "Setting domain search to domain name "
241                        << dns_domain_name_;
242    domain_search.push_back(dns_domain_name_ + ".");
243  }
244  resolver_->SetDNSFromLists(dns_servers_, domain_search,
245                             dns_timeout_parameters_);
246}
247
248void Connection::RequestRouting() {
249  if (routing_request_count_++ == 0) {
250    DeviceRefPtr device = device_info_->GetDevice(interface_index_);
251    DCHECK(device.get());
252    if (!device.get()) {
253      LOG(ERROR) << "Device is NULL!";
254      return;
255    }
256    device->DisableReversePathFilter();
257  }
258}
259
260void Connection::ReleaseRouting() {
261  DCHECK(routing_request_count_ > 0);
262  if (--routing_request_count_ == 0) {
263    DeviceRefPtr device = device_info_->GetDevice(interface_index_);
264    DCHECK(device.get());
265    if (!device.get()) {
266      LOG(ERROR) << "Device is NULL!";
267      return;
268    }
269    device->EnableReversePathFilter();
270
271    // Clear any cached routes that might have accumulated while reverse-path
272    // filtering was disabled.
273    routing_table_->FlushCache();
274  }
275}
276
277bool Connection::RequestHostRoute(const IPAddress &address) {
278  // Set the prefix to be the entire address size.
279  IPAddress address_prefix(address);
280  address_prefix.set_prefix(address_prefix.GetLength() * 8);
281
282  // Do not set interface_index_ since this may not be the default route through
283  // which this destination can be found.  However, we should tag the created
284  // route with our interface index so we can clean this route up when this
285  // connection closes.  Also, add route query callback to determine the lower
286  // connection and bind to it.
287  if (!routing_table_->RequestRouteToHost(
288          address_prefix,
289          -1,
290          interface_index_,
291          Bind(&Connection::OnRouteQueryResponse,
292               weak_ptr_factory_.GetWeakPtr()))) {
293    LOG(ERROR) << "Could not request route to " << address.ToString();
294    return false;
295  }
296
297  return true;
298}
299
300// static
301bool Connection::FixGatewayReachability(IPAddress *local,
302                                        IPAddress *peer,
303                                        IPAddress *gateway,
304                                        const IPAddress &trusted_ip) {
305  if (!gateway->IsValid()) {
306    LOG(WARNING) << "No gateway address was provided for this connection.";
307    return false;
308  }
309
310  if (peer->IsValid()) {
311    if (!gateway->Equals(*peer)) {
312      LOG(WARNING) << "Gateway address "
313                   << gateway->ToString()
314                   << " does not match peer address "
315                   << peer->ToString();
316      return false;
317    }
318    if (gateway->Equals(trusted_ip)) {
319      // In order to send outgoing traffic in a point-to-point network,
320      // the gateway IP address isn't of significance.  As opposed to
321      // broadcast networks, we never ARP for the gateway IP address,
322      // but just send the IP packet addressed to the recipient.  As
323      // such, since using the external trusted IP address as the
324      // gateway or peer wreaks havoc on the routing rules, we choose
325      // not to supply a gateway address.  Here's an example:
326      //
327      //     Client    <->  Internet  <->  VPN Gateway  <->  Internal Network
328      //   192.168.1.2                      10.0.1.25         172.16.5.0/24
329      //
330      // In this example, a client connects to a VPN gateway on its
331      // public IP address 10.0.1.25.  It gets issued an IP address
332      // from the VPN internal pool.  For some VPN gateways, this
333      // results in a pushed-down PPP configuration which specifies:
334      //
335      //    Client local address:   172.16.5.13
336      //    Client peer address:    10.0.1.25
337      //    Client default gateway: 10.0.1.25
338      //
339      // If we take this literally, we need to resolve the fact that
340      // 10.0.1.25 is now listed as the default gateway and interface
341      // peer address for the point-to-point interface.  However, in
342      // order to route tunneled packets to the VPN gateway we must
343      // use the external route through the physical interface and
344      // not the tunnel, or else we end up in an infinite loop
345      // re-entering the tunnel trying to route towards the VPN server.
346      //
347      // We can do this by pinning a route, but we would need to wait
348      // for the pinning process to complete before assigning this
349      // address.  Currently this process is asynchronous and will
350      // complete only after returning to the event loop.  Additionally,
351      // since there's no metric associated with assigning an address
352      // to an interface, it's always possible that having the peer
353      // address of the interface might still trump a host route.
354      //
355      // To solve this problem, we reset the peer and gateway
356      // addresses.  Neither is required in order to perform the
357      // underlying routing task.  A gateway route can be specified
358      // without an IP endpoint on point-to-point links, and simply
359      // specify the outbound interface index.  Similarly, a peer
360      // IP address is not necessary either, and will be assigned
361      // the same IP address as the local IP.  This approach
362      // simplifies routing and doesn't change the desired
363      // functional behavior.
364      //
365      LOG(INFO) << "Removing gateway and peer addresses to preserve "
366                << "routability to trusted IP address.";
367      peer->SetAddressToDefault();
368      gateway->SetAddressToDefault();
369    }
370    return true;
371  }
372
373  if (local->CanReachAddress(*gateway)) {
374    return true;
375  }
376
377  LOG(WARNING) << "Gateway "
378               << gateway->ToString()
379               << " is unreachable from local address/prefix "
380               << local->ToString() << "/" << local->prefix();
381
382  bool found_new_prefix = false;
383  size_t original_prefix = local->prefix();
384  // Only try to expand the netmask if the configured prefix is
385  // less than "all ones".  This special-cases the "all-ones"
386  // prefix as a forced conversion to point-to-point networking.
387  if (local->prefix() < IPAddress::GetMaxPrefixLength(local->family())) {
388    size_t prefix = original_prefix - 1;
389    for (; prefix >= local->GetMinPrefixLength(); --prefix) {
390      local->set_prefix(prefix);
391      if (local->CanReachAddress(*gateway)) {
392        found_new_prefix = true;
393        break;
394      }
395    }
396  }
397
398  if (!found_new_prefix) {
399    // Restore the original prefix since we cannot find a better one.
400    local->set_prefix(original_prefix);
401    DCHECK(!peer->IsValid());
402    LOG(WARNING) << "Assuming point-to-point configuration.";
403    *peer = *gateway;
404    return true;
405  }
406
407  LOG(WARNING) << "Mitigating this by setting local prefix to "
408               << local->prefix();
409  return true;
410}
411
412uint32 Connection::GetMetric(bool is_default) {
413  // If this is not the default route, assign a metric based on the interface
414  // index.  This way all non-default routes (even to the same gateway IP) end
415  // up with unique metrics so they do not collide.
416  return is_default ? kDefaultMetric : kNonDefaultMetricBase + interface_index_;
417}
418
419bool Connection::PinHostRoute(const IPAddress &trusted_ip,
420                              const IPAddress &gateway) {
421  SLOG(Connection, 2) << __func__;
422  if (!trusted_ip.IsValid() || !gateway.IsValid()) {
423    LOG_IF(ERROR, !gateway.IsValid())
424        << "No gateway -- unable to pin host route.";
425    LOG_IF(ERROR, !trusted_ip.IsValid())
426        << "No trusted IP -- unable to pin host route.";
427    return false;
428  }
429
430  return RequestHostRoute(trusted_ip);
431}
432
433void Connection::OnRouteQueryResponse(int interface_index,
434                                      const RoutingTableEntry &entry) {
435  SLOG(Connection, 2) << __func__ << "(" << interface_index << ", "
436                      << entry.tag << ")" << " @ " << interface_name_;
437  lower_binder_.Attach(NULL);
438  DeviceRefPtr device = device_info_->GetDevice(interface_index);
439  if (!device) {
440    LOG(ERROR) << "Unable to lookup device for index " << interface_index;
441    return;
442  }
443  ConnectionRefPtr connection = device->connection();
444  if (!connection) {
445    LOG(ERROR) << "Device " << interface_index << " has no connection.";
446    return;
447  }
448  lower_binder_.Attach(connection);
449  connection->CreateGatewayRoute();
450}
451
452bool Connection::CreateGatewayRoute() {
453  // Ensure that the gateway for the lower connection remains reachable,
454  // since we may create routes that conflict with it.
455  if (!has_broadcast_domain_) {
456    return false;
457  }
458  // It is not worth keeping track of this route, since it is benign,
459  // and only pins persistent state that was already true of the connection.
460  // If DHCP parameters change later (without the connection having been
461  // destroyed and recreated), the binding processes will likely terminate
462  // and restart, causing a new link route to be created.
463  return routing_table_->CreateLinkRoute(interface_index_, local_, gateway_);
464}
465
466void Connection::OnLowerDisconnect() {
467  SLOG(Connection, 2) << __func__ << " @ " << interface_name_;
468  // Ensures that |this| instance doesn't get destroyed in the middle of
469  // notifying the binders. This method needs to be separate from
470  // NotifyBindersOnDisconnect because the latter may be invoked by Connection's
471  // destructor when |this| instance's reference count is already 0.
472  ConnectionRefPtr connection(this);
473  connection->NotifyBindersOnDisconnect();
474}
475
476void Connection::NotifyBindersOnDisconnect() {
477  // Note that this method may be invoked by the destructor.
478  SLOG(Connection, 2) << __func__ << " @ " << interface_name_;
479
480  // Unbinds the lower connection before notifying the binders. This ensures
481  // correct behavior in case of circular binding.
482  lower_binder_.Attach(NULL);
483  while (!binders_.empty()) {
484    // Pop the binder first and then notify it to ensure that each binder is
485    // notified only once.
486    Binder *binder = binders_.front();
487    binders_.pop_front();
488    binder->OnDisconnect();
489  }
490}
491
492void Connection::AttachBinder(Binder *binder) {
493  SLOG(Connection, 2) << __func__ << "(" << binder->name() << ")" << " @ "
494                      << interface_name_;
495  binders_.push_back(binder);
496}
497
498void Connection::DetachBinder(Binder *binder) {
499  SLOG(Connection, 2) << __func__ << "(" << binder->name() << ")" << " @ "
500                      << interface_name_;
501  for (deque<Binder *>::iterator it = binders_.begin();
502       it != binders_.end(); ++it) {
503    if (binder == *it) {
504      binders_.erase(it);
505      return;
506    }
507  }
508}
509
510}  // namespace shill
511