blob: dc78f4bc460eb635254edcb76b22945a5fe059e6 [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(
67 RTNLHandler::kRequestRoute | RTNLHandler::kRequestRoute6);
68}
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,
83 RTNLMessage::kMessageModeAdd,
84 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;
134 ApplyRoute(interface_index, old_entry, RTNLMessage::kMessageModeAdd,
135 NLM_F_CREATE | NLM_F_REPLACE);
136 }
137 return true;
138 } else {
139 ApplyRoute(interface_index,
140 old_entry,
141 RTNLMessage::kMessageModeDelete,
142 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,
151 0,
152 default_address,
153 0,
154 gateway_address,
155 metric,
156 RT_SCOPE_UNIVERSE,
157 false));
158}
159
160void RoutingTable::FlushRoutes(int interface_index) {
161 VLOG(2) << __func__;
162
163 base::hash_map<int, vector<RoutingTableEntry> >::iterator table =
164 tables_.find(interface_index);
165
166 if (table == tables_.end()) {
167 return;
168 }
169
170 vector<RoutingTableEntry>::iterator nent;
171
172 for (nent = table->second.begin(); nent != table->second.end(); ++nent) {
173 ApplyRoute(interface_index, *nent, RTNLMessage::kMessageModeDelete, 0);
174 }
175}
176
177void RoutingTable::ResetTable(int interface_index) {
178 tables_.erase(interface_index);
179}
180
181void RoutingTable::SetDefaultMetric(int interface_index, uint32 metric) {
182 RoutingTableEntry entry;
183
184 VLOG(2) << __func__;
185
186 if (GetDefaultRoute(interface_index, IPAddress::kAddressFamilyIPv4, &entry) &&
187 entry.metric != metric) {
188 entry.metric = metric;
189 ApplyRoute(interface_index, entry, RTNLMessage::kMessageModeAdd,
190 NLM_F_CREATE | NLM_F_REPLACE);
191 }
192
193 if (GetDefaultRoute(interface_index, IPAddress::kAddressFamilyIPv6, &entry) &&
194 entry.metric != metric) {
195 entry.metric = metric;
196 ApplyRoute(interface_index, entry, RTNLMessage::kMessageModeAdd,
197 NLM_F_CREATE | NLM_F_REPLACE);
198 }
199}
200
Chris Masone2aa97072011-08-09 17:35:08 -0700201void RoutingTable::RouteMsgHandler(const RTNLMessage &msg) {
Paul Stewart75e89d22011-08-01 10:00:02 -0700202 VLOG(2) << __func__;
203
204 if (msg.type() != RTNLMessage::kMessageTypeRoute ||
205 msg.family() == IPAddress::kAddressFamilyUnknown ||
206 !msg.HasAttribute(RTA_OIF)) {
207 return;
208 }
209
210 const RTNLMessage::RouteStatus &route_status = msg.route_status();
211
212 if (route_status.type != RTN_UNICAST ||
213 route_status.protocol != RTPROT_BOOT ||
214 route_status.table != RT_TABLE_MAIN) {
215 return;
216 }
217
218 uint32 interface_index = 0;
219 if (!msg.GetAttribute(RTA_OIF).ConvertToCPUUInt32(&interface_index)) {
220 return;
221 }
222
223 uint32 metric = 0;
224 if (msg.HasAttribute(RTA_PRIORITY)) {
225 msg.GetAttribute(RTA_PRIORITY).ConvertToCPUUInt32(&metric);
226 }
227
228 IPAddress default_addr(msg.family());
229 default_addr.SetAddressToDefault();
230
231 ByteString dst_bytes(default_addr.address());
232 if (msg.HasAttribute(RTA_DST)) {
233 dst_bytes = msg.GetAttribute(RTA_DST);
234 }
235 ByteString src_bytes(default_addr.address());
236 if (msg.HasAttribute(RTA_SRC)) {
237 src_bytes = msg.GetAttribute(RTA_SRC);
238 }
239 ByteString gateway_bytes(default_addr.address());
240 if (msg.HasAttribute(RTA_GATEWAY)) {
241 gateway_bytes = msg.GetAttribute(RTA_GATEWAY);
242 }
243
244 RoutingTableEntry entry(
245 IPAddress(msg.family(), dst_bytes),
246 route_status.dst_prefix,
247 IPAddress(msg.family(), src_bytes),
248 route_status.src_prefix,
249 IPAddress(msg.family(), gateway_bytes),
250 metric,
251 route_status.scope,
252 true);
253
254 vector<RoutingTableEntry> &table = tables_[interface_index];
255 vector<RoutingTableEntry>::iterator nent;
256 for (nent = table.begin(); nent != table.end(); ++nent) {
257 if (nent->dst.Equals(entry.dst) &&
258 nent->dst_prefix == entry.dst_prefix &&
259 nent->src.Equals(entry.src) &&
260 nent->src_prefix == entry.src_prefix &&
261 nent->gateway.Equals(entry.gateway) &&
262 nent->scope == entry.scope) {
263 if (msg.mode() == RTNLMessage::kMessageModeDelete) {
264 table.erase(nent);
265 } else {
266 nent->from_rtnl = true;
267 nent->metric = entry.metric;
268 }
269 return;
270 }
271 }
272
273 if (msg.mode() == RTNLMessage::kMessageModeAdd) {
274 table.push_back(entry);
275 }
276}
277
278bool RoutingTable::ApplyRoute(uint32 interface_index,
279 const RoutingTableEntry &entry,
280 RTNLMessage::MessageMode mode,
281 unsigned int flags) {
282 VLOG(2) << base::StringPrintf("%s: index %d mode %d flags 0x%x",
283 __func__, interface_index, mode, flags);
284
285 RTNLMessage msg(
286 RTNLMessage::kMessageTypeRoute,
287 mode,
288 flags,
289 0,
290 0,
291 0,
292 entry.dst.family());
293
294 msg.set_route_status(RTNLMessage::RouteStatus(
295 entry.dst_prefix,
296 entry.src_prefix,
297 RT_TABLE_MAIN,
298 RTPROT_BOOT,
299 entry.scope,
300 RTN_UNICAST,
301 0));
302
303 msg.SetAttribute(RTA_DST, entry.dst.address());
304 if (!entry.src.IsDefault()) {
305 msg.SetAttribute(RTA_SRC, entry.src.address());
306 }
307 if (!entry.gateway.IsDefault()) {
308 msg.SetAttribute(RTA_GATEWAY, entry.gateway.address());
309 }
310 msg.SetAttribute(RTA_PRIORITY, ByteString::CreateFromCPUUInt32(entry.metric));
311 msg.SetAttribute(RTA_OIF, ByteString::CreateFromCPUUInt32(interface_index));
312
313 return RTNLHandler::GetInstance()->SendMessage(&msg);
314}
315
316bool RoutingTable::FlushCache() {
317 static const char *kPaths[2] = { kRouteFlushPath4, kRouteFlushPath6 };
318 bool ret = true;
319
320 VLOG(2) << __func__;
321
322 for (size_t i = 0; i < arraysize(kPaths); ++i) {
323 if (file_util::WriteFile(FilePath(kPaths[i]), "-1", 2) != 2) {
324 LOG(ERROR) << base::StringPrintf("Cannot write to route flush file %s",
325 kPaths[i]);
326 ret = false;
327 }
328 }
329
330 return ret;
331}
332
333} // namespace shill