Enable the network link carrier state to be reported.

This change allows networkd to keep track of, and report, the state of
the network carrier signal. When a NIC cable is pulled, or inserted, a
DBus client is able identify the condition.

Tested:
ip link set down dev eth0  # take eth0 down
Get bmc/EthernetInterfaces/eth0 from Redfish # LinkStatus = LinkDown
                                             # InterfaceEnabled = false
ip link set up dev eth0    # bring eth0 back
Get bmc/EthernetInterfaces/eth0 from Redfish # LinkStatus = Linkup
                                             # InterfaceEnabled = true
Pull eth0 cable
Get bmc/EthernetInterfaces/eth0 from Redfish # LinkStatus = LinkDown
                                             # InterfaceEnabled = true
Insert eth0 cable
Get bmc/EthernetInterfaces/eth0 from Redfish # LinkStatus = Linkup
                                             # InterfaceEnabled = true

Change-Id: I5530cf7882cfbfdba1436dd34b3219c735047c5e
Signed-off-by: Johnathan Mantey <johnathanx.mantey@intel.com>
diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp
index 2375482..f293ef5 100644
--- a/ethernet_interface.cpp
+++ b/ethernet_interface.cpp
@@ -37,6 +37,28 @@
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
 using Argument = xyz::openbmc_project::Common::InvalidArgument;
 
+struct EthernetIntfSocket
+{
+    EthernetIntfSocket(int domain, int type, int protocol)
+    {
+        if ((sock = socket(domain, type, protocol)) < 0)
+        {
+            log<level::ERR>("socket creation failed:",
+                            entry("ERROR=%s", strerror(errno)));
+        }
+    }
+
+    ~EthernetIntfSocket()
+    {
+        if (sock >= 0)
+        {
+            close(sock);
+        }
+    }
+
+    int sock{-1};
+};
+
 EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus,
                                      const std::string& objPath,
                                      bool dhcpEnabled, Manager& parent,
@@ -56,6 +78,7 @@
 
     EthernetInterfaceIntf::autoNeg(std::get<2>(ifInfo));
     EthernetInterfaceIntf::speed(std::get<0>(ifInfo));
+    EthernetInterfaceIntf::linkUp(std::get<3>(ifInfo));
 
     // Emit deferred signal.
     if (emitSignal)
@@ -227,43 +250,33 @@
 
 InterfaceInfo EthernetInterface::getInterfaceInfo() const
 {
-    int sock{-1};
     ifreq ifr{0};
     ethtool_cmd edata{0};
     LinkSpeed speed{0};
     Autoneg autoneg{0};
     DuplexMode duplex{0};
-    do
+    LinkUp linkState{false};
+    EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+    if (eifSocket.sock < 0)
     {
-        sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
-        if (sock < 0)
-        {
-            log<level::ERR>("socket creation  failed:",
-                            entry("ERROR=%s", strerror(errno)));
-            break;
-        }
+        return std::make_tuple(speed, duplex, autoneg, linkState);
+    }
 
-        strcpy(ifr.ifr_name, interfaceName().c_str());
-        ifr.ifr_data = reinterpret_cast<char*>(&edata);
+    std::strncpy(ifr.ifr_name, interfaceName().c_str(), IFNAMSIZ - 1);
+    ifr.ifr_data = reinterpret_cast<char*>(&edata);
 
-        edata.cmd = ETHTOOL_GSET;
-
-        if (ioctl(sock, SIOCETHTOOL, &ifr) < 0)
-        {
-            log<level::ERR>("ioctl failed for SIOCETHTOOL:",
-                            entry("ERROR=%s", strerror(errno)));
-            break;
-        }
+    edata.cmd = ETHTOOL_GSET;
+    if (ioctl(eifSocket.sock, SIOCETHTOOL, &ifr) >= 0)
+    {
         speed = edata.speed;
         duplex = edata.duplex;
         autoneg = edata.autoneg;
-    } while (0);
-
-    if (sock)
-    {
-        close(sock);
     }
-    return std::make_tuple(speed, duplex, autoneg);
+
+    linkState = linkUp();
+
+    return std::make_tuple(speed, duplex, autoneg, linkState);
 }
 
 /** @brief get the mac address of the interface.
@@ -273,17 +286,17 @@
 std::string
     EthernetInterface::getMACAddress(const std::string& interfaceName) const
 {
-    ifreq ifr{};
-    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
-    if (sock < 0)
+    std::string activeMACAddr = MacAddressIntf::mACAddress();
+    EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+    if (eifSocket.sock < 0)
     {
-        log<level::ERR>("socket creation  failed:",
-                        entry("ERROR=%s", strerror(errno)));
-        elog<InternalFailure>();
+        return activeMACAddr;
     }
 
-    std::strcpy(ifr.ifr_name, interfaceName.c_str());
-    if (ioctl(sock, SIOCGIFHWADDR, &ifr) != 0)
+    ifreq ifr{0};
+    std::strncpy(ifr.ifr_name, interfaceName.c_str(), IFNAMSIZ - 1);
+    if (ioctl(eifSocket.sock, SIOCGIFHWADDR, &ifr) != 0)
     {
         log<level::ERR>("ioctl failed for SIOCGIFHWADDR:",
                         entry("ERROR=%s", strerror(errno)));
@@ -446,6 +459,31 @@
     return value;
 }
 
+bool EthernetInterface::linkUp() const
+{
+    EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+    bool value = EthernetInterfaceIntf::linkUp();
+
+    if (eifSocket.sock < 0)
+    {
+        return value;
+    }
+
+    ifreq ifr{0};
+    std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1);
+    if (ioctl(eifSocket.sock, SIOCGIFFLAGS, &ifr) == 0)
+    {
+        value = static_cast<bool>(ifr.ifr_flags & IFF_RUNNING);
+    }
+    else
+    {
+        log<level::ERR>("ioctl failed for SIOCGIFFLAGS:",
+                        entry("ERROR=%s", strerror(errno)));
+    }
+
+    return value;
+}
+
 ServerList EthernetInterface::nameservers(ServerList value)
 {
     for (const auto& nameserverip : value)
diff --git a/ethernet_interface.hpp b/ethernet_interface.hpp
index 60c56e3..68668d6 100644
--- a/ethernet_interface.hpp
+++ b/ethernet_interface.hpp
@@ -50,9 +50,10 @@
 using LinkSpeed = uint16_t;
 using DuplexMode = uint8_t;
 using Autoneg = uint8_t;
+using LinkUp = bool;
 using VlanId = uint32_t;
 using InterfaceName = std::string;
-using InterfaceInfo = std::tuple<LinkSpeed, DuplexMode, Autoneg>;
+using InterfaceInfo = std::tuple<LinkSpeed, DuplexMode, Autoneg, LinkUp>;
 using AddressMap = std::map<std::string, std::shared_ptr<IPAddress>>;
 using NeighborMap = std::map<std::string, std::shared_ptr<Neighbor>>;
 using VlanInterfaceMap =
@@ -150,6 +151,9 @@
     /** Set value of DHCPEnabled */
     bool dHCPEnabled(bool value) override;
 
+    /** Retrieve Link State */
+    bool linkUp() const override;
+
     /** @brief sets the MAC address.
      *  @param[in] value - MAC address which needs to be set on the system.
      *  @returns macAddress of the interface or throws an error.
@@ -197,6 +201,7 @@
 
     using EthernetInterfaceIntf::dHCPEnabled;
     using EthernetInterfaceIntf::interfaceName;
+    using EthernetInterfaceIntf::linkUp;
     using MacAddressIntf::mACAddress;
 
     /** @brief Absolute path of the resolv conf file */