blob: 53eb3eb8225d65a70a88780a0bb3a7676fd0586a [file] [log] [blame]
Paul Stewartac802ac2013-04-02 15:45:24 -07001// 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/eap_listener.h"
6
7#include <linux/if_ether.h>
8#include <linux/if_packet.h>
9#include <netinet/in.h>
10#include <string.h>
11
12#include <base/bind.h>
13#include <gtest/gtest.h>
14
15#include "shill/byte_string.h"
16#include "shill/eap_protocol.h"
17#include "shill/mock_event_dispatcher.h"
18#include "shill/mock_log.h"
19#include "shill/mock_sockets.h"
20
21using testing::_;
22using testing::HasSubstr;
23using testing::Invoke;
24using testing::Return;
25using testing::StrictMock;
26
27namespace shill {
28
29class EapListenerTest : public testing::Test {
30 public:
31 EapListenerTest() : listener_(&dispatcher_, kInterfaceIndex) {}
32 virtual ~EapListenerTest() {}
33
34 virtual void SetUp() {
35 sockets_ = new StrictMock<MockSockets>();
36 // Passes ownership.
37 listener_.sockets_.reset(sockets_);
38 listener_.set_request_received_callback(
39 base::Bind(&EapListenerTest::ReceiveCallback, base::Unretained(this)));
40 }
41
42 virtual void TearDown() {
43 if (GetSocket() == kSocketFD) {
44 EXPECT_CALL(*sockets_, Close(kSocketFD));
45 listener_.Stop();
46 }
47 }
48
49 ssize_t SimulateRecvFrom(int sockfd, void *buf, size_t len, int flags,
50 struct sockaddr *src_addr, socklen_t *addrlen);
51
52 MOCK_METHOD0(ReceiveCallback, void());
53
54 protected:
55 static const int kInterfaceIndex;
56 static const int kSocketFD;
57 static const uint8 kEapPacketPayload[];
58
59 bool CreateSocket() { return listener_.CreateSocket(); }
60 int GetInterfaceIndex() { return listener_.interface_index_; }
61 size_t GetMaxEapPacketLength() { return EapListener::kMaxEapPacketLength; }
62 int GetSocket() { return listener_.socket_; }
63 void StartListener() { StartListenerWithFD(kSocketFD); }
64 void ReceiveRequest() { listener_.ReceiveRequest(kSocketFD); }
65 void StartListenerWithFD(int fd);
66
67 MockEventDispatcher dispatcher_;
68 EapListener listener_;
69
70 // Owned by EapListener, and tracked here only for mocks.
71 MockSockets *sockets_;
72
73 // Tests can assign this in order to set the data isreturned in our
74 // mock implementation of Sockets::RecvFrom().
75 ByteString recvfrom_reply_data_;
76};
77
78// static
79const int EapListenerTest::kInterfaceIndex = 123;
80const int EapListenerTest::kSocketFD = 456;
81const uint8 EapListenerTest::kEapPacketPayload[] = {
82 eap_protocol::kIeee8021xEapolVersion2,
83 eap_protocol::kIIeee8021xTypeEapPacket,
84 0x00, 0x00, // Payload length (should be 5, but unparsed by EapListener).
85 eap_protocol::kEapCodeRequest,
86 0x00, // Identifier (unparsed).
87 0x00, 0x00, // Packet length (should be 5, but unparsed by EapListener).
88 0x01 // Request type: Identity (not parsed by EapListener).
89};
90
91ssize_t EapListenerTest::SimulateRecvFrom(int sockfd, void *buf, size_t len,
92 int flags, struct sockaddr *src_addr,
93 socklen_t *addrlen) {
94 // Mimic behavior of the real recvfrom -- copy no more than requested.
95 int copy_length = std::min(recvfrom_reply_data_.GetLength(), len);
96 memcpy(buf, recvfrom_reply_data_.GetConstData(), copy_length);
97 return copy_length;
98}
99
100MATCHER_P(IsEapLinkAddress, interface_index, "") {
101 const struct sockaddr_ll *socket_address =
102 reinterpret_cast<const struct sockaddr_ll *>(arg);
103 return socket_address->sll_family == AF_PACKET &&
104 socket_address->sll_protocol == htons(ETH_P_PAE) &&
105 socket_address->sll_ifindex == interface_index;
106}
107
108void EapListenerTest::StartListenerWithFD(int fd) {
109 EXPECT_CALL(*sockets_, Socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)))
110 .WillOnce(Return(fd));
111 EXPECT_CALL(*sockets_, SetNonBlocking(fd)).WillOnce(Return(0));
112 EXPECT_CALL(*sockets_,
113 Bind(fd, IsEapLinkAddress(kInterfaceIndex), sizeof(sockaddr_ll)))
114 .WillOnce(Return(0));
115 EXPECT_CALL(dispatcher_, CreateReadyHandler(fd, IOHandler::kModeInput, _));
116 EXPECT_TRUE(listener_.Start());
117 EXPECT_EQ(fd, listener_.socket_);
118}
119
120TEST_F(EapListenerTest, Constructor) {
121 EXPECT_EQ(kInterfaceIndex, GetInterfaceIndex());
122 EXPECT_EQ(8, GetMaxEapPacketLength());
123 EXPECT_EQ(-1, GetSocket());
124}
125
126TEST_F(EapListenerTest, SocketOpenFail) {
127 ScopedMockLog log;
128 EXPECT_CALL(log,
129 Log(logging::LOG_ERROR, _,
130 HasSubstr("Could not create EAP listener socket"))).Times(1);
131
132 EXPECT_CALL(*sockets_, Socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)))
133 .WillOnce(Return(-1));
134 EXPECT_FALSE(CreateSocket());
135}
136
137TEST_F(EapListenerTest, SocketNonBlockingFail) {
138 ScopedMockLog log;
139 EXPECT_CALL(log,
140 Log(logging::LOG_ERROR, _,
141 HasSubstr("Could not set socket to be non-blocking"))).Times(1);
142
143 EXPECT_CALL(*sockets_, Socket(_, _, _)).WillOnce(Return(kSocketFD));
144 EXPECT_CALL(*sockets_, SetNonBlocking(kSocketFD)).WillOnce(Return(-1));
145 EXPECT_FALSE(CreateSocket());
146}
147
148TEST_F(EapListenerTest, SocketBindFail) {
149 ScopedMockLog log;
150 EXPECT_CALL(log,
151 Log(logging::LOG_ERROR, _,
152 HasSubstr("Could not bind socket to interface"))).Times(1);
153
154 EXPECT_CALL(*sockets_, Socket(_, _, _)).WillOnce(Return(kSocketFD));
155 EXPECT_CALL(*sockets_, SetNonBlocking(kSocketFD)).WillOnce(Return(0));
156 EXPECT_CALL(*sockets_, Bind(kSocketFD, _, _)).WillOnce(Return(-1));
157 EXPECT_FALSE(CreateSocket());
158}
159
160TEST_F(EapListenerTest, StartSuccess) {
161 StartListener();
162}
163
164TEST_F(EapListenerTest, StartMultipleTimes) {
165 const int kFirstSocketFD = kSocketFD + 1;
166 StartListenerWithFD(kFirstSocketFD);
167 EXPECT_CALL(*sockets_, Close(kFirstSocketFD));
168 StartListener();
169}
170
171TEST_F(EapListenerTest, Stop) {
172 StartListener();
173 EXPECT_EQ(kSocketFD, GetSocket());
174 EXPECT_CALL(*sockets_, Close(kSocketFD));
175 listener_.Stop();
176 EXPECT_EQ(-1, GetSocket());
177}
178
179
180TEST_F(EapListenerTest, ReceiveFail) {
181 StartListener();
182 EXPECT_CALL(*sockets_,
183 RecvFrom(kSocketFD, _, GetMaxEapPacketLength(), 0, _, _))
184 .WillOnce(Return(-1));
185 EXPECT_CALL(*this, ReceiveCallback()).Times(0);
186 EXPECT_CALL(*sockets_, Close(kSocketFD));
187
188 ScopedMockLog log;
189 // RecvFrom returns an error.
190 EXPECT_CALL(log,
191 Log(logging::LOG_ERROR, _,
192 HasSubstr("Socket recvfrom failed"))).Times(1);
193 ReceiveRequest();
194}
195
196TEST_F(EapListenerTest, ReceiveEmpty) {
197 StartListener();
198 EXPECT_CALL(*sockets_,
199 RecvFrom(kSocketFD, _, GetMaxEapPacketLength(), 0, _, _))
200 .WillOnce(Return(0));
201 EXPECT_CALL(*this, ReceiveCallback()).Times(0);
202 ReceiveRequest();
203}
204
205TEST_F(EapListenerTest, ReceiveShort) {
206 StartListener();
207 recvfrom_reply_data_ = ByteString(kEapPacketPayload,
208 GetMaxEapPacketLength() - 1);
209 EXPECT_CALL(*sockets_,
210 RecvFrom(kSocketFD, _, GetMaxEapPacketLength(), 0, _, _))
211 .WillOnce(Invoke(this, &EapListenerTest::SimulateRecvFrom));
212 EXPECT_CALL(*this, ReceiveCallback()).Times(0);
213 ScopedMockLog log;
214 EXPECT_CALL(log,
215 Log(logging::LOG_INFO, _,
216 HasSubstr("Short EAP packet received"))).Times(1);
217 ReceiveRequest();
218}
219
220TEST_F(EapListenerTest, ReceiveInvalid) {
221 StartListener();
222 // We're partially initializing this field, just making sure at least one
223 // part of it is incorrect.
224 uint8 bad_payload[sizeof(kEapPacketPayload)] = {
225 eap_protocol::kIeee8021xEapolVersion2 - 1
226 };
227 recvfrom_reply_data_ = ByteString(bad_payload, sizeof(bad_payload));
228 EXPECT_CALL(*sockets_,
229 RecvFrom(kSocketFD, _, GetMaxEapPacketLength(), 0, _, _))
230 .WillOnce(Invoke(this, &EapListenerTest::SimulateRecvFrom));
231 EXPECT_CALL(*this, ReceiveCallback()).Times(0);
232 ScopedMockLog log;
233 EXPECT_CALL(log,
234 Log(logging::LOG_INFO, _,
235 HasSubstr("Packet is not a valid EAP request"))).Times(1);
236 ReceiveRequest();
237}
238
239TEST_F(EapListenerTest, ReceiveSuccess) {
240 StartListener();
241 recvfrom_reply_data_ =
242 ByteString(kEapPacketPayload, sizeof(kEapPacketPayload));
243 EXPECT_CALL(*sockets_,
244 RecvFrom(kSocketFD, _, GetMaxEapPacketLength(), 0, _, _))
245 .WillOnce(Invoke(this, &EapListenerTest::SimulateRecvFrom));
246 EXPECT_CALL(*this, ReceiveCallback()).Times(1);
247 ReceiveRequest();
248}
249
250} // namespace shill