blob: fbcc9da5cbc52bbeffa2bc61917742a493463c84 [file] [log] [blame]
Paul Stewart960d4692013-12-09 07:41:59 -08001// Copyright (c) 2013 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/icmp.h"
6
7#include <netinet/in.h>
8#include <netinet/ip_icmp.h>
9
10#include <gtest/gtest.h>
11
12#include "shill/ip_address.h"
13#include "shill/mock_log.h"
14#include "shill/mock_sockets.h"
15
16using testing::_;
17using testing::HasSubstr;
18using testing::InSequence;
19using testing::Return;
20using testing::StrictMock;
21using testing::Test;
22
23namespace shill {
24
25class IcmpTest : public Test {
26 public:
27 IcmpTest() {}
28 virtual ~IcmpTest() {}
29
30 virtual void SetUp() {
31 sockets_ = new StrictMock<MockSockets>();
32 // Passes ownership.
33 icmp_.sockets_.reset(sockets_);
34 }
35
36 virtual void TearDown() {
37 if (icmp_.IsStarted()) {
38 EXPECT_CALL(*sockets_, Close(kSocketFD));
39 icmp_.Stop();
40 }
41 EXPECT_FALSE(icmp_.IsStarted());
42 }
43
44 protected:
45 static const int kSocketFD;
46 static const char kIPAddress[];
47
48 int GetSocket() { return icmp_.socket_; }
49 bool StartIcmp() { return StartIcmpWithFD(kSocketFD); }
50 bool StartIcmpWithFD(int fd) {
51 EXPECT_CALL(*sockets_, Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP))
52 .WillOnce(Return(fd));
53 EXPECT_CALL(*sockets_, SetNonBlocking(fd)).WillOnce(Return(0));
54 bool start_status = icmp_.Start();
55 EXPECT_TRUE(start_status);
56 EXPECT_EQ(fd, icmp_.socket_);
57 EXPECT_TRUE(icmp_.IsStarted());
58 return start_status;
59 }
60
61 // Owned by Icmp, and tracked here only for mocks.
62 MockSockets *sockets_;
63
64 Icmp icmp_;
65};
66
67
68const int IcmpTest::kSocketFD = 456;
69const char IcmpTest::kIPAddress[] = "10.0.1.1";
70
71
72TEST_F(IcmpTest, Constructor) {
73 EXPECT_EQ(-1, GetSocket());
74 EXPECT_FALSE(icmp_.IsStarted());
75}
76
77TEST_F(IcmpTest, SocketOpenFail) {
78 ScopedMockLog log;
79 EXPECT_CALL(log,
80 Log(logging::LOG_ERROR, _,
81 HasSubstr("Could not create ICMP socket"))).Times(1);
82
83 EXPECT_CALL(*sockets_, Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP))
84 .WillOnce(Return(-1));
85 EXPECT_FALSE(icmp_.Start());
86 EXPECT_FALSE(icmp_.IsStarted());
87}
88
89TEST_F(IcmpTest, SocketNonBlockingFail) {
90 ScopedMockLog log;
91 EXPECT_CALL(log,
92 Log(logging::LOG_ERROR, _,
93 HasSubstr("Could not set socket to be non-blocking"))).Times(1);
94
95 EXPECT_CALL(*sockets_, Socket(_, _, _)).WillOnce(Return(kSocketFD));
96 EXPECT_CALL(*sockets_, SetNonBlocking(kSocketFD)).WillOnce(Return(-1));
97 EXPECT_CALL(*sockets_, Close(kSocketFD));
98 EXPECT_FALSE(icmp_.Start());
99 EXPECT_FALSE(icmp_.IsStarted());
100}
101
102TEST_F(IcmpTest, StartMultipleTimes) {
103 const int kFirstSocketFD = kSocketFD + 1;
104 StartIcmpWithFD(kFirstSocketFD);
105 EXPECT_CALL(*sockets_, Close(kFirstSocketFD));
106 StartIcmp();
107}
108
109MATCHER_P(IsIcmpHeader, header, "") {
110 return memcmp(arg, &header, sizeof(header)) == 0;
111}
112
113
114MATCHER_P(IsSocketAddress, address, "") {
115 const struct sockaddr_in *sock_addr =
116 reinterpret_cast<const struct sockaddr_in *>(arg);
117 return sock_addr->sin_family == address.family() &&
118 memcmp(&sock_addr->sin_addr.s_addr, address.GetConstData(),
119 address.GetLength()) == 0;
120}
121
122TEST_F(IcmpTest, TransmitEchoRequest) {
123 StartIcmp();
124 // Address isn't valid.
125 EXPECT_FALSE(icmp_.TransmitEchoRequest(IPAddress(IPAddress::kFamilyIPv4)));
126
127 // IPv6 adresses aren't implemented.
128 IPAddress ipv6_destination(IPAddress::kFamilyIPv6);
129 EXPECT_TRUE(ipv6_destination.SetAddressFromString(
130 "fe80::1aa9:5ff:abcd:1234"));
131 EXPECT_FALSE(icmp_.TransmitEchoRequest(ipv6_destination));
132
133 IPAddress ipv4_destination(IPAddress::kFamilyIPv4);
134 EXPECT_TRUE(ipv4_destination.SetAddressFromString(kIPAddress));
135
136 struct icmphdr icmp_header;
137 icmp_header.type = ICMP_ECHO;
138 icmp_header.un.echo.id = 1;
139 icmp_header.un.echo.sequence = 1;
140 EXPECT_CALL(*sockets_, SendTo(kSocketFD,
141 IsIcmpHeader(icmp_header),
142 sizeof(icmp_header),
143 0,
144 IsSocketAddress(ipv4_destination),
145 sizeof(sockaddr_in)))
146 .WillOnce(Return(-1))
147 .WillOnce(Return(0))
148 .WillOnce(Return(sizeof(icmp_header) - 1))
149 .WillOnce(Return(sizeof(icmp_header)));
150 {
151 InSequence seq;
152 ScopedMockLog log;
153 EXPECT_CALL(log,
154 Log(logging::LOG_ERROR, _,
155 HasSubstr("Socket sendto failed"))).Times(1);
156 EXPECT_CALL(log,
157 Log(logging::LOG_ERROR, _,
158 HasSubstr("less than the expected result"))).Times(2);
159
160 EXPECT_FALSE(icmp_.TransmitEchoRequest(ipv4_destination));
161 EXPECT_FALSE(icmp_.TransmitEchoRequest(ipv4_destination));
162 EXPECT_FALSE(icmp_.TransmitEchoRequest(ipv4_destination));
163 EXPECT_TRUE(icmp_.TransmitEchoRequest(ipv4_destination));
164 }
165}
166
167} // namespace shill