// // Copyright (C) 2013 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "shill/eap_listener.h" #include #include #include #include #include #include #include #include "shill/eap_protocol.h" #include "shill/mock_event_dispatcher.h" #include "shill/mock_log.h" #include "shill/net/byte_string.h" #include "shill/net/mock_sockets.h" using testing::_; using testing::HasSubstr; using testing::Invoke; using testing::Return; using testing::StrictMock; namespace shill { class EapListenerTest : public testing::Test { public: EapListenerTest() : listener_(&dispatcher_, kInterfaceIndex) {} virtual ~EapListenerTest() {} virtual void SetUp() { sockets_ = new StrictMock(); // Passes ownership. listener_.sockets_.reset(sockets_); listener_.set_request_received_callback( base::Bind(&EapListenerTest::ReceiveCallback, base::Unretained(this))); } virtual void TearDown() { if (GetSocket() == kSocketFD) { EXPECT_CALL(*sockets_, Close(kSocketFD)); listener_.Stop(); } } ssize_t SimulateRecvFrom(int sockfd, void* buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen); MOCK_METHOD0(ReceiveCallback, void()); protected: static const int kInterfaceIndex; static const int kSocketFD; static const uint8_t kEapPacketPayload[]; bool CreateSocket() { return listener_.CreateSocket(); } int GetInterfaceIndex() { return listener_.interface_index_; } size_t GetMaxEapPacketLength() { return EapListener::kMaxEapPacketLength; } int GetSocket() { return listener_.socket_; } void StartListener() { StartListenerWithFD(kSocketFD); } void ReceiveRequest() { listener_.ReceiveRequest(kSocketFD); } void StartListenerWithFD(int fd); MockEventDispatcher dispatcher_; EapListener listener_; // Owned by EapListener, and tracked here only for mocks. MockSockets* sockets_; // Tests can assign this in order to set the data isreturned in our // mock implementation of Sockets::RecvFrom(). ByteString recvfrom_reply_data_; }; // static const int EapListenerTest::kInterfaceIndex = 123; const int EapListenerTest::kSocketFD = 456; const uint8_t EapListenerTest::kEapPacketPayload[] = { eap_protocol::kIeee8021xEapolVersion2, eap_protocol::kIIeee8021xTypeEapPacket, 0x00, 0x00, // Payload length (should be 5, but unparsed by EapListener). eap_protocol::kEapCodeRequest, 0x00, // Identifier (unparsed). 0x00, 0x00, // Packet length (should be 5, but unparsed by EapListener). 0x01 // Request type: Identity (not parsed by EapListener). }; ssize_t EapListenerTest::SimulateRecvFrom(int sockfd, void* buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen) { // Mimic behavior of the real recvfrom -- copy no more than requested. int copy_length = std::min(recvfrom_reply_data_.GetLength(), len); memcpy(buf, recvfrom_reply_data_.GetConstData(), copy_length); return copy_length; } MATCHER_P(IsEapLinkAddress, interface_index, "") { const struct sockaddr_ll* socket_address = reinterpret_cast(arg); return socket_address->sll_family == AF_PACKET && socket_address->sll_protocol == htons(ETH_P_PAE) && socket_address->sll_ifindex == interface_index; } void EapListenerTest::StartListenerWithFD(int fd) { EXPECT_CALL(*sockets_, Socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE))) .WillOnce(Return(fd)); EXPECT_CALL(*sockets_, SetNonBlocking(fd)).WillOnce(Return(0)); EXPECT_CALL(*sockets_, Bind(fd, IsEapLinkAddress(kInterfaceIndex), sizeof(sockaddr_ll))) .WillOnce(Return(0)); EXPECT_CALL(dispatcher_, CreateReadyHandler(fd, IOHandler::kModeInput, _)); EXPECT_TRUE(listener_.Start()); EXPECT_EQ(fd, listener_.socket_); } TEST_F(EapListenerTest, Constructor) { EXPECT_EQ(kInterfaceIndex, GetInterfaceIndex()); EXPECT_EQ(8, GetMaxEapPacketLength()); EXPECT_EQ(-1, GetSocket()); } TEST_F(EapListenerTest, SocketOpenFail) { ScopedMockLog log; EXPECT_CALL(log, Log(logging::LOG_ERROR, _, HasSubstr("Could not create EAP listener socket"))).Times(1); EXPECT_CALL(*sockets_, Socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE))) .WillOnce(Return(-1)); EXPECT_FALSE(CreateSocket()); } TEST_F(EapListenerTest, SocketNonBlockingFail) { ScopedMockLog log; EXPECT_CALL(log, Log(logging::LOG_ERROR, _, HasSubstr("Could not set socket to be non-blocking"))).Times(1); EXPECT_CALL(*sockets_, Socket(_, _, _)).WillOnce(Return(kSocketFD)); EXPECT_CALL(*sockets_, SetNonBlocking(kSocketFD)).WillOnce(Return(-1)); EXPECT_FALSE(CreateSocket()); } TEST_F(EapListenerTest, SocketBindFail) { ScopedMockLog log; EXPECT_CALL(log, Log(logging::LOG_ERROR, _, HasSubstr("Could not bind socket to interface"))).Times(1); EXPECT_CALL(*sockets_, Socket(_, _, _)).WillOnce(Return(kSocketFD)); EXPECT_CALL(*sockets_, SetNonBlocking(kSocketFD)).WillOnce(Return(0)); EXPECT_CALL(*sockets_, Bind(kSocketFD, _, _)).WillOnce(Return(-1)); EXPECT_FALSE(CreateSocket()); } TEST_F(EapListenerTest, StartSuccess) { StartListener(); } TEST_F(EapListenerTest, StartMultipleTimes) { const int kFirstSocketFD = kSocketFD + 1; StartListenerWithFD(kFirstSocketFD); EXPECT_CALL(*sockets_, Close(kFirstSocketFD)); StartListener(); } TEST_F(EapListenerTest, Stop) { StartListener(); EXPECT_EQ(kSocketFD, GetSocket()); EXPECT_CALL(*sockets_, Close(kSocketFD)); listener_.Stop(); EXPECT_EQ(-1, GetSocket()); } TEST_F(EapListenerTest, ReceiveFail) { StartListener(); EXPECT_CALL(*sockets_, RecvFrom(kSocketFD, _, GetMaxEapPacketLength(), 0, _, _)) .WillOnce(Return(-1)); EXPECT_CALL(*this, ReceiveCallback()).Times(0); EXPECT_CALL(*sockets_, Close(kSocketFD)); ScopedMockLog log; // RecvFrom returns an error. EXPECT_CALL(log, Log(logging::LOG_ERROR, _, HasSubstr("Socket recvfrom failed"))).Times(1); ReceiveRequest(); } TEST_F(EapListenerTest, ReceiveEmpty) { StartListener(); EXPECT_CALL(*sockets_, RecvFrom(kSocketFD, _, GetMaxEapPacketLength(), 0, _, _)) .WillOnce(Return(0)); EXPECT_CALL(*this, ReceiveCallback()).Times(0); ReceiveRequest(); } TEST_F(EapListenerTest, ReceiveShort) { StartListener(); recvfrom_reply_data_ = ByteString(kEapPacketPayload, GetMaxEapPacketLength() - 1); EXPECT_CALL(*sockets_, RecvFrom(kSocketFD, _, GetMaxEapPacketLength(), 0, _, _)) .WillOnce(Invoke(this, &EapListenerTest::SimulateRecvFrom)); EXPECT_CALL(*this, ReceiveCallback()).Times(0); ScopedMockLog log; EXPECT_CALL(log, Log(logging::LOG_INFO, _, HasSubstr("Short EAP packet received"))).Times(1); ReceiveRequest(); } TEST_F(EapListenerTest, ReceiveInvalid) { StartListener(); // We're partially initializing this field, just making sure at least one // part of it is incorrect. uint8_t bad_payload[sizeof(kEapPacketPayload)] = { eap_protocol::kIeee8021xEapolVersion1 - 1 }; recvfrom_reply_data_ = ByteString(bad_payload, sizeof(bad_payload)); EXPECT_CALL(*sockets_, RecvFrom(kSocketFD, _, GetMaxEapPacketLength(), 0, _, _)) .WillOnce(Invoke(this, &EapListenerTest::SimulateRecvFrom)); EXPECT_CALL(*this, ReceiveCallback()).Times(0); ScopedMockLog log; EXPECT_CALL(log, Log(logging::LOG_INFO, _, HasSubstr("Packet is not a valid EAP request"))).Times(1); ReceiveRequest(); } TEST_F(EapListenerTest, ReceiveSuccess) { StartListener(); recvfrom_reply_data_ = ByteString(kEapPacketPayload, sizeof(kEapPacketPayload)); EXPECT_CALL(*sockets_, RecvFrom(kSocketFD, _, GetMaxEapPacketLength(), 0, _, _)) .WillOnce(Invoke(this, &EapListenerTest::SimulateRecvFrom)); EXPECT_CALL(*this, ReceiveCallback()).Times(1); ReceiveRequest(); } } // namespace shill