普通文本  |  265行  |  8.5 KB

//
// 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 <linux/if_ether.h>
#include <linux/if_packet.h>
#include <netinet/in.h>
#include <string.h>

#include <algorithm>

#include <base/bind.h>
#include <gtest/gtest.h>

#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<MockSockets>();
    // 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<const struct sockaddr_ll*>(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