routing_table.cc revision 536820ddd33f63e1d95e6e04e1757bd0248bbe38
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/routing_table.h"
6
7#include <arpa/inet.h>
8#include <fcntl.h>
9#include <linux/netlink.h>
10#include <linux/rtnetlink.h>
11#include <netinet/ether.h>
12#include <net/if.h>
13#include <net/if_arp.h>
14#include <string.h>
15#include <sys/socket.h>
16#include <time.h>
17#include <unistd.h>
18
19#include <string>
20
21#include <base/bind.h>
22#include <base/file_path.h>
23#include <base/file_util.h>
24#include <base/hash_tables.h>
25#include <base/logging.h>
26#include <base/memory/scoped_ptr.h>
27#include <base/stl_util.h>
28#include <base/stringprintf.h>
29
30#include "shill/byte_string.h"
31#include "shill/routing_table_entry.h"
32#include "shill/rtnl_handler.h"
33#include "shill/rtnl_listener.h"
34#include "shill/rtnl_message.h"
35
36using base::Bind;
37using base::Unretained;
38using std::string;
39using std::vector;
40
41namespace shill {
42
43// TODO(ers): not using LAZY_INSTANCE_INITIALIZER
44// because of http://crbug.com/114828
45static base::LazyInstance<RoutingTable> g_routing_table = {0, {{0}}};
46
47// static
48const char RoutingTable::kRouteFlushPath4[] = "/proc/sys/net/ipv4/route/flush";
49// static
50const char RoutingTable::kRouteFlushPath6[] = "/proc/sys/net/ipv6/route/flush";
51
52RoutingTable::RoutingTable()
53    : route_callback_(Bind(&RoutingTable::RouteMsgHandler, Unretained(this))),
54      route_listener_(NULL),
55      rtnl_handler_(RTNLHandler::GetInstance()) {
56  VLOG(2) << __func__;
57}
58
59RoutingTable::~RoutingTable() {}
60
61RoutingTable* RoutingTable::GetInstance() {
62  return g_routing_table.Pointer();
63}
64
65void RoutingTable::Start() {
66  VLOG(2) << __func__;
67
68  route_listener_.reset(
69      new RTNLListener(RTNLHandler::kRequestRoute, route_callback_));
70  rtnl_handler_->RequestDump(RTNLHandler::kRequestRoute);
71}
72
73void RoutingTable::Stop() {
74  VLOG(2) << __func__;
75
76  route_listener_.reset();
77}
78
79bool RoutingTable::AddRoute(int interface_index,
80                            const RoutingTableEntry &entry) {
81  VLOG(2) << __func__ << ": "
82          << "destination " << entry.dst.ToString()
83          << " index " << interface_index
84          << " gateway " << entry.gateway.ToString()
85          << " metric " << entry.metric;
86
87  CHECK(!entry.from_rtnl);
88  if (!ApplyRoute(interface_index,
89                  entry,
90                  RTNLMessage::kModeAdd,
91                  NLM_F_CREATE | NLM_F_EXCL)) {
92    return false;
93  }
94  tables_[interface_index].push_back(entry);
95  return true;
96}
97
98bool RoutingTable::GetDefaultRoute(int interface_index,
99                                   IPAddress::Family family,
100                                   RoutingTableEntry *entry) {
101  RoutingTableEntry *found_entry;
102  bool ret = GetDefaultRouteInternal(interface_index, family, &found_entry);
103  if (ret) {
104    *entry = *found_entry;
105  }
106  return ret;
107}
108
109bool RoutingTable::GetDefaultRouteInternal(int interface_index,
110                                           IPAddress::Family family,
111                                           RoutingTableEntry **entry) {
112  VLOG(2) << __func__ << " index " << interface_index
113          << " family " << IPAddress::GetAddressFamilyName(family);
114
115  base::hash_map<int, vector<RoutingTableEntry> >::iterator table =
116    tables_.find(interface_index);
117
118  if (table == tables_.end()) {
119    VLOG(2) << __func__ << " no table";
120    return false;
121  }
122
123  vector<RoutingTableEntry>::iterator nent;
124
125  for (nent = table->second.begin(); nent != table->second.end(); ++nent) {
126    if (nent->dst.IsDefault() && nent->dst.family() == family) {
127      *entry = &(*nent);
128      VLOG(2) << __func__ << ": found"
129              << " gateway " << nent->gateway.ToString()
130              << " metric " << nent->metric;
131      return true;
132    }
133  }
134
135  VLOG(2) << __func__ << " no route";
136  return false;
137}
138
139bool RoutingTable::SetDefaultRoute(int interface_index,
140                                   const IPConfigRefPtr &ipconfig,
141                                   uint32 metric) {
142  VLOG(2) << __func__ << " index " << interface_index << " metric " << metric;
143
144  const IPConfig::Properties &ipconfig_props = ipconfig->properties();
145  RoutingTableEntry *old_entry;
146  IPAddress gateway_address(ipconfig_props.address_family);
147  if (!gateway_address.SetAddressFromString(ipconfig_props.gateway)) {
148    return false;
149  }
150
151  if (GetDefaultRouteInternal(interface_index,
152                              ipconfig_props.address_family,
153                              &old_entry)) {
154    if (old_entry->gateway.Equals(gateway_address)) {
155      if (old_entry->metric != metric) {
156        ReplaceMetric(interface_index, old_entry, metric);
157      }
158      return true;
159    } else {
160      // TODO(quiche): Update internal state as well?
161      ApplyRoute(interface_index,
162                 *old_entry,
163                 RTNLMessage::kModeDelete,
164                 0);
165    }
166  }
167
168  IPAddress default_address(ipconfig_props.address_family);
169  default_address.SetAddressToDefault();
170
171  return AddRoute(interface_index,
172                  RoutingTableEntry(default_address,
173                                    default_address,
174                                    gateway_address,
175                                    metric,
176                                    RT_SCOPE_UNIVERSE,
177                                    false));
178}
179
180bool RoutingTable::ConfigureRoutes(int interface_index,
181                                   const IPConfigRefPtr &ipconfig,
182                                   uint32 metric) {
183  bool ret = true;
184
185  IPAddress::Family address_family = ipconfig->properties().address_family;
186  const vector<IPConfig::Route> &routes = ipconfig->properties().routes;
187
188  for (vector<IPConfig::Route>::const_iterator it = routes.begin();
189       it != routes.end();
190       ++it) {
191    VLOG(3) << "Installing route:"
192            << " Destination: " << it->host
193            << " Netmask: " << it->netmask
194            << " Gateway: " << it->gateway;
195    IPAddress destination_address(address_family);
196    IPAddress source_address(address_family);  // Left as default.
197    IPAddress gateway_address(address_family);
198    if (!destination_address.SetAddressFromString(it->host)) {
199      LOG(ERROR) << "Failed to parse host "
200                 << it->host;
201      ret = false;
202      continue;
203    }
204    if (!gateway_address.SetAddressFromString(it->gateway)) {
205      LOG(ERROR) << "Failed to parse gateway "
206                 << it->gateway;
207      ret = false;
208      continue;
209    }
210    destination_address.set_prefix(
211        IPAddress::GetPrefixLengthFromMask(address_family, it->netmask));
212    if (!AddRoute(interface_index,
213                  RoutingTableEntry(destination_address,
214                                    source_address,
215                                    gateway_address,
216                                    metric,
217                                    RT_SCOPE_UNIVERSE,
218                                    false))) {
219      ret = false;
220    }
221  }
222  return ret;
223}
224
225void RoutingTable::FlushRoutes(int interface_index) {
226  VLOG(2) << __func__;
227
228  base::hash_map<int, vector<RoutingTableEntry> >::iterator table =
229    tables_.find(interface_index);
230
231  if (table == tables_.end()) {
232    return;
233  }
234
235  vector<RoutingTableEntry>::iterator nent;
236
237  for (nent = table->second.begin(); nent != table->second.end(); ++nent) {
238      ApplyRoute(interface_index, *nent, RTNLMessage::kModeDelete, 0);
239  }
240  table->second.clear();
241}
242
243void RoutingTable::ResetTable(int interface_index) {
244  tables_.erase(interface_index);
245}
246
247void RoutingTable::SetDefaultMetric(int interface_index, uint32 metric) {
248  VLOG(2) << __func__ << " index " << interface_index << " metric " << metric;
249
250  RoutingTableEntry *entry;
251  if (GetDefaultRouteInternal(
252          interface_index, IPAddress::kFamilyIPv4, &entry) &&
253      entry->metric != metric) {
254    ReplaceMetric(interface_index, entry, metric);
255  }
256
257  if (GetDefaultRouteInternal(
258          interface_index, IPAddress::kFamilyIPv6, &entry) &&
259      entry->metric != metric) {
260    ReplaceMetric(interface_index, entry, metric);
261  }
262}
263
264// static
265bool RoutingTable::ParseRoutingTableMessage(const RTNLMessage &message,
266                                            int *interface_index,
267                                            RoutingTableEntry *entry) {
268  if (message.type() != RTNLMessage::kTypeRoute ||
269      message.family() == IPAddress::kFamilyUnknown ||
270      !message.HasAttribute(RTA_OIF)) {
271    return false;
272  }
273
274  const RTNLMessage::RouteStatus &route_status = message.route_status();
275
276  if (route_status.type != RTN_UNICAST ||
277      route_status.table != RT_TABLE_MAIN) {
278    return false;
279  }
280
281  uint32 interface_index_u32 = 0;
282  if (!message.GetAttribute(RTA_OIF).ConvertToCPUUInt32(&interface_index_u32)) {
283    return false;
284  }
285  *interface_index = interface_index_u32;
286
287  uint32 metric = 0;
288  if (message.HasAttribute(RTA_PRIORITY)) {
289    message.GetAttribute(RTA_PRIORITY).ConvertToCPUUInt32(&metric);
290  }
291
292  IPAddress default_addr(message.family());
293  default_addr.SetAddressToDefault();
294
295  ByteString dst_bytes(default_addr.address());
296  if (message.HasAttribute(RTA_DST)) {
297    dst_bytes = message.GetAttribute(RTA_DST);
298  }
299  ByteString src_bytes(default_addr.address());
300  if (message.HasAttribute(RTA_SRC)) {
301    src_bytes = message.GetAttribute(RTA_SRC);
302  }
303  ByteString gateway_bytes(default_addr.address());
304  if (message.HasAttribute(RTA_GATEWAY)) {
305    gateway_bytes = message.GetAttribute(RTA_GATEWAY);
306  }
307
308  entry->dst = IPAddress(message.family(), dst_bytes, route_status.dst_prefix);
309  entry->src = IPAddress(message.family(), src_bytes, route_status.src_prefix);
310  entry->gateway = IPAddress(message.family(), gateway_bytes);
311  entry->metric = metric;
312  entry->scope = route_status.scope;
313  entry->from_rtnl = true;
314
315  return true;
316}
317
318void RoutingTable::RouteMsgHandler(const RTNLMessage &message) {
319  int interface_index;
320  RoutingTableEntry entry;
321
322  if (!ParseRoutingTableMessage(message, &interface_index, &entry)) {
323    return;
324  }
325
326  if (!route_query_sequences_.empty() &&
327      message.route_status().protocol == RTPROT_UNSPEC) {
328    VLOG(3) << __func__ << ": Message seq: " << message.seq()
329            << " mode " << message.mode()
330            << ", next query seq: " << route_query_sequences_.front();
331
332    // Purge queries that have expired (sequence number of this message is
333    // greater than that of the head of the route query sequence).  Do the
334    // math in a way that's roll-over independent.
335    while (route_query_sequences_.front() - message.seq() > kuint32max / 2) {
336      LOG(ERROR) << __func__ << ": Purging un-replied route request sequence "
337                 << route_query_sequences_.front()
338                 << " (< " << message.seq() << ")";
339      route_query_sequences_.pop();
340      if (route_query_sequences_.empty())
341        return;
342    }
343
344    if (route_query_sequences_.front() == message.seq()) {
345      VLOG(2) << __func__ << ": Adding host route to " << entry.dst.ToString();
346      route_query_sequences_.pop();
347      RoutingTableEntry add_entry(entry);
348      add_entry.from_rtnl = false;
349      AddRoute(interface_index, add_entry);
350    }
351    return;
352  } else if (message.route_status().protocol != RTPROT_BOOT) {
353    // Responses to route queries come back with a protocol of
354    // RTPROT_UNSPEC.  Otherwise, normal route updates that we are
355    // interested in come with a protocol of RTPROT_BOOT.
356    return;
357  }
358
359  vector<RoutingTableEntry> &table = tables_[interface_index];
360  vector<RoutingTableEntry>::iterator nent;
361  for (nent = table.begin(); nent != table.end(); ++nent) {
362    if (nent->dst.Equals(entry.dst) &&
363        nent->src.Equals(entry.src) &&
364        nent->gateway.Equals(entry.gateway) &&
365        nent->scope == entry.scope) {
366      if (message.mode() == RTNLMessage::kModeDelete &&
367          nent->metric == entry.metric) {
368        table.erase(nent);
369      } else if (message.mode() == RTNLMessage::kModeAdd) {
370        nent->from_rtnl = true;
371        nent->metric = entry.metric;
372      }
373      return;
374    }
375  }
376
377  if (message.mode() == RTNLMessage::kModeAdd) {
378    VLOG(2) << __func__ << " adding"
379            << " destination " << entry.dst.ToString()
380            << " index " << interface_index
381            << " gateway " << entry.gateway.ToString()
382            << " metric " << entry.metric;
383    table.push_back(entry);
384  }
385}
386
387bool RoutingTable::ApplyRoute(uint32 interface_index,
388                              const RoutingTableEntry &entry,
389                              RTNLMessage::Mode mode,
390                              unsigned int flags) {
391  VLOG(2) << base::StringPrintf("%s: dst %s/%d src %s/%d index %d mode %d "
392                                "flags 0x%x",
393                                __func__, entry.dst.ToString().c_str(),
394                                entry.dst.prefix(),
395                                entry.src.ToString().c_str(),
396                                entry.src.prefix(), interface_index, mode,
397                                flags);
398
399  RTNLMessage message(
400      RTNLMessage::kTypeRoute,
401      mode,
402      NLM_F_REQUEST | flags,
403      0,
404      0,
405      0,
406      entry.dst.family());
407
408  message.set_route_status(RTNLMessage::RouteStatus(
409      entry.dst.prefix(),
410      entry.src.prefix(),
411      RT_TABLE_MAIN,
412      RTPROT_BOOT,
413      entry.scope,
414      RTN_UNICAST,
415      0));
416
417  message.SetAttribute(RTA_DST, entry.dst.address());
418  if (!entry.src.IsDefault()) {
419    message.SetAttribute(RTA_SRC, entry.src.address());
420  }
421  if (!entry.gateway.IsDefault()) {
422    message.SetAttribute(RTA_GATEWAY, entry.gateway.address());
423  }
424  message.SetAttribute(RTA_PRIORITY,
425                       ByteString::CreateFromCPUUInt32(entry.metric));
426  message.SetAttribute(RTA_OIF,
427                       ByteString::CreateFromCPUUInt32(interface_index));
428
429  return rtnl_handler_->SendMessage(&message);
430}
431
432// Somewhat surprisingly, the kernel allows you to create multiple routes
433// to the same destination through the same interface with different metrics.
434// Therefore, to change the metric on a route, we can't just use the
435// NLM_F_REPLACE flag by itself.  We have to explicitly remove the old route.
436// We do so after creating the route at a new metric so there is no traffic
437// disruption to existing network streams.
438void RoutingTable::ReplaceMetric(uint32 interface_index,
439                                 RoutingTableEntry *entry,
440                                 uint32 metric) {
441  VLOG(2) << __func__ << " index " << interface_index << " metric " << metric;
442  RoutingTableEntry new_entry = *entry;
443  new_entry.metric = metric;
444  // First create the route at the new metric.
445  ApplyRoute(interface_index, new_entry, RTNLMessage::kModeAdd,
446             NLM_F_CREATE | NLM_F_REPLACE);
447  // Then delete the route at the old metric.
448  ApplyRoute(interface_index, *entry, RTNLMessage::kModeDelete, 0);
449  // Now, update our routing table (via |*entry|) from |new_entry|.
450  *entry = new_entry;
451}
452
453bool RoutingTable::FlushCache() {
454  static const char *kPaths[2] = { kRouteFlushPath4, kRouteFlushPath6 };
455  bool ret = true;
456
457  VLOG(2) << __func__;
458
459  for (size_t i = 0; i < arraysize(kPaths); ++i) {
460    if (file_util::WriteFile(FilePath(kPaths[i]), "-1", 2) != 2) {
461      LOG(ERROR) << base::StringPrintf("Cannot write to route flush file %s",
462                                       kPaths[i]);
463      ret = false;
464    }
465  }
466
467  return ret;
468}
469
470bool RoutingTable::RequestRouteToHost(const IPAddress &address,
471                                      int interface_index) {
472  RTNLMessage message(
473      RTNLMessage::kTypeRoute,
474      RTNLMessage::kModeQuery,
475      NLM_F_REQUEST,
476      0,
477      0,
478      interface_index,
479      address.family());
480
481  RTNLMessage::RouteStatus status;
482  status.dst_prefix = address.prefix();
483  message.set_route_status(status);
484  message.SetAttribute(RTA_DST, address.address());
485
486  if (interface_index != -1) {
487    message.SetAttribute(RTA_OIF,
488                         ByteString::CreateFromCPUUInt32(interface_index));
489  }
490
491  if (!rtnl_handler_->SendMessage(&message)) {
492    return false;
493  }
494
495  // Save the sequence number of the request so we can create a route for
496  // this host when we get a reply.
497  route_query_sequences_.push(message.seq());
498
499  return true;
500}
501
502}  // namespace shill
503