blob: cde1b48757fcd0a03f9392c4eb18fafb2c81cf57 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shill/net/arp_client.h"
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <base/stl_util.h>
#include <gtest/gtest.h>
#include "shill/mock_log.h"
#include "shill/net/arp_packet.h"
#include "shill/net/ip_address.h"
#include "shill/net/mock_sockets.h"
using testing::_;
using testing::AnyNumber;
using testing::HasSubstr;
using testing::InSequence;
using testing::Invoke;
using testing::Return;
using testing::StrictMock;
using testing::Test;
namespace shill {
class ArpClientTest : public Test {
public:
ArpClientTest() : client_(kInterfaceIndex) {}
~ArpClientTest() override = default;
void SetUp() override {
sockets_ = new StrictMock<MockSockets>();
// Passes ownership.
client_.sockets_.reset(sockets_);
memset(&recvfrom_sender_, 0, sizeof(recvfrom_sender_));
}
void TearDown() override {
if (GetSocket() == kSocketFD) {
EXPECT_CALL(*sockets_, Close(kSocketFD));
client_.Stop();
}
}
ssize_t SimulateRecvFrom(int sockfd,
void* buf,
size_t len,
int flags,
struct sockaddr* src_addr,
socklen_t* addrlen);
protected:
static const int kInterfaceIndex;
static const int kSocketFD;
static const char kLocalIPAddress[];
static const uint8_t kLocalMacAddress[];
static const char kRemoteIPAddress[];
static const uint8_t kRemoteMacAddress[];
static const int kArpOpOffset;
bool CreateSocket() { return client_.CreateSocket(ARPOP_REPLY); }
int GetInterfaceIndex() { return client_.interface_index_; }
size_t GetMaxArpPacketLength() { return ArpClient::kMaxArpPacketLength; }
int GetSocket() { return client_.socket_; }
void SetupValidPacket(ArpPacket* packet);
void StartClient() { StartClientWithFD(kSocketFD); }
void StartClientWithFD(int fd);
// Owned by ArpClient, and tracked here only for mocks.
MockSockets* sockets_;
ArpClient client_;
ByteString recvfrom_reply_data_;
sockaddr_ll recvfrom_sender_;
};
const int ArpClientTest::kInterfaceIndex = 123;
const int ArpClientTest::kSocketFD = 456;
const char ArpClientTest::kLocalIPAddress[] = "10.0.1.1";
const uint8_t ArpClientTest::kLocalMacAddress[] = {0, 1, 2, 3, 4, 5};
const char ArpClientTest::kRemoteIPAddress[] = "10.0.1.2";
const uint8_t ArpClientTest::kRemoteMacAddress[] = {6, 7, 8, 9, 10, 11};
const int ArpClientTest::kArpOpOffset = 7;
MATCHER_P2(IsLinkAddress, interface_index, destination_mac, "") {
const struct sockaddr_ll* socket_address =
reinterpret_cast<const struct sockaddr_ll*>(arg);
ByteString socket_mac(
reinterpret_cast<const unsigned char*>(&socket_address->sll_addr),
destination_mac.GetLength());
return socket_address->sll_family == AF_PACKET &&
socket_address->sll_protocol == htons(ETHERTYPE_ARP) &&
socket_address->sll_ifindex == interface_index &&
destination_mac.Equals(socket_mac);
}
MATCHER_P(IsByteData, byte_data, "") {
return ByteString(reinterpret_cast<const unsigned char*>(arg),
byte_data.GetLength())
.Equals(byte_data);
}
void ArpClientTest::SetupValidPacket(ArpPacket* packet) {
IPAddress local_ip(IPAddress::kFamilyIPv4);
EXPECT_TRUE(local_ip.SetAddressFromString(kLocalIPAddress));
packet->set_local_ip_address(local_ip);
IPAddress remote_ip(IPAddress::kFamilyIPv4);
EXPECT_TRUE(remote_ip.SetAddressFromString(kRemoteIPAddress));
packet->set_remote_ip_address(remote_ip);
ByteString local_mac(kLocalMacAddress, base::size(kLocalMacAddress));
packet->set_local_mac_address(local_mac);
ByteString remote_mac(kRemoteMacAddress, base::size(kRemoteMacAddress));
packet->set_remote_mac_address(remote_mac);
}
ssize_t ArpClientTest::SimulateRecvFrom(int sockfd,
void* buf,
size_t len,
int flags,
struct sockaddr* src_addr,
socklen_t* addrlen) {
memcpy(buf, recvfrom_reply_data_.GetConstData(),
recvfrom_reply_data_.GetLength());
memcpy(src_addr, &recvfrom_sender_, sizeof(recvfrom_sender_));
return recvfrom_reply_data_.GetLength();
}
void ArpClientTest::StartClientWithFD(int fd) {
EXPECT_CALL(*sockets_, Socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC,
htons(ETHERTYPE_ARP)))
.WillOnce(Return(fd));
EXPECT_CALL(*sockets_, AttachFilter(fd, _)).WillOnce(Return(0));
EXPECT_CALL(*sockets_, SetNonBlocking(fd)).WillOnce(Return(0));
EXPECT_CALL(*sockets_, Bind(fd, IsLinkAddress(kInterfaceIndex, ByteString()),
sizeof(sockaddr_ll)))
.WillOnce(Return(0));
EXPECT_TRUE(CreateSocket());
EXPECT_EQ(fd, client_.socket_);
}
TEST_F(ArpClientTest, Constructor) {
EXPECT_EQ(kInterfaceIndex, GetInterfaceIndex());
EXPECT_EQ(-1, GetSocket());
}
TEST_F(ArpClientTest, SocketOpenFail) {
ScopedMockLog log;
EXPECT_CALL(log, Log(logging::LOGGING_ERROR, _,
HasSubstr("Could not create ARP socket")))
.Times(1);
EXPECT_CALL(*sockets_, Socket(PF_PACKET, _, htons(ETHERTYPE_ARP)))
.WillOnce(Return(-1));
EXPECT_FALSE(CreateSocket());
}
TEST_F(ArpClientTest, SocketFilterFail) {
ScopedMockLog log;
EXPECT_CALL(log, Log(logging::LOGGING_ERROR, _,
HasSubstr("Could not attach packet filter")))
.Times(1);
EXPECT_CALL(*sockets_, Socket(_, _, _)).WillOnce(Return(kSocketFD));
EXPECT_CALL(*sockets_, AttachFilter(kSocketFD, _)).WillOnce(Return(-1));
EXPECT_FALSE(CreateSocket());
}
TEST_F(ArpClientTest, SocketNonBlockingFail) {
ScopedMockLog log;
EXPECT_CALL(log, Log(logging::LOGGING_ERROR, _,
HasSubstr("Could not set socket to be non-blocking")))
.Times(1);
EXPECT_CALL(*sockets_, Socket(_, _, _)).WillOnce(Return(kSocketFD));
EXPECT_CALL(*sockets_, AttachFilter(kSocketFD, _)).WillOnce(Return(0));
EXPECT_CALL(*sockets_, SetNonBlocking(kSocketFD)).WillOnce(Return(-1));
EXPECT_FALSE(CreateSocket());
}
TEST_F(ArpClientTest, SocketBindFail) {
ScopedMockLog log;
EXPECT_CALL(log, Log(logging::LOGGING_ERROR, _,
HasSubstr("Could not bind socket to interface")))
.Times(1);
EXPECT_CALL(*sockets_, Socket(_, _, _)).WillOnce(Return(kSocketFD));
EXPECT_CALL(*sockets_, AttachFilter(kSocketFD, _)).WillOnce(Return(0));
EXPECT_CALL(*sockets_, SetNonBlocking(kSocketFD)).WillOnce(Return(0));
EXPECT_CALL(*sockets_, Bind(kSocketFD, _, _)).WillOnce(Return(-1));
EXPECT_FALSE(CreateSocket());
}
TEST_F(ArpClientTest, StartSuccess) {
StartClient();
}
TEST_F(ArpClientTest, StartMultipleTimes) {
const int kFirstSocketFD = kSocketFD + 1;
StartClientWithFD(kFirstSocketFD);
EXPECT_CALL(*sockets_, Close(kFirstSocketFD));
StartClient();
}
TEST_F(ArpClientTest, Receive) {
StartClient();
EXPECT_CALL(*sockets_,
RecvFrom(kSocketFD, _, GetMaxArpPacketLength(), 0, _, _))
.WillOnce(Return(-1))
.WillRepeatedly(Invoke(this, &ArpClientTest::SimulateRecvFrom));
ArpPacket reply;
ByteString sender;
ScopedMockLog log;
EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
{
InSequence seq;
// RecvFrom returns an error.
EXPECT_CALL(log, Log(logging::LOGGING_ERROR, _,
HasSubstr("Socket recvfrom failed")))
.Times(1);
EXPECT_FALSE(client_.ReceivePacket(&reply, &sender));
// RecvFrom returns an empty response which fails to parse.
EXPECT_CALL(log, Log(logging::LOGGING_ERROR, _,
HasSubstr("Failed to parse ARP packet")))
.Times(1);
EXPECT_FALSE(client_.ReceivePacket(&reply, &sender));
ArpPacket packet;
SetupValidPacket(&packet);
packet.FormatRequest(&recvfrom_reply_data_);
// Hack: Force this packet to be an ARP repsonse instead of an ARP request.
recvfrom_reply_data_.GetData()[kArpOpOffset] = ARPOP_REPLY;
static const uint8_t kSenderBytes[] = {0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
memcpy(&recvfrom_sender_.sll_addr, kSenderBytes, sizeof(kSenderBytes));
recvfrom_sender_.sll_halen = sizeof(kSenderBytes);
EXPECT_TRUE(client_.ReceivePacket(&reply, &sender));
EXPECT_TRUE(reply.local_ip_address().Equals(packet.local_ip_address()));
EXPECT_TRUE(reply.local_mac_address().Equals(packet.local_mac_address()));
EXPECT_TRUE(reply.remote_ip_address().Equals(packet.remote_ip_address()));
EXPECT_TRUE(reply.remote_mac_address().Equals(packet.remote_mac_address()));
EXPECT_TRUE(
sender.Equals(ByteString(kSenderBytes, base::size(kSenderBytes))));
}
}
TEST_F(ArpClientTest, Transmit) {
ArpPacket packet;
StartClient();
// Packet isn't valid.
EXPECT_FALSE(client_.TransmitRequest(packet));
SetupValidPacket(&packet);
const ByteString& remote_mac = packet.remote_mac_address();
ByteString packet_bytes;
ASSERT_TRUE(packet.FormatRequest(&packet_bytes));
EXPECT_CALL(
*sockets_,
SendTo(kSocketFD, IsByteData(packet_bytes), packet_bytes.GetLength(), 0,
IsLinkAddress(kInterfaceIndex, remote_mac), sizeof(sockaddr_ll)))
.WillOnce(Return(-1))
.WillOnce(Return(0))
.WillOnce(Return(packet_bytes.GetLength() - 1))
.WillOnce(Return(packet_bytes.GetLength()));
{
InSequence seq;
ScopedMockLog log;
EXPECT_CALL(
log, Log(logging::LOGGING_ERROR, _, HasSubstr("Socket sendto failed")))
.Times(1);
EXPECT_CALL(log, Log(logging::LOGGING_ERROR, _,
HasSubstr("different from expected result")))
.Times(2);
EXPECT_FALSE(client_.TransmitRequest(packet));
EXPECT_FALSE(client_.TransmitRequest(packet));
EXPECT_FALSE(client_.TransmitRequest(packet));
EXPECT_TRUE(client_.TransmitRequest(packet));
}
// If the destination MAC address is unset, it should be sent to the
// broadcast MAC address.
static const uint8_t kZeroBytes[] = {0, 0, 0, 0, 0, 0};
packet.set_remote_mac_address(ByteString(kZeroBytes, base::size(kZeroBytes)));
ASSERT_TRUE(packet.FormatRequest(&packet_bytes));
static const uint8_t kBroadcastBytes[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
ByteString broadcast_mac(kBroadcastBytes, base::size(kBroadcastBytes));
EXPECT_CALL(*sockets_, SendTo(kSocketFD, IsByteData(packet_bytes),
packet_bytes.GetLength(), 0,
IsLinkAddress(kInterfaceIndex, broadcast_mac),
sizeof(sockaddr_ll)))
.WillOnce(Return(packet_bytes.GetLength()));
EXPECT_TRUE(client_.TransmitRequest(packet));
}
} // namespace shill