blob: a058b0f0c75486eff90d42d733aedb901126af55 [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
41// static
42const char RoutingTable::kRouteFlushPath4[] = "/proc/sys/net/ipv4/route/flush";
43const char RoutingTable::kRouteFlushPath6[] = "/proc/sys/net/ipv6/route/flush";
44
45RoutingTable::RoutingTable()
46 : route_callback_(NewCallback(this, &RoutingTable::RouteMsgHandler)),
47 route_listener_(NULL) {
48 VLOG(2) << __func__;
49}
50
51RoutingTable::~RoutingTable() {}
52
53RoutingTable* RoutingTable::GetInstance() {
54 return Singleton<RoutingTable>::get();
55}
56
57void RoutingTable::Start() {
58 VLOG(2) << __func__;
59
60 route_listener_.reset(
61 new RTNLListener(RTNLHandler::kRequestRoute, route_callback_.get()));
62 RTNLHandler::GetInstance()->RequestDump(
63 RTNLHandler::kRequestRoute | RTNLHandler::kRequestRoute6);
64}
65
66void RoutingTable::Stop() {
67 VLOG(2) << __func__;
68
69 route_listener_.reset();
70}
71
72bool RoutingTable::AddRoute(int interface_index,
73 const RoutingTableEntry &entry) {
74 VLOG(2) << __func__;
75
76 CHECK(!entry.from_rtnl);
77 if (!ApplyRoute(interface_index,
78 entry,
79 RTNLMessage::kMessageModeAdd,
80 NLM_F_CREATE | NLM_F_EXCL)) {
81 return false;
82 }
83 tables_[interface_index].push_back(entry);
84 return true;
85}
86
87bool RoutingTable::GetDefaultRoute(int interface_index,
88 IPAddress::Family family,
89 RoutingTableEntry *entry) {
90 VLOG(2) << __func__;
91
92 base::hash_map<int, vector<RoutingTableEntry> >::iterator table =
93 tables_.find(interface_index);
94
95 if (table == tables_.end()) {
96 return false;
97 }
98
99 vector<RoutingTableEntry>::iterator nent;
100
101 for (nent = table->second.begin(); nent != table->second.end(); ++nent) {
102 if (nent->dst.IsDefault() && nent->dst.family() == family) {
103 *entry = *nent;
104 return true;
105 }
106 }
107
108 return false;
109}
110
111bool RoutingTable::SetDefaultRoute(int interface_index,
112 const IPConfigRefPtr &ipconfig,
113 uint32 metric) {
114 const IPConfig::Properties &ipconfig_props = ipconfig->properties();
115 RoutingTableEntry old_entry;
116
117 VLOG(2) << __func__;
118
119 IPAddress gateway_address(ipconfig_props.address_family);
120 if (!gateway_address.SetAddressFromString(ipconfig_props.gateway)) {
121 return false;
122 }
123
124 if (GetDefaultRoute(interface_index,
125 ipconfig_props.address_family,
126 &old_entry)) {
127 if (old_entry.gateway.Equals(gateway_address)) {
128 if (old_entry.metric != metric) {
129 old_entry.metric = metric;
130 ApplyRoute(interface_index, old_entry, RTNLMessage::kMessageModeAdd,
131 NLM_F_CREATE | NLM_F_REPLACE);
132 }
133 return true;
134 } else {
135 ApplyRoute(interface_index,
136 old_entry,
137 RTNLMessage::kMessageModeDelete,
138 0);
139 }
140 }
141
142 IPAddress default_address(ipconfig_props.address_family);
143 default_address.SetAddressToDefault();
144
145 return AddRoute(interface_index,
146 RoutingTableEntry(default_address,
147 0,
148 default_address,
149 0,
150 gateway_address,
151 metric,
152 RT_SCOPE_UNIVERSE,
153 false));
154}
155
156void RoutingTable::FlushRoutes(int interface_index) {
157 VLOG(2) << __func__;
158
159 base::hash_map<int, vector<RoutingTableEntry> >::iterator table =
160 tables_.find(interface_index);
161
162 if (table == tables_.end()) {
163 return;
164 }
165
166 vector<RoutingTableEntry>::iterator nent;
167
168 for (nent = table->second.begin(); nent != table->second.end(); ++nent) {
169 ApplyRoute(interface_index, *nent, RTNLMessage::kMessageModeDelete, 0);
170 }
171}
172
173void RoutingTable::ResetTable(int interface_index) {
174 tables_.erase(interface_index);
175}
176
177void RoutingTable::SetDefaultMetric(int interface_index, uint32 metric) {
178 RoutingTableEntry entry;
179
180 VLOG(2) << __func__;
181
182 if (GetDefaultRoute(interface_index, IPAddress::kAddressFamilyIPv4, &entry) &&
183 entry.metric != metric) {
184 entry.metric = metric;
185 ApplyRoute(interface_index, entry, RTNLMessage::kMessageModeAdd,
186 NLM_F_CREATE | NLM_F_REPLACE);
187 }
188
189 if (GetDefaultRoute(interface_index, IPAddress::kAddressFamilyIPv6, &entry) &&
190 entry.metric != metric) {
191 entry.metric = metric;
192 ApplyRoute(interface_index, entry, RTNLMessage::kMessageModeAdd,
193 NLM_F_CREATE | NLM_F_REPLACE);
194 }
195}
196
197void RoutingTable::RouteMsgHandler(struct nlmsghdr *hdr) {
198 RTNLMessage msg;
199
200 // TODO(pstew): When RTNLHandler starts using RTNLMessages, this goes away
201 if (!msg.Decode(ByteString(reinterpret_cast<unsigned char *>(hdr),
202 hdr->nlmsg_len))) {
203 return;
204 }
205
206 VLOG(2) << __func__;
207
208 if (msg.type() != RTNLMessage::kMessageTypeRoute ||
209 msg.family() == IPAddress::kAddressFamilyUnknown ||
210 !msg.HasAttribute(RTA_OIF)) {
211 return;
212 }
213
214 const RTNLMessage::RouteStatus &route_status = msg.route_status();
215
216 if (route_status.type != RTN_UNICAST ||
217 route_status.protocol != RTPROT_BOOT ||
218 route_status.table != RT_TABLE_MAIN) {
219 return;
220 }
221
222 uint32 interface_index = 0;
223 if (!msg.GetAttribute(RTA_OIF).ConvertToCPUUInt32(&interface_index)) {
224 return;
225 }
226
227 uint32 metric = 0;
228 if (msg.HasAttribute(RTA_PRIORITY)) {
229 msg.GetAttribute(RTA_PRIORITY).ConvertToCPUUInt32(&metric);
230 }
231
232 IPAddress default_addr(msg.family());
233 default_addr.SetAddressToDefault();
234
235 ByteString dst_bytes(default_addr.address());
236 if (msg.HasAttribute(RTA_DST)) {
237 dst_bytes = msg.GetAttribute(RTA_DST);
238 }
239 ByteString src_bytes(default_addr.address());
240 if (msg.HasAttribute(RTA_SRC)) {
241 src_bytes = msg.GetAttribute(RTA_SRC);
242 }
243 ByteString gateway_bytes(default_addr.address());
244 if (msg.HasAttribute(RTA_GATEWAY)) {
245 gateway_bytes = msg.GetAttribute(RTA_GATEWAY);
246 }
247
248 RoutingTableEntry entry(
249 IPAddress(msg.family(), dst_bytes),
250 route_status.dst_prefix,
251 IPAddress(msg.family(), src_bytes),
252 route_status.src_prefix,
253 IPAddress(msg.family(), gateway_bytes),
254 metric,
255 route_status.scope,
256 true);
257
258 vector<RoutingTableEntry> &table = tables_[interface_index];
259 vector<RoutingTableEntry>::iterator nent;
260 for (nent = table.begin(); nent != table.end(); ++nent) {
261 if (nent->dst.Equals(entry.dst) &&
262 nent->dst_prefix == entry.dst_prefix &&
263 nent->src.Equals(entry.src) &&
264 nent->src_prefix == entry.src_prefix &&
265 nent->gateway.Equals(entry.gateway) &&
266 nent->scope == entry.scope) {
267 if (msg.mode() == RTNLMessage::kMessageModeDelete) {
268 table.erase(nent);
269 } else {
270 nent->from_rtnl = true;
271 nent->metric = entry.metric;
272 }
273 return;
274 }
275 }
276
277 if (msg.mode() == RTNLMessage::kMessageModeAdd) {
278 table.push_back(entry);
279 }
280}
281
282bool RoutingTable::ApplyRoute(uint32 interface_index,
283 const RoutingTableEntry &entry,
284 RTNLMessage::MessageMode mode,
285 unsigned int flags) {
286 VLOG(2) << base::StringPrintf("%s: index %d mode %d flags 0x%x",
287 __func__, interface_index, mode, flags);
288
289 RTNLMessage msg(
290 RTNLMessage::kMessageTypeRoute,
291 mode,
292 flags,
293 0,
294 0,
295 0,
296 entry.dst.family());
297
298 msg.set_route_status(RTNLMessage::RouteStatus(
299 entry.dst_prefix,
300 entry.src_prefix,
301 RT_TABLE_MAIN,
302 RTPROT_BOOT,
303 entry.scope,
304 RTN_UNICAST,
305 0));
306
307 msg.SetAttribute(RTA_DST, entry.dst.address());
308 if (!entry.src.IsDefault()) {
309 msg.SetAttribute(RTA_SRC, entry.src.address());
310 }
311 if (!entry.gateway.IsDefault()) {
312 msg.SetAttribute(RTA_GATEWAY, entry.gateway.address());
313 }
314 msg.SetAttribute(RTA_PRIORITY, ByteString::CreateFromCPUUInt32(entry.metric));
315 msg.SetAttribute(RTA_OIF, ByteString::CreateFromCPUUInt32(interface_index));
316
317 return RTNLHandler::GetInstance()->SendMessage(&msg);
318}
319
320bool RoutingTable::FlushCache() {
321 static const char *kPaths[2] = { kRouteFlushPath4, kRouteFlushPath6 };
322 bool ret = true;
323
324 VLOG(2) << __func__;
325
326 for (size_t i = 0; i < arraysize(kPaths); ++i) {
327 if (file_util::WriteFile(FilePath(kPaths[i]), "-1", 2) != 2) {
328 LOG(ERROR) << base::StringPrintf("Cannot write to route flush file %s",
329 kPaths[i]);
330 ret = false;
331 }
332 }
333
334 return ret;
335}
336
337} // namespace shill