blob: 1c7ce76fc615f42e28c982a5711ab675bc5b5eea [file] [log] [blame]
mukesh agrawal2c15d2c2012-02-21 16:09:21 -08001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Paul Stewart75e89d22011-08-01 10:00:02 -07002// 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/callback_old.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-inl.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 std::string;
37using std::vector;
38
39namespace shill {
40
Paul Stewart0d2ada32011-08-09 17:01:57 -070041static base::LazyInstance<RoutingTable> g_routing_table(
42 base::LINKER_INITIALIZED);
43
Paul Stewart75e89d22011-08-01 10:00:02 -070044// static
45const char RoutingTable::kRouteFlushPath4[] = "/proc/sys/net/ipv4/route/flush";
Paul Stewart0d2ada32011-08-09 17:01:57 -070046// static
Paul Stewart75e89d22011-08-01 10:00:02 -070047const char RoutingTable::kRouteFlushPath6[] = "/proc/sys/net/ipv6/route/flush";
48
49RoutingTable::RoutingTable()
50 : route_callback_(NewCallback(this, &RoutingTable::RouteMsgHandler)),
51 route_listener_(NULL) {
52 VLOG(2) << __func__;
53}
54
55RoutingTable::~RoutingTable() {}
56
57RoutingTable* RoutingTable::GetInstance() {
Paul Stewart0d2ada32011-08-09 17:01:57 -070058 return g_routing_table.Pointer();
Paul Stewart75e89d22011-08-01 10:00:02 -070059}
60
61void RoutingTable::Start() {
62 VLOG(2) << __func__;
63
64 route_listener_.reset(
65 new RTNLListener(RTNLHandler::kRequestRoute, route_callback_.get()));
66 RTNLHandler::GetInstance()->RequestDump(
Paul Stewart9a908082011-08-31 12:18:48 -070067 RTNLHandler::kRequestRoute);
Paul Stewart75e89d22011-08-01 10:00:02 -070068}
69
70void RoutingTable::Stop() {
71 VLOG(2) << __func__;
72
73 route_listener_.reset();
74}
75
76bool RoutingTable::AddRoute(int interface_index,
77 const RoutingTableEntry &entry) {
mukesh agrawal2c15d2c2012-02-21 16:09:21 -080078 VLOG(2) << __func__ << " "
79 << "index " << interface_index
80 << "gateway " << entry.gateway.ToString() << " "
81 << "metric " << entry.metric;
Paul Stewart75e89d22011-08-01 10:00:02 -070082
83 CHECK(!entry.from_rtnl);
84 if (!ApplyRoute(interface_index,
85 entry,
Paul Stewart9a908082011-08-31 12:18:48 -070086 RTNLMessage::kModeAdd,
Paul Stewart75e89d22011-08-01 10:00:02 -070087 NLM_F_CREATE | NLM_F_EXCL)) {
88 return false;
89 }
90 tables_[interface_index].push_back(entry);
91 return true;
92}
93
94bool RoutingTable::GetDefaultRoute(int interface_index,
95 IPAddress::Family family,
96 RoutingTableEntry *entry) {
mukesh agrawal2c15d2c2012-02-21 16:09:21 -080097 VLOG(2) << __func__ << " index " << interface_index << " family " << family;
Paul Stewart75e89d22011-08-01 10:00:02 -070098
99 base::hash_map<int, vector<RoutingTableEntry> >::iterator table =
100 tables_.find(interface_index);
101
102 if (table == tables_.end()) {
mukesh agrawal2c15d2c2012-02-21 16:09:21 -0800103 VLOG(2) << __func__ << " no table";
Paul Stewart75e89d22011-08-01 10:00:02 -0700104 return false;
105 }
106
107 vector<RoutingTableEntry>::iterator nent;
108
109 for (nent = table->second.begin(); nent != table->second.end(); ++nent) {
110 if (nent->dst.IsDefault() && nent->dst.family() == family) {
111 *entry = *nent;
mukesh agrawal2c15d2c2012-02-21 16:09:21 -0800112 VLOG(2) << __func__ << " found "
113 << "gateway " << nent->gateway.ToString() << " "
114 << "metric " << nent->metric;
Paul Stewart75e89d22011-08-01 10:00:02 -0700115 return true;
116 }
117 }
118
mukesh agrawal2c15d2c2012-02-21 16:09:21 -0800119 VLOG(2) << __func__ << " no route";
Paul Stewart75e89d22011-08-01 10:00:02 -0700120 return false;
121}
122
123bool RoutingTable::SetDefaultRoute(int interface_index,
124 const IPConfigRefPtr &ipconfig,
125 uint32 metric) {
mukesh agrawal2c15d2c2012-02-21 16:09:21 -0800126 VLOG(2) << __func__ << " index " << interface_index << " metric " << metric;
127
Paul Stewart75e89d22011-08-01 10:00:02 -0700128 const IPConfig::Properties &ipconfig_props = ipconfig->properties();
129 RoutingTableEntry old_entry;
Paul Stewart75e89d22011-08-01 10:00:02 -0700130 IPAddress gateway_address(ipconfig_props.address_family);
131 if (!gateway_address.SetAddressFromString(ipconfig_props.gateway)) {
132 return false;
133 }
134
135 if (GetDefaultRoute(interface_index,
136 ipconfig_props.address_family,
137 &old_entry)) {
138 if (old_entry.gateway.Equals(gateway_address)) {
139 if (old_entry.metric != metric) {
Paul Stewartc1dec4d2011-12-08 15:25:28 -0800140 ReplaceMetric(interface_index, old_entry, metric);
Paul Stewart75e89d22011-08-01 10:00:02 -0700141 }
142 return true;
143 } else {
144 ApplyRoute(interface_index,
145 old_entry,
Paul Stewart9a908082011-08-31 12:18:48 -0700146 RTNLMessage::kModeDelete,
Paul Stewart75e89d22011-08-01 10:00:02 -0700147 0);
148 }
149 }
150
151 IPAddress default_address(ipconfig_props.address_family);
152 default_address.SetAddressToDefault();
153
154 return AddRoute(interface_index,
155 RoutingTableEntry(default_address,
Paul Stewart75e89d22011-08-01 10:00:02 -0700156 default_address,
Paul Stewart75e89d22011-08-01 10:00:02 -0700157 gateway_address,
158 metric,
159 RT_SCOPE_UNIVERSE,
160 false));
161}
162
163void RoutingTable::FlushRoutes(int interface_index) {
164 VLOG(2) << __func__;
165
166 base::hash_map<int, vector<RoutingTableEntry> >::iterator table =
167 tables_.find(interface_index);
168
169 if (table == tables_.end()) {
170 return;
171 }
172
173 vector<RoutingTableEntry>::iterator nent;
174
175 for (nent = table->second.begin(); nent != table->second.end(); ++nent) {
Paul Stewart9a908082011-08-31 12:18:48 -0700176 ApplyRoute(interface_index, *nent, RTNLMessage::kModeDelete, 0);
Paul Stewart75e89d22011-08-01 10:00:02 -0700177 }
178}
179
180void RoutingTable::ResetTable(int interface_index) {
181 tables_.erase(interface_index);
182}
183
184void RoutingTable::SetDefaultMetric(int interface_index, uint32 metric) {
mukesh agrawal2c15d2c2012-02-21 16:09:21 -0800185 VLOG(2) << __func__ << " "
186 << "index " << interface_index << " metric " << metric;
Paul Stewart75e89d22011-08-01 10:00:02 -0700187
mukesh agrawal2c15d2c2012-02-21 16:09:21 -0800188 RoutingTableEntry entry;
Paul Stewart75e89d22011-08-01 10:00:02 -0700189
Paul Stewart7355ce12011-09-02 10:47:01 -0700190 if (GetDefaultRoute(interface_index, IPAddress::kFamilyIPv4, &entry) &&
Paul Stewart75e89d22011-08-01 10:00:02 -0700191 entry.metric != metric) {
Paul Stewartc1dec4d2011-12-08 15:25:28 -0800192 ReplaceMetric(interface_index, entry, metric);
Paul Stewart75e89d22011-08-01 10:00:02 -0700193 }
194
Paul Stewart7355ce12011-09-02 10:47:01 -0700195 if (GetDefaultRoute(interface_index, IPAddress::kFamilyIPv6, &entry) &&
Paul Stewart75e89d22011-08-01 10:00:02 -0700196 entry.metric != metric) {
Paul Stewartc1dec4d2011-12-08 15:25:28 -0800197 ReplaceMetric(interface_index, entry, metric);
Paul Stewart75e89d22011-08-01 10:00:02 -0700198 }
199}
200
Chris Masone2aa97072011-08-09 17:35:08 -0700201void RoutingTable::RouteMsgHandler(const RTNLMessage &msg) {
Paul Stewart9a908082011-08-31 12:18:48 -0700202 if (msg.type() != RTNLMessage::kTypeRoute ||
Paul Stewart7355ce12011-09-02 10:47:01 -0700203 msg.family() == IPAddress::kFamilyUnknown ||
Paul Stewart75e89d22011-08-01 10:00:02 -0700204 !msg.HasAttribute(RTA_OIF)) {
205 return;
206 }
207
208 const RTNLMessage::RouteStatus &route_status = msg.route_status();
209
210 if (route_status.type != RTN_UNICAST ||
211 route_status.protocol != RTPROT_BOOT ||
212 route_status.table != RT_TABLE_MAIN) {
213 return;
214 }
215
216 uint32 interface_index = 0;
217 if (!msg.GetAttribute(RTA_OIF).ConvertToCPUUInt32(&interface_index)) {
218 return;
219 }
220
221 uint32 metric = 0;
222 if (msg.HasAttribute(RTA_PRIORITY)) {
223 msg.GetAttribute(RTA_PRIORITY).ConvertToCPUUInt32(&metric);
224 }
225
226 IPAddress default_addr(msg.family());
227 default_addr.SetAddressToDefault();
228
229 ByteString dst_bytes(default_addr.address());
230 if (msg.HasAttribute(RTA_DST)) {
231 dst_bytes = msg.GetAttribute(RTA_DST);
232 }
233 ByteString src_bytes(default_addr.address());
234 if (msg.HasAttribute(RTA_SRC)) {
235 src_bytes = msg.GetAttribute(RTA_SRC);
236 }
237 ByteString gateway_bytes(default_addr.address());
238 if (msg.HasAttribute(RTA_GATEWAY)) {
239 gateway_bytes = msg.GetAttribute(RTA_GATEWAY);
240 }
241
242 RoutingTableEntry entry(
Paul Stewart9e3fcd72011-08-26 15:46:16 -0700243 IPAddress(msg.family(), dst_bytes, route_status.dst_prefix),
244 IPAddress(msg.family(), src_bytes, route_status.src_prefix),
Paul Stewart75e89d22011-08-01 10:00:02 -0700245 IPAddress(msg.family(), gateway_bytes),
246 metric,
247 route_status.scope,
248 true);
249
250 vector<RoutingTableEntry> &table = tables_[interface_index];
251 vector<RoutingTableEntry>::iterator nent;
252 for (nent = table.begin(); nent != table.end(); ++nent) {
253 if (nent->dst.Equals(entry.dst) &&
Paul Stewart75e89d22011-08-01 10:00:02 -0700254 nent->src.Equals(entry.src) &&
Paul Stewart75e89d22011-08-01 10:00:02 -0700255 nent->gateway.Equals(entry.gateway) &&
256 nent->scope == entry.scope) {
Paul Stewartc1dec4d2011-12-08 15:25:28 -0800257 if (msg.mode() == RTNLMessage::kModeDelete &&
258 nent->metric == entry.metric) {
Paul Stewart75e89d22011-08-01 10:00:02 -0700259 table.erase(nent);
Paul Stewartc1dec4d2011-12-08 15:25:28 -0800260 } else if (msg.mode() == RTNLMessage::kModeAdd) {
Paul Stewart75e89d22011-08-01 10:00:02 -0700261 nent->from_rtnl = true;
262 nent->metric = entry.metric;
263 }
264 return;
265 }
266 }
267
Paul Stewart9a908082011-08-31 12:18:48 -0700268 if (msg.mode() == RTNLMessage::kModeAdd) {
mukesh agrawal2c15d2c2012-02-21 16:09:21 -0800269 VLOG(2) << __func__ << " adding "
270 << "index " << interface_index
271 << "gateway " << entry.gateway.ToString() << " "
272 << "metric " << entry.metric;
Paul Stewart75e89d22011-08-01 10:00:02 -0700273 table.push_back(entry);
274 }
275}
276
277bool RoutingTable::ApplyRoute(uint32 interface_index,
278 const RoutingTableEntry &entry,
Paul Stewart9a908082011-08-31 12:18:48 -0700279 RTNLMessage::Mode mode,
Paul Stewart75e89d22011-08-01 10:00:02 -0700280 unsigned int flags) {
281 VLOG(2) << base::StringPrintf("%s: index %d mode %d flags 0x%x",
282 __func__, interface_index, mode, flags);
283
284 RTNLMessage msg(
Paul Stewart9a908082011-08-31 12:18:48 -0700285 RTNLMessage::kTypeRoute,
Paul Stewart75e89d22011-08-01 10:00:02 -0700286 mode,
Paul Stewarte6132022011-08-16 09:11:02 -0700287 NLM_F_REQUEST | flags,
Paul Stewart75e89d22011-08-01 10:00:02 -0700288 0,
289 0,
290 0,
291 entry.dst.family());
292
293 msg.set_route_status(RTNLMessage::RouteStatus(
Paul Stewart9e3fcd72011-08-26 15:46:16 -0700294 entry.dst.prefix(),
295 entry.src.prefix(),
Paul Stewart75e89d22011-08-01 10:00:02 -0700296 RT_TABLE_MAIN,
297 RTPROT_BOOT,
298 entry.scope,
299 RTN_UNICAST,
300 0));
301
302 msg.SetAttribute(RTA_DST, entry.dst.address());
303 if (!entry.src.IsDefault()) {
304 msg.SetAttribute(RTA_SRC, entry.src.address());
305 }
306 if (!entry.gateway.IsDefault()) {
307 msg.SetAttribute(RTA_GATEWAY, entry.gateway.address());
308 }
309 msg.SetAttribute(RTA_PRIORITY, ByteString::CreateFromCPUUInt32(entry.metric));
310 msg.SetAttribute(RTA_OIF, ByteString::CreateFromCPUUInt32(interface_index));
311
312 return RTNLHandler::GetInstance()->SendMessage(&msg);
313}
314
Paul Stewartc1dec4d2011-12-08 15:25:28 -0800315// Somewhat surprisingly, the kernel allows you to create multiple routes
316// to the same destination through the same interface with different metrics.
317// Therefore, to change the metric on a route, we can't just use the
318// NLM_F_REPLACE flag by itself. We have to explicitly remove the old route.
319// We do so after creating the route at a new metric so there is no traffic
320// disruption to existing network streams.
321void RoutingTable::ReplaceMetric(uint32 interface_index,
322 const RoutingTableEntry &entry,
323 uint32 metric) {
mukesh agrawal2c15d2c2012-02-21 16:09:21 -0800324 VLOG(2) << __func__ << " "
325 << "index " << interface_index << " metric " << metric;
Paul Stewartc1dec4d2011-12-08 15:25:28 -0800326 RoutingTableEntry new_entry = entry;
327 new_entry.metric = metric;
328 // First create the route at the new metric.
329 ApplyRoute(interface_index, new_entry, RTNLMessage::kModeAdd,
330 NLM_F_CREATE | NLM_F_REPLACE);
331 // Then delete the route at the old metric.
332 ApplyRoute(interface_index, entry, RTNLMessage::kModeDelete, 0);
333}
334
Paul Stewart75e89d22011-08-01 10:00:02 -0700335bool RoutingTable::FlushCache() {
336 static const char *kPaths[2] = { kRouteFlushPath4, kRouteFlushPath6 };
337 bool ret = true;
338
339 VLOG(2) << __func__;
340
341 for (size_t i = 0; i < arraysize(kPaths); ++i) {
342 if (file_util::WriteFile(FilePath(kPaths[i]), "-1", 2) != 2) {
343 LOG(ERROR) << base::StringPrintf("Cannot write to route flush file %s",
344 kPaths[i]);
345 ret = false;
346 }
347 }
348
349 return ret;
350}
351
352} // namespace shill