Paul Stewart | 960d469 | 2013-12-09 07:41:59 -0800 | [diff] [blame] | 1 | // 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 | |
| 16 | using testing::_; |
| 17 | using testing::HasSubstr; |
| 18 | using testing::InSequence; |
| 19 | using testing::Return; |
| 20 | using testing::StrictMock; |
| 21 | using testing::Test; |
| 22 | |
| 23 | namespace shill { |
| 24 | |
| 25 | class 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 | |
| 68 | const int IcmpTest::kSocketFD = 456; |
| 69 | const char IcmpTest::kIPAddress[] = "10.0.1.1"; |
| 70 | |
| 71 | |
| 72 | TEST_F(IcmpTest, Constructor) { |
| 73 | EXPECT_EQ(-1, GetSocket()); |
| 74 | EXPECT_FALSE(icmp_.IsStarted()); |
| 75 | } |
| 76 | |
| 77 | TEST_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 | |
| 89 | TEST_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 | |
| 102 | TEST_F(IcmpTest, StartMultipleTimes) { |
| 103 | const int kFirstSocketFD = kSocketFD + 1; |
| 104 | StartIcmpWithFD(kFirstSocketFD); |
| 105 | EXPECT_CALL(*sockets_, Close(kFirstSocketFD)); |
| 106 | StartIcmp(); |
| 107 | } |
| 108 | |
| 109 | MATCHER_P(IsIcmpHeader, header, "") { |
| 110 | return memcmp(arg, &header, sizeof(header)) == 0; |
| 111 | } |
| 112 | |
| 113 | |
| 114 | MATCHER_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 | |
| 122 | TEST_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 |