blob: 447cba065c5526d63d0ab91372ad243cc3c85283 [file] [log] [blame]
Paul Stewart75e89d22011-08-01 10:00:02 -07001// Copyright (c) 2011 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/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) {
78 VLOG(2) << __func__;
79
80 CHECK(!entry.from_rtnl);
81 if (!ApplyRoute(interface_index,
82 entry,
Paul Stewart9a908082011-08-31 12:18:48 -070083 RTNLMessage::kModeAdd,
Paul Stewart75e89d22011-08-01 10:00:02 -070084 NLM_F_CREATE | NLM_F_EXCL)) {
85 return false;
86 }
87 tables_[interface_index].push_back(entry);
88 return true;
89}
90
91bool RoutingTable::GetDefaultRoute(int interface_index,
92 IPAddress::Family family,
93 RoutingTableEntry *entry) {
94 VLOG(2) << __func__;
95
96 base::hash_map<int, vector<RoutingTableEntry> >::iterator table =
97 tables_.find(interface_index);
98
99 if (table == tables_.end()) {
100 return false;
101 }
102
103 vector<RoutingTableEntry>::iterator nent;
104
105 for (nent = table->second.begin(); nent != table->second.end(); ++nent) {
106 if (nent->dst.IsDefault() && nent->dst.family() == family) {
107 *entry = *nent;
108 return true;
109 }
110 }
111
112 return false;
113}
114
115bool RoutingTable::SetDefaultRoute(int interface_index,
116 const IPConfigRefPtr &ipconfig,
117 uint32 metric) {
118 const IPConfig::Properties &ipconfig_props = ipconfig->properties();
119 RoutingTableEntry old_entry;
120
121 VLOG(2) << __func__;
122
123 IPAddress gateway_address(ipconfig_props.address_family);
124 if (!gateway_address.SetAddressFromString(ipconfig_props.gateway)) {
125 return false;
126 }
127
128 if (GetDefaultRoute(interface_index,
129 ipconfig_props.address_family,
130 &old_entry)) {
131 if (old_entry.gateway.Equals(gateway_address)) {
132 if (old_entry.metric != metric) {
133 old_entry.metric = metric;
Paul Stewart9a908082011-08-31 12:18:48 -0700134 ApplyRoute(interface_index, old_entry, RTNLMessage::kModeAdd,
Paul Stewart75e89d22011-08-01 10:00:02 -0700135 NLM_F_CREATE | NLM_F_REPLACE);
136 }
137 return true;
138 } else {
139 ApplyRoute(interface_index,
140 old_entry,
Paul Stewart9a908082011-08-31 12:18:48 -0700141 RTNLMessage::kModeDelete,
Paul Stewart75e89d22011-08-01 10:00:02 -0700142 0);
143 }
144 }
145
146 IPAddress default_address(ipconfig_props.address_family);
147 default_address.SetAddressToDefault();
148
149 return AddRoute(interface_index,
150 RoutingTableEntry(default_address,
Paul Stewart75e89d22011-08-01 10:00:02 -0700151 default_address,
Paul Stewart75e89d22011-08-01 10:00:02 -0700152 gateway_address,
153 metric,
154 RT_SCOPE_UNIVERSE,
155 false));
156}
157
158void RoutingTable::FlushRoutes(int interface_index) {
159 VLOG(2) << __func__;
160
161 base::hash_map<int, vector<RoutingTableEntry> >::iterator table =
162 tables_.find(interface_index);
163
164 if (table == tables_.end()) {
165 return;
166 }
167
168 vector<RoutingTableEntry>::iterator nent;
169
170 for (nent = table->second.begin(); nent != table->second.end(); ++nent) {
Paul Stewart9a908082011-08-31 12:18:48 -0700171 ApplyRoute(interface_index, *nent, RTNLMessage::kModeDelete, 0);
Paul Stewart75e89d22011-08-01 10:00:02 -0700172 }
173}
174
175void RoutingTable::ResetTable(int interface_index) {
176 tables_.erase(interface_index);
177}
178
179void RoutingTable::SetDefaultMetric(int interface_index, uint32 metric) {
180 RoutingTableEntry entry;
181
182 VLOG(2) << __func__;
183
Paul Stewart7355ce12011-09-02 10:47:01 -0700184 if (GetDefaultRoute(interface_index, IPAddress::kFamilyIPv4, &entry) &&
Paul Stewart75e89d22011-08-01 10:00:02 -0700185 entry.metric != metric) {
186 entry.metric = metric;
Paul Stewart9a908082011-08-31 12:18:48 -0700187 ApplyRoute(interface_index, entry, RTNLMessage::kModeAdd,
Paul Stewart75e89d22011-08-01 10:00:02 -0700188 NLM_F_CREATE | NLM_F_REPLACE);
189 }
190
Paul Stewart7355ce12011-09-02 10:47:01 -0700191 if (GetDefaultRoute(interface_index, IPAddress::kFamilyIPv6, &entry) &&
Paul Stewart75e89d22011-08-01 10:00:02 -0700192 entry.metric != metric) {
193 entry.metric = metric;
Paul Stewart9a908082011-08-31 12:18:48 -0700194 ApplyRoute(interface_index, entry, RTNLMessage::kModeAdd,
Paul Stewart75e89d22011-08-01 10:00:02 -0700195 NLM_F_CREATE | NLM_F_REPLACE);
196 }
197}
198
Chris Masone2aa97072011-08-09 17:35:08 -0700199void RoutingTable::RouteMsgHandler(const RTNLMessage &msg) {
Paul Stewart75e89d22011-08-01 10:00:02 -0700200 VLOG(2) << __func__;
201
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 Stewart9a908082011-08-31 12:18:48 -0700257 if (msg.mode() == RTNLMessage::kModeDelete) {
Paul Stewart75e89d22011-08-01 10:00:02 -0700258 table.erase(nent);
259 } else {
260 nent->from_rtnl = true;
261 nent->metric = entry.metric;
262 }
263 return;
264 }
265 }
266
Paul Stewart9a908082011-08-31 12:18:48 -0700267 if (msg.mode() == RTNLMessage::kModeAdd) {
Paul Stewart75e89d22011-08-01 10:00:02 -0700268 table.push_back(entry);
269 }
270}
271
272bool RoutingTable::ApplyRoute(uint32 interface_index,
273 const RoutingTableEntry &entry,
Paul Stewart9a908082011-08-31 12:18:48 -0700274 RTNLMessage::Mode mode,
Paul Stewart75e89d22011-08-01 10:00:02 -0700275 unsigned int flags) {
276 VLOG(2) << base::StringPrintf("%s: index %d mode %d flags 0x%x",
277 __func__, interface_index, mode, flags);
278
279 RTNLMessage msg(
Paul Stewart9a908082011-08-31 12:18:48 -0700280 RTNLMessage::kTypeRoute,
Paul Stewart75e89d22011-08-01 10:00:02 -0700281 mode,
Paul Stewarte6132022011-08-16 09:11:02 -0700282 NLM_F_REQUEST | flags,
Paul Stewart75e89d22011-08-01 10:00:02 -0700283 0,
284 0,
285 0,
286 entry.dst.family());
287
288 msg.set_route_status(RTNLMessage::RouteStatus(
Paul Stewart9e3fcd72011-08-26 15:46:16 -0700289 entry.dst.prefix(),
290 entry.src.prefix(),
Paul Stewart75e89d22011-08-01 10:00:02 -0700291 RT_TABLE_MAIN,
292 RTPROT_BOOT,
293 entry.scope,
294 RTN_UNICAST,
295 0));
296
297 msg.SetAttribute(RTA_DST, entry.dst.address());
298 if (!entry.src.IsDefault()) {
299 msg.SetAttribute(RTA_SRC, entry.src.address());
300 }
301 if (!entry.gateway.IsDefault()) {
302 msg.SetAttribute(RTA_GATEWAY, entry.gateway.address());
303 }
304 msg.SetAttribute(RTA_PRIORITY, ByteString::CreateFromCPUUInt32(entry.metric));
305 msg.SetAttribute(RTA_OIF, ByteString::CreateFromCPUUInt32(interface_index));
306
307 return RTNLHandler::GetInstance()->SendMessage(&msg);
308}
309
310bool RoutingTable::FlushCache() {
311 static const char *kPaths[2] = { kRouteFlushPath4, kRouteFlushPath6 };
312 bool ret = true;
313
314 VLOG(2) << __func__;
315
316 for (size_t i = 0; i < arraysize(kPaths); ++i) {
317 if (file_util::WriteFile(FilePath(kPaths[i]), "-1", 2) != 2) {
318 LOG(ERROR) << base::StringPrintf("Cannot write to route flush file %s",
319 kPaths[i]);
320 ret = false;
321 }
322 }
323
324 return ret;
325}
326
327} // namespace shill