connection.cc revision fad4a0b7e55dd82d3815ee96862b6e546727eb6e
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 std::string;
17
18namespace shill {
19
20// static
21const uint32 Connection::kDefaultMetric = 1;
22// static
23const uint32 Connection::kNonDefaultMetricBase = 10;
24
25Connection::Connection(int interface_index,
26                       const std::string& interface_name,
27                       Technology::Identifier technology,
28                       const DeviceInfo *device_info)
29    : is_default_(false),
30      routing_request_count_(0),
31      interface_index_(interface_index),
32      interface_name_(interface_name),
33      technology_(technology),
34      device_info_(device_info),
35      resolver_(Resolver::GetInstance()),
36      routing_table_(RoutingTable::GetInstance()),
37      rtnl_handler_(RTNLHandler::GetInstance()) {
38  SLOG(Connection, 2) << __func__ << "(" << interface_index << ", "
39                      << interface_name << ", "
40                      << Technology::NameFromIdentifier(technology) << ")";
41}
42
43Connection::~Connection() {
44  SLOG(Connection, 2) << __func__ << " " << interface_name_;
45
46  DCHECK(!routing_request_count_);
47  routing_table_->FlushRoutes(interface_index_);
48  device_info_->FlushAddresses(interface_index_);
49}
50
51void Connection::UpdateFromIPConfig(const IPConfigRefPtr &config) {
52  SLOG(Connection, 2) << __func__ << " " << interface_name_;
53
54  const IPConfig::Properties &properties = config->properties();
55  IPAddress local(properties.address_family);
56  if (!local.SetAddressFromString(properties.address)) {
57    LOG(ERROR) << "Local address " << properties.address << " is invalid";
58    return;
59  }
60  local.set_prefix(properties.subnet_prefix);
61
62  IPAddress broadcast(properties.address_family);
63  if (!broadcast.SetAddressFromString(properties.broadcast_address) &&
64      technology_ != Technology::kVPN) {
65    LOG(ERROR) << "Broadcast address " << properties.broadcast_address
66               << " is invalid";
67    return;
68  }
69
70  IPAddress peer(properties.address_family);
71  if (!properties.peer_address.empty() &&
72      !peer.SetAddressFromString(properties.peer_address)) {
73    LOG(ERROR) << "Peer address " << properties.peer_address
74               << " is invalid";
75    return;
76  }
77
78  IPAddress gateway_address(properties.address_family);
79  if (!properties.gateway.empty() &&
80      !gateway_address.SetAddressFromString(properties.gateway)) {
81    LOG(ERROR) << "Gateway address " << properties.peer_address
82               << " is invalid";
83    return;
84  }
85
86  FixGatewayReachability(&local, gateway_address);
87
88  rtnl_handler_->AddInterfaceAddress(interface_index_, local, broadcast, peer);
89
90  if (gateway_address.IsValid()) {
91    routing_table_->SetDefaultRoute(interface_index_, gateway_address,
92                                    GetMetric(is_default_));
93  } else if (!peer.IsValid()) {
94    LOG(WARNING) << "No gateway or peer address was provided for this "
95                 << "connection.  Expect limited network connectivity.";
96  }
97
98  // Install any explicitly configured routes at the default metric.
99  routing_table_->ConfigureRoutes(interface_index_, config, kDefaultMetric);
100
101  // Save a copy of the last non-null DNS config
102  if (!config->properties().dns_servers.empty()) {
103    dns_servers_ = config->properties().dns_servers;
104    dns_domain_search_ = config->properties().domain_search;
105  }
106
107  if (is_default_) {
108    resolver_->SetDNSFromIPConfig(config);
109  }
110}
111
112void Connection::SetIsDefault(bool is_default) {
113  SLOG(Connection, 2) << __func__ << " " << interface_name_
114                      << " (index " << interface_index_ << ") "
115                      << is_default_ << " -> " << is_default;
116  if (is_default == is_default_) {
117    return;
118  }
119
120  routing_table_->SetDefaultMetric(interface_index_, GetMetric(is_default));
121
122  is_default_ = is_default;
123
124  if (is_default) {
125    resolver_->SetDNSFromLists(dns_servers_, dns_domain_search_);
126    DeviceRefPtr device = device_info_->GetDevice(interface_index_);
127    if (device) {
128      device->RequestPortalDetection();
129    }
130  }
131}
132
133void Connection::RequestRouting() {
134  if (routing_request_count_++ == 0) {
135    DeviceRefPtr device = device_info_->GetDevice(interface_index_);
136    DCHECK(device.get());
137    if (!device.get()) {
138      LOG(ERROR) << "Device is NULL!";
139      return;
140    }
141    device->DisableReversePathFilter();
142  }
143}
144
145void Connection::ReleaseRouting() {
146  DCHECK(routing_request_count_ > 0);
147  if (--routing_request_count_ == 0) {
148    DeviceRefPtr device = device_info_->GetDevice(interface_index_);
149    DCHECK(device.get());
150    if (!device.get()) {
151      LOG(ERROR) << "Device is NULL!";
152      return;
153    }
154    device->EnableReversePathFilter();
155
156    // Clear any cached routes that might have accumulated while reverse-path
157    // filtering was disabled.
158    routing_table_->FlushCache();
159  }
160}
161
162bool Connection::RequestHostRoute(const IPAddress &address) {
163  // Set the prefix to be the entire address size.
164  IPAddress address_prefix(address);
165  address_prefix.set_prefix(address_prefix.GetLength() * 8);
166
167  // Do not set interface_index_ since this may not be the
168  // default route through which this destination can be found.
169  if (!routing_table_->RequestRouteToHost(address_prefix, -1)) {
170    LOG(ERROR) << "Could not request route to " << address.ToString();
171    return false;
172  }
173
174  return true;
175}
176
177// static
178void Connection::FixGatewayReachability(IPAddress *local,
179                                        const IPAddress &gateway) {
180  if (!gateway.IsValid() || local->CanReachAddress(gateway)) {
181    return;
182  }
183
184  LOG(WARNING) << "Gateway "
185               << gateway.ToString()
186               << " is unreachable from local address/prefix "
187               << local->ToString() << "/" << local->prefix();
188
189  size_t original_prefix = local->prefix();
190  size_t prefix = original_prefix - 1;
191  for (; prefix >= local->GetMinPrefixLength(); --prefix) {
192    local->set_prefix(prefix);
193    if (local->CanReachAddress(gateway)) {
194      break;
195    }
196  }
197
198  if (prefix < local->GetMinPrefixLength()) {
199    // Restore the original prefix since we cannot find a better one.
200    local->set_prefix(original_prefix);
201    LOG(WARNING) << "Expect limited network connectivity.";
202  } else {
203    LOG(WARNING) << "Mitigating this by setting local prefix to " << prefix;
204  }
205}
206
207uint32 Connection::GetMetric(bool is_default) {
208  // If this is not the default route, assign a metric based on the interface
209  // index.  This way all non-default routes (even to the same gateway IP) end
210  // up with unique metrics so they do not collide.
211  return is_default ? kDefaultMetric : kNonDefaultMetricBase + interface_index_;
212}
213
214}  // namespace shill
215