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