test: Mock out MAC address access

This makes it possible for our ethernet interfaces to have MAC Addresses
detected and configured for testing without the system providing a real
NIC.

Change-Id: Ie985efec0a5e393b0b76f3d02bd3015fef6349e4
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/test/Makefile.am b/test/Makefile.am
index 388f679..842f8ca 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -37,7 +37,7 @@
 
 test_CPPFLAGS = ${generic_cpp_flags}
 test_CXXFLAGS = ${generic_cxx_flags}
-test_LDFLAGS  = ${generic_ld_flags}
+test_LDFLAGS  = ${generic_ld_flags} -ldl
 
 test_dns_updater_CPPFLAGS = ${generic_cpp_flags}
 test_dns_updater_CXXFLAGS = ${generic_cxx_flags}
diff --git a/test/mock_syscall.cpp b/test/mock_syscall.cpp
index d6338c5..3f96e1d 100644
--- a/test/mock_syscall.cpp
+++ b/test/mock_syscall.cpp
@@ -1,10 +1,14 @@
 #include <arpa/inet.h>
+#include <dlfcn.h>
 #include <ifaddrs.h>
+#include <net/ethernet.h>
 #include <net/if.h>
 #include <netinet/in.h>
+#include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 
+#include <cstdarg>
 #include <cstring>
 #include <map>
 #include <stdexcept>
@@ -35,8 +39,9 @@
 
 std::map<std::string, int> mock_if_nametoindex;
 std::map<int, std::string> mock_if_indextoname;
+std::map<std::string, ether_addr> mock_macs;
 
-void mock_addIF(const std::string& name, int idx)
+void mock_addIF(const std::string& name, int idx, const ether_addr& mac)
 {
     if (idx == 0)
     {
@@ -45,6 +50,7 @@
 
     mock_if_nametoindex[name] = idx;
     mock_if_indextoname[idx] = name;
+    mock_macs[name] = mac;
 }
 
 void mock_addIP(const char* name, const char* addr, const char* mask,
@@ -78,6 +84,8 @@
     mock_ifaddrs = &mock_ifaddr_storage[0].ifaddr;
 }
 
+extern "C" {
+
 int getifaddrs(ifaddrs** ifap)
 {
     *ifap = mock_ifaddrs;
@@ -112,3 +120,30 @@
     }
     return std::strcpy(ifname, it->second.c_str());
 }
+
+int ioctl(int fd, unsigned long int request, ...)
+{
+    va_list vl;
+    va_start(vl, request);
+    void* data = va_arg(vl, void*);
+    va_end(vl);
+
+    if (request == SIOCGIFHWADDR)
+    {
+        auto req = reinterpret_cast<ifreq*>(data);
+        auto it = mock_macs.find(req->ifr_name);
+        if (it == mock_macs.end())
+        {
+            errno = ENXIO;
+            return -1;
+        }
+        std::memcpy(req->ifr_hwaddr.sa_data, &it->second, sizeof(it->second));
+        return 0;
+    }
+
+    static auto real_ioctl =
+        reinterpret_cast<decltype(&ioctl)>(dlsym(RTLD_NEXT, "ioctl"));
+    return real_ioctl(fd, request, data);
+}
+
+} // extern "C"
diff --git a/test/mock_syscall.hpp b/test/mock_syscall.hpp
index f14bd96..4d934b3 100644
--- a/test/mock_syscall.hpp
+++ b/test/mock_syscall.hpp
@@ -1,4 +1,6 @@
 #pragma once
+#include <net/ethernet.h>
+
 #include <string>
 
 /** @brief Adds the given interface and addr info
@@ -12,9 +14,11 @@
 void mock_addIP(const char* name, const char* addr, const char* mask,
                 unsigned int flags);
 
-/** @brief Adds an address string to index mapping
+/** @brief Adds an address string to index mapping and MAC mapping
  *
  *  @param[in] name - Interface name
  *  @param[in] idx  - Interface index
+ *  @param[in] mac  - Interface MAC address
  */
-void mock_addIF(const std::string& name, int idx);
+void mock_addIF(const std::string& name, int idx,
+                const ether_addr& mac = ether_addr{});
diff --git a/test/test_ethernet_interface.cpp b/test/test_ethernet_interface.cpp
index a075423..c25bba9 100644
--- a/test/test_ethernet_interface.cpp
+++ b/test/test_ethernet_interface.cpp
@@ -2,6 +2,7 @@
 #include "ipaddress.hpp"
 #include "mock_network_manager.hpp"
 #include "mock_syscall.hpp"
+#include "util.hpp"
 
 #include <arpa/inet.h>
 #include <net/if.h>
@@ -50,10 +51,12 @@
         }
     }
 
+    static constexpr ether_addr mac{0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+
     static EthernetInterface makeInterface(sdbusplus::bus::bus& bus,
                                            MockManager& manager)
     {
-        mock_addIF("test0", 1);
+        mock_addIF("test0", 1, mac);
         return {bus, "/xyz/openbmc_test/network/test0", false, manager};
     }
 
@@ -125,6 +128,7 @@
 TEST_F(TestEthernetInterface, NoIPaddress)
 {
     EXPECT_EQ(countIPObjects(), 0);
+    EXPECT_EQ(mac_address::toString(mac), interface.mACAddress());
 }
 
 TEST_F(TestEthernetInterface, AddIPAddress)