shill: LinkMonitor: Add ArpPacket code

Add ArpPacket class to parse and compose ARP packets.

BUG=chromium-os:32600
TEST=New unit tests

Change-Id: Icac65cc16e059f07109fc695ba9046cafa0b042c
Reviewed-on: https://gerrit.chromium.org/gerrit/28096
Commit-Ready: Paul Stewart <pstew@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/arp_packet_unittest.cc b/arp_packet_unittest.cc
new file mode 100644
index 0000000..4cdaa76
--- /dev/null
+++ b/arp_packet_unittest.cc
@@ -0,0 +1,267 @@
+// Copyright (c) 2012 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/arp_packet.h"
+
+#include <gtest/gtest.h>
+
+#include "shill/mock_log.h"
+
+using testing::_;
+using testing::HasSubstr;
+using testing::Test;
+
+namespace shill {
+
+namespace {
+const uint8 kArpRequestV4[] =
+    { 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01 };
+const uint8 kArpRequestV6[] =
+    { 0x00, 0x01, 0x86, 0xdd, 0x06, 0x10, 0x00, 0x01 };
+const uint8 kArpReplyV4[] =
+    { 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x02 };
+const uint8 kArpReplyV6[] =
+    { 0x00, 0x01, 0x86, 0xdd, 0x06, 0x10, 0x00, 0x02 };
+const char kIPv4Address0[] = "192.168.0.1";
+const char kIPv4Address1[] = "10.0.12.13";
+const char kIPv6Address0[] = "fe80::1aa9:5ff:7ebf:14c5";
+const char kIPv6Address1[] = "1980:0:0:1000:1b02:1aa9:5ff:7ebf";
+const uint8 kMACAddress0[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
+const uint8 kMACAddress1[] = { 0x88, 0x87, 0x86, 0x85, 0x84, 0x83 };
+const uint8 kMACBroadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+const uint8 kInsertedByte[] = { 0x00 };
+}  // namespace {}
+
+class ArpPacketTest : public Test {
+ public:
+  ArpPacketTest()
+      : ipv4_address0_(IPAddress::kFamilyIPv4),
+        ipv4_address1_(IPAddress::kFamilyIPv4),
+        ipv6_address0_(IPAddress::kFamilyIPv6),
+        ipv6_address1_(IPAddress::kFamilyIPv6),
+        mac_address0_(kMACAddress0, arraysize(kMACAddress0)),
+        mac_address1_(kMACAddress1, arraysize(kMACAddress1)),
+        inserted_byte_(kInsertedByte, arraysize(kInsertedByte)) {}
+  virtual ~ArpPacketTest() {}
+
+  virtual void SetUp() {
+    EXPECT_TRUE(ipv4_address0_.SetAddressFromString(kIPv4Address0));
+    EXPECT_TRUE(ipv4_address1_.SetAddressFromString(kIPv4Address1));
+    EXPECT_TRUE(ipv6_address0_.SetAddressFromString(kIPv6Address0));
+    EXPECT_TRUE(ipv6_address1_.SetAddressFromString(kIPv6Address1));
+  }
+
+ protected:
+  IPAddress ipv4_address0_;
+  IPAddress ipv4_address1_;
+  IPAddress ipv6_address0_;
+  IPAddress ipv6_address1_;
+  ByteString mac_address0_;
+  ByteString mac_address1_;
+  ByteString inserted_byte_;
+  ArpPacket packet_;
+};
+
+TEST_F(ArpPacketTest, Constructor) {
+  EXPECT_FALSE(packet_.local_ip_address().IsValid());
+  EXPECT_FALSE(packet_.remote_ip_address().IsValid());
+  EXPECT_TRUE(packet_.local_mac_address().IsEmpty());
+  EXPECT_TRUE(packet_.remote_mac_address().IsEmpty());
+}
+
+TEST_F(ArpPacketTest, GettersAndSetters) {
+  packet_.set_local_ip_address(ipv4_address0_);
+  packet_.set_remote_ip_address(ipv6_address1_);
+  packet_.set_local_mac_address(mac_address0_);
+  packet_.set_remote_mac_address(mac_address1_);
+  EXPECT_TRUE(ipv4_address0_.Equals(packet_.local_ip_address()));
+  EXPECT_TRUE(ipv6_address1_.Equals(packet_.remote_ip_address()));
+  EXPECT_TRUE(mac_address0_.Equals(packet_.local_mac_address()));
+  EXPECT_TRUE(mac_address1_.Equals(packet_.remote_mac_address()));
+}
+
+TEST_F(ArpPacketTest, ParseReplyTinyPacket) {
+  ScopedMockLog log;
+  EXPECT_CALL(log,
+      Log(logging::LOG_ERROR, _,
+          HasSubstr("too short to contain ARP header."))).Times(1);
+
+  ByteString arp_bytes(kArpReplyV4, arraysize(kArpReplyV4));
+  arp_bytes.Resize(arp_bytes.GetLength() - 1);
+  EXPECT_FALSE(packet_.ParseReply(arp_bytes));
+}
+
+TEST_F(ArpPacketTest, ParseReplyBadHRDType) {
+  ScopedMockLog log;
+  EXPECT_CALL(log,
+      Log(logging::LOG_ERROR, _,
+          HasSubstr("Packet is of unknown ARPHRD type 257"))).Times(1);
+
+  ByteString arp_bytes(kArpReplyV4, arraysize(kArpReplyV4));
+  arp_bytes.GetData()[0] = 0x1;
+  EXPECT_FALSE(packet_.ParseReply(arp_bytes));
+}
+
+TEST_F(ArpPacketTest, ParseReplyBadProtocol) {
+  ScopedMockLog log;
+  EXPECT_CALL(log,
+      Log(logging::LOG_ERROR, _,
+          HasSubstr("Packet has unknown protocol 2049"))).Times(1);
+
+  ByteString arp_bytes(kArpReplyV4, arraysize(kArpReplyV4));
+  arp_bytes.GetData()[3] = 0x1;
+  EXPECT_FALSE(packet_.ParseReply(arp_bytes));
+}
+
+TEST_F(ArpPacketTest, ParseReplyBadHardwareLength) {
+  ScopedMockLog log;
+  EXPECT_CALL(log,
+      Log(logging::LOG_ERROR, _,
+          HasSubstr("Packet has unexpected hardware address length"))).Times(1);
+
+  ByteString arp_bytes(kArpReplyV4, arraysize(kArpReplyV4));
+  arp_bytes.GetData()[4] = 0x1;
+  EXPECT_FALSE(packet_.ParseReply(arp_bytes));
+}
+
+TEST_F(ArpPacketTest, ParseReplyBadProtocolLength) {
+  ScopedMockLog log;
+  EXPECT_CALL(log,
+      Log(logging::LOG_ERROR, _,
+          HasSubstr("Packet has unexpected protocol address length"))).Times(1);
+
+  ByteString arp_bytes(kArpReplyV4, arraysize(kArpReplyV4));
+  arp_bytes.GetData()[5] = 0x1;
+  EXPECT_FALSE(packet_.ParseReply(arp_bytes));
+}
+
+TEST_F(ArpPacketTest, ParseReplyBadOpCode) {
+  ScopedMockLog log;
+  EXPECT_CALL(log,
+      Log(logging::LOG_ERROR, _,
+          HasSubstr("Packet is not an ARP reply but of type 258"))).Times(1);
+
+  ByteString arp_bytes(kArpReplyV4, arraysize(kArpReplyV4));
+  arp_bytes.GetData()[6] = 0x1;
+  EXPECT_FALSE(packet_.ParseReply(arp_bytes));
+}
+
+TEST_F(ArpPacketTest, ParseReplyShortPacket) {
+  ScopedMockLog log;
+  EXPECT_CALL(log,
+      Log(logging::LOG_ERROR, _,
+          HasSubstr("is too small to contain entire ARP payload"))).Times(1);
+
+  ByteString arp_bytes(kArpReplyV6, arraysize(kArpReplyV6));
+  arp_bytes.Append(mac_address1_);
+  arp_bytes.Append(ipv6_address0_.address());
+  arp_bytes.Append(mac_address0_);
+  arp_bytes.Append(ipv6_address1_.address());
+  arp_bytes.Resize(arp_bytes.GetLength() - 1);
+  EXPECT_FALSE(packet_.ParseReply(arp_bytes));
+}
+
+TEST_F(ArpPacketTest, ParseReplyIPv4) {
+  ByteString arp_bytes(kArpReplyV4, arraysize(kArpReplyV4));
+  arp_bytes.Append(mac_address0_);
+  arp_bytes.Append(ipv4_address0_.address());
+  arp_bytes.Append(mac_address1_);
+  arp_bytes.Append(ipv4_address1_.address());
+  EXPECT_TRUE(packet_.ParseReply(arp_bytes));
+  EXPECT_TRUE(ipv4_address0_.Equals(packet_.local_ip_address()));
+  EXPECT_TRUE(ipv4_address1_.Equals(packet_.remote_ip_address()));
+  EXPECT_TRUE(mac_address0_.Equals(packet_.local_mac_address()));
+  EXPECT_TRUE(mac_address1_.Equals(packet_.remote_mac_address()));
+}
+
+TEST_F(ArpPacketTest, ParseReplyIPv6) {
+  ByteString arp_bytes(kArpReplyV6, arraysize(kArpReplyV6));
+  arp_bytes.Append(mac_address1_);
+  arp_bytes.Append(ipv6_address0_.address());
+  arp_bytes.Append(mac_address0_);
+  arp_bytes.Append(ipv6_address1_.address());
+  EXPECT_TRUE(packet_.ParseReply(arp_bytes));
+  EXPECT_TRUE(ipv6_address0_.Equals(packet_.local_ip_address()));
+  EXPECT_TRUE(ipv6_address1_.Equals(packet_.remote_ip_address()));
+  EXPECT_TRUE(mac_address1_.Equals(packet_.local_mac_address()));
+  EXPECT_TRUE(mac_address0_.Equals(packet_.remote_mac_address()));
+}
+
+TEST_F(ArpPacketTest, FormatRequestInvalidAddress) {
+  ScopedMockLog log;
+  EXPECT_CALL(log,
+      Log(logging::LOG_ERROR, _,
+          HasSubstr("Local or remote IP address is not valid"))).Times(3);
+
+  ByteString arp_bytes;
+  EXPECT_FALSE(packet_.FormatRequest(&arp_bytes));
+  packet_.set_local_ip_address(ipv4_address0_);
+  EXPECT_FALSE(packet_.FormatRequest(&arp_bytes));
+  packet_.set_local_ip_address(IPAddress(IPAddress::kFamilyUnknown));
+  packet_.set_remote_ip_address(ipv4_address0_);
+  EXPECT_FALSE(packet_.FormatRequest(&arp_bytes));
+}
+
+TEST_F(ArpPacketTest, FormatRequestMismatchedAddresses) {
+  ScopedMockLog log;
+  EXPECT_CALL(log,
+      Log(logging::LOG_ERROR, _,
+          HasSubstr("IP address families do not match"))).Times(1);
+
+  ByteString arp_bytes;
+  packet_.set_local_ip_address(ipv4_address0_);
+  packet_.set_remote_ip_address(ipv6_address1_);
+  EXPECT_FALSE(packet_.FormatRequest(&arp_bytes));
+}
+
+TEST_F(ArpPacketTest, FormatRequestBadMACAddressLength) {
+  ScopedMockLog log;
+  EXPECT_CALL(log,
+      Log(logging::LOG_ERROR, _,
+          HasSubstr("MAC address length is incorrect"))).Times(3);
+
+  ByteString arp_bytes;
+  packet_.set_local_ip_address(ipv4_address0_);
+  packet_.set_remote_ip_address(ipv4_address1_);
+  EXPECT_FALSE(packet_.FormatRequest(&arp_bytes));
+  packet_.set_local_mac_address(mac_address0_);
+  EXPECT_FALSE(packet_.FormatRequest(&arp_bytes));
+  packet_.set_local_mac_address(ByteString());
+  packet_.set_remote_mac_address(mac_address0_);
+  EXPECT_FALSE(packet_.FormatRequest(&arp_bytes));
+}
+
+TEST_F(ArpPacketTest, FormatRequestIPv4) {
+  ByteString arp_bytes;
+  packet_.set_local_ip_address(ipv4_address0_);
+  packet_.set_remote_ip_address(ipv4_address1_);
+  packet_.set_local_mac_address(mac_address0_);
+  packet_.set_remote_mac_address(mac_address1_);
+  EXPECT_TRUE(packet_.FormatRequest(&arp_bytes));
+
+  ByteString expected_bytes(kArpRequestV4, arraysize(kArpRequestV4));
+  expected_bytes.Append(mac_address0_);
+  expected_bytes.Append(ipv4_address0_.address());
+  expected_bytes.Append(mac_address1_);
+  expected_bytes.Append(ipv4_address1_.address());
+  EXPECT_TRUE(expected_bytes.Equals(arp_bytes));
+}
+
+TEST_F(ArpPacketTest, FormatRequestIPv6) {
+  ByteString arp_bytes;
+  packet_.set_local_ip_address(ipv6_address0_);
+  packet_.set_remote_ip_address(ipv6_address1_);
+  packet_.set_local_mac_address(mac_address1_);
+  packet_.set_remote_mac_address(mac_address0_);
+  EXPECT_TRUE(packet_.FormatRequest(&arp_bytes));
+
+  ByteString expected_bytes(kArpRequestV6, arraysize(kArpRequestV6));
+  expected_bytes.Append(mac_address1_);
+  expected_bytes.Append(ipv6_address0_.address());
+  expected_bytes.Append(mac_address0_);
+  expected_bytes.Append(ipv6_address1_.address());
+  EXPECT_TRUE(expected_bytes.Equals(arp_bytes));
+}
+
+}  // namespace shill