blob: 333b81ca95169f67b10d01aa35127c35d5bca211 [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 <sys/socket.h>
6#include <linux/rtnetlink.h>
7
8#include <base/logging.h>
9#include <base/stl_util-inl.h>
10#include <gtest/gtest.h>
11#include <gmock/gmock.h>
12
13#include "shill/byte_string.h"
14#include "shill/mock_control.h"
15#include "shill/mock_sockets.h"
16#include "shill/routing_table.h"
17#include "shill/routing_table_entry.h"
18#include "shill/rtnl_handler.h"
19#include "shill/rtnl_message.h"
20
21using testing::_;
22using testing::Return;
23using testing::Test;
24
25namespace shill {
26
27class TestEventDispatcher : public EventDispatcher {
28 public:
29 virtual IOInputHandler *CreateInputHandler(
30 int fd,
31 Callback1<InputData*>::Type *callback) {
32 return NULL;
33 }
34};
35
36class RoutingTableTest : public Test {
37 public:
38 RoutingTableTest() : routing_table_(RoutingTable::GetInstance()) {}
39
40 base::hash_map<int, std::vector<RoutingTableEntry> > *GetRoutingTables() {
41 return &routing_table_->tables_;
42 }
43
44 void SendRouteMsg(RTNLMessage::MessageMode mode,
45 uint32 interface_index,
46 const RoutingTableEntry &entry);
47
48 protected:
49 static const int kTestSocket;
50 static const uint32 kTestDeviceIndex0;
51 static const uint32 kTestDeviceIndex1;
52 static const char kTestDeviceName0[];
53 static const char kTestNetAddress0[];
54 static const char kTestNetAddress1[];
55
56 void StartRTNLHandler();
57 void StopRTNLHandler();
58
59 MockSockets sockets_;
60 RoutingTable *routing_table_;
61 TestEventDispatcher dispatcher_;
62};
63
64const int RoutingTableTest::kTestSocket = 123;
65const uint32 RoutingTableTest::kTestDeviceIndex0 = 12345;
66const uint32 RoutingTableTest::kTestDeviceIndex1 = 67890;
67const char RoutingTableTest::kTestDeviceName0[] = "test-device0";
68const char RoutingTableTest::kTestNetAddress0[] = "192.168.1.1";
69const char RoutingTableTest::kTestNetAddress1[] = "192.168.1.2";
70
71
72MATCHER_P4(IsRoutingPacket, mode, index, entry, flags, "") {
73 // NB: Matchers don't get passed multiple arguments, so we can
74 // get the address of a Send(), its length, but not both.
75 // We have to punt and assume the length is correct -- which
76 // should already be tested in rtnl_message_unittest.
77 struct nlmsghdr hdr;
78 memcpy(&hdr, arg, sizeof(hdr));
79
80 RTNLMessage msg;
81 if (!msg.Decode(ByteString(reinterpret_cast<const unsigned char *>(arg),
82 hdr.nlmsg_len))) {
83 return false;
84 }
85
86 const RTNLMessage::RouteStatus &status = msg.route_status();
87
88 uint32 oif;
89 uint32 priority;
90
91 return
92 msg.type() == RTNLMessage::kMessageTypeRoute &&
93 msg.family() == entry.gateway.family() &&
94 msg.flags() == flags &&
95 status.table == RT_TABLE_MAIN &&
96 status.protocol == RTPROT_BOOT &&
97 status.scope == entry.scope &&
98 status.type == RTN_UNICAST &&
99 msg.HasAttribute(RTA_DST) &&
100 IPAddress(msg.family(),
101 msg.GetAttribute(RTA_DST)).Equals(entry.dst) &&
102 !msg.HasAttribute(RTA_SRC) &&
103 msg.HasAttribute(RTA_GATEWAY) &&
104 IPAddress(msg.family(),
105 msg.GetAttribute(RTA_GATEWAY)).Equals(entry.gateway) &&
106 msg.GetAttribute(RTA_OIF).ConvertToCPUUInt32(&oif) &&
107 oif == index &&
108 msg.GetAttribute(RTA_PRIORITY).ConvertToCPUUInt32(&priority) &&
109 priority == entry.metric;
110}
111
112void RoutingTableTest::SendRouteMsg(RTNLMessage::MessageMode mode,
113 uint32 interface_index,
114 const RoutingTableEntry &entry) {
115 RTNLMessage msg(
116 RTNLMessage::kMessageTypeRoute,
117 mode,
118 0,
119 0,
120 0,
121 0,
122 entry.dst.family());
123
124 msg.set_route_status(RTNLMessage::RouteStatus(
125 entry.dst_prefix,
126 entry.src_prefix,
127 RT_TABLE_MAIN,
128 RTPROT_BOOT,
129 entry.scope,
130 RTN_UNICAST,
131 0));
132
133 msg.SetAttribute(RTA_DST, entry.dst.address());
134 if (!entry.src.IsDefault()) {
135 msg.SetAttribute(RTA_SRC, entry.src.address());
136 }
137 if (!entry.gateway.IsDefault()) {
138 msg.SetAttribute(RTA_GATEWAY, entry.gateway.address());
139 }
140 msg.SetAttribute(RTA_PRIORITY, ByteString::CreateFromCPUUInt32(entry.metric));
141 msg.SetAttribute(RTA_OIF, ByteString::CreateFromCPUUInt32(interface_index));
142
143 ByteString msgdata = msg.Encode();
144 EXPECT_NE(0, msgdata.GetLength());
145
146 InputData data(msgdata.GetData(), msgdata.GetLength());
147 RTNLHandler::GetInstance()->ParseRTNL(&data);
148}
149
150void RoutingTableTest::StartRTNLHandler() {
151 EXPECT_CALL(sockets_, Socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE))
152 .WillOnce(Return(kTestSocket));
153 EXPECT_CALL(sockets_, Bind(kTestSocket, _, sizeof(sockaddr_nl)))
154 .WillOnce(Return(0));
155 RTNLHandler::GetInstance()->Start(&dispatcher_, &sockets_);
156}
157
158void RoutingTableTest::StopRTNLHandler() {
159 EXPECT_CALL(sockets_, Close(_)).WillOnce(Return(0));
160 RTNLHandler::GetInstance()->Stop();
161}
162
163TEST_F(RoutingTableTest, RouteAddDelete) {
164 EXPECT_CALL(sockets_, SendTo(kTestSocket, _, _, 0, _, sizeof(sockaddr_nl)));
165 StartRTNLHandler();
166 routing_table_->Start();
167
168 // Expect the tables to be empty by default
169 EXPECT_EQ(0, GetRoutingTables()->size());
170
171 IPAddress default_address(IPAddress::kAddressFamilyIPv4);
172 default_address.SetAddressToDefault();
173
174 IPAddress gateway_address0(IPAddress::kAddressFamilyIPv4);
175 gateway_address0.SetAddressFromString(kTestNetAddress0);
176
177 int metric = 10;
178
179 RoutingTableEntry entry0(default_address,
180 0,
181 default_address,
182 0,
183 gateway_address0,
184 metric,
185 RT_SCOPE_UNIVERSE,
186 true);
187 // Add a single entry
188 SendRouteMsg(RTNLMessage::kMessageModeAdd,
189 kTestDeviceIndex0,
190 entry0);
191
192 base::hash_map<int, std::vector<RoutingTableEntry> > *tables =
193 GetRoutingTables();
194
195 // Should have a single table, which should in turn have a single entry
196 EXPECT_EQ(1, tables->size());
197 EXPECT_TRUE(ContainsKey(*tables, kTestDeviceIndex0));
198 EXPECT_EQ(1, (*tables)[kTestDeviceIndex0].size());
199
200 RoutingTableEntry test_entry = (*tables)[kTestDeviceIndex0][0];
201 EXPECT_TRUE(entry0.Equals(test_entry));
202
203 // Add a second entry for a different interface
204 SendRouteMsg(RTNLMessage::kMessageModeAdd,
205 kTestDeviceIndex1,
206 entry0);
207
208 // Should have two tables, which should have a single entry each
209 EXPECT_EQ(2, tables->size());
210 EXPECT_TRUE(ContainsKey(*tables, kTestDeviceIndex1));
211 EXPECT_EQ(1, (*tables)[kTestDeviceIndex0].size());
212 EXPECT_EQ(1, (*tables)[kTestDeviceIndex1].size());
213
214 test_entry = (*tables)[kTestDeviceIndex1][0];
215 EXPECT_TRUE(entry0.Equals(test_entry));
216
217 IPAddress gateway_address1(IPAddress::kAddressFamilyIPv4);
218 gateway_address1.SetAddressFromString(kTestNetAddress1);
219
220 RoutingTableEntry entry1(default_address,
221 0,
222 default_address,
223 0,
224 gateway_address1,
225 metric,
226 RT_SCOPE_UNIVERSE,
227 true);
228
229 // Add a second gateway route to the second interface
230 SendRouteMsg(RTNLMessage::kMessageModeAdd,
231 kTestDeviceIndex1,
232 entry1);
233
234 // Should have two tables, one of which has a single entry, the other has two
235 EXPECT_EQ(2, tables->size());
236 EXPECT_EQ(1, (*tables)[kTestDeviceIndex0].size());
237 EXPECT_EQ(2, (*tables)[kTestDeviceIndex1].size());
238
239 test_entry = (*tables)[kTestDeviceIndex1][1];
240 EXPECT_TRUE(entry1.Equals(test_entry));
241
242 // Remove the first gateway route from the second interface
243 SendRouteMsg(RTNLMessage::kMessageModeDelete,
244 kTestDeviceIndex1,
245 entry0);
246
247 // We should be back to having one route per table
248 EXPECT_EQ(2, tables->size());
249 EXPECT_EQ(1, (*tables)[kTestDeviceIndex0].size());
250 EXPECT_EQ(1, (*tables)[kTestDeviceIndex1].size());
251
252 test_entry = (*tables)[kTestDeviceIndex1][0];
253 EXPECT_TRUE(entry1.Equals(test_entry));
254
255 // Send a duplicate of the second gatway route message, changing the metric
256 RoutingTableEntry entry2(entry1);
257 entry2.metric++;
258 SendRouteMsg(RTNLMessage::kMessageModeAdd,
259 kTestDeviceIndex1,
260 entry2);
261
262 // Routing table size shouldn't change, but the new metric should match
263 EXPECT_EQ(1, (*tables)[kTestDeviceIndex1].size());
264 test_entry = (*tables)[kTestDeviceIndex1][0];
265 EXPECT_TRUE(entry2.Equals(test_entry));
266
267 // Find a matching entry
268 EXPECT_TRUE(routing_table_->GetDefaultRoute(kTestDeviceIndex1,
269 IPAddress::kAddressFamilyIPv4,
270 &test_entry));
271 EXPECT_TRUE(entry2.Equals(test_entry));
272
273 // Test that a search for a non-matching family fails
274 EXPECT_FALSE(routing_table_->GetDefaultRoute(kTestDeviceIndex1,
275 IPAddress::kAddressFamilyIPv6,
276 &test_entry));
277
278 // Remove last entry from an existing interface and test that we now fail
279 SendRouteMsg(RTNLMessage::kMessageModeDelete,
280 kTestDeviceIndex1,
281 entry2);
282
283 EXPECT_FALSE(routing_table_->GetDefaultRoute(kTestDeviceIndex1,
284 IPAddress::kAddressFamilyIPv4,
285 &test_entry));
286
287 // Add a route from an IPConfig entry
288 MockControl control;
289 IPConfigRefPtr ipconfig(new IPConfig(&control, kTestDeviceName0));
290 IPConfig::Properties properties;
291 properties.address_family = IPAddress::kAddressFamilyIPv4;
292 properties.gateway = kTestNetAddress0;
293 properties.address = kTestNetAddress1;
294 ipconfig->UpdateProperties(properties, true);
295
296 EXPECT_CALL(sockets_,
297 Send(kTestSocket,
298 IsRoutingPacket(RTNLMessage::kMessageModeAdd,
299 kTestDeviceIndex1,
300 entry0,
301 NLM_F_CREATE | NLM_F_EXCL),
302 _,
303 0));
304 EXPECT_TRUE(routing_table_->SetDefaultRoute(kTestDeviceIndex1,
305 ipconfig,
306 metric));
307
308 // The table entry should look much like entry0, except with from_rtnl = false
309 RoutingTableEntry entry3(entry0);
310 entry3.from_rtnl = false;
311 EXPECT_TRUE(routing_table_->GetDefaultRoute(kTestDeviceIndex1,
312 IPAddress::kAddressFamilyIPv4,
313 &test_entry));
314 EXPECT_TRUE(entry3.Equals(test_entry));
315
316 // Setting the same route on the interface with a different metric should
317 // push the route with different flags to indicate we are replacing it.
318 RoutingTableEntry entry4(entry3);
319 entry4.metric += 10;
320 EXPECT_CALL(sockets_,
321 Send(kTestSocket,
322 IsRoutingPacket(RTNLMessage::kMessageModeAdd,
323 kTestDeviceIndex1,
324 entry4,
325 NLM_F_CREATE | NLM_F_REPLACE),
326 _,
327 0));
328 EXPECT_TRUE(routing_table_->SetDefaultRoute(kTestDeviceIndex1,
329 ipconfig,
330 entry4.metric));
331
332 // Test that removing the table causes the route to disappear
333 routing_table_->ResetTable(kTestDeviceIndex1);
334 EXPECT_FALSE(ContainsKey(*tables, kTestDeviceIndex1));
335 EXPECT_FALSE(routing_table_->GetDefaultRoute(kTestDeviceIndex1,
336 IPAddress::kAddressFamilyIPv4,
337 &test_entry));
338 EXPECT_EQ(1, GetRoutingTables()->size());
339
340 // When we set the metric on an existing route, a new add message appears
341 RoutingTableEntry entry5(entry4);
342 entry5.metric += 10;
343 EXPECT_CALL(sockets_,
344 Send(kTestSocket,
345 IsRoutingPacket(RTNLMessage::kMessageModeAdd,
346 kTestDeviceIndex0,
347 entry5,
348 NLM_F_CREATE | NLM_F_REPLACE),
349 _,
350 0));
351 routing_table_->SetDefaultMetric(kTestDeviceIndex0, entry5.metric);
352
353 // Ask to flush table0. We should see a delete message sent
354 EXPECT_CALL(sockets_,
355 Send(kTestSocket,
356 IsRoutingPacket(RTNLMessage::kMessageModeDelete,
357 kTestDeviceIndex0,
358 entry0,
359 0),
360 _,
361 0));
362 routing_table_->FlushRoutes(kTestDeviceIndex0);
363
364 // Test that the routing table size returns to zero
365 EXPECT_EQ(1, GetRoutingTables()->size());
366 routing_table_->ResetTable(kTestDeviceIndex0);
367 EXPECT_EQ(0, GetRoutingTables()->size());
368
369 routing_table_->Stop();
370 StopRTNLHandler();
371}
372
373} // namespace shill