Implement Set function for MAC address

Change-Id: I16992dda259246a66512792f06cbbb874e56a15d
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp
index c24a3e8..39c12cf 100644
--- a/ethernet_interface.cpp
+++ b/ethernet_interface.cpp
@@ -33,10 +33,6 @@
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
 
-constexpr auto MAC_ADDRESS_FORMAT = "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx";
-constexpr size_t SIZE_MAC = 18;
-constexpr size_t SIZE_BUFF = 512;
-
 EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus,
                                      const std::string& objPath,
                                      bool dhcpEnabled,
@@ -51,7 +47,7 @@
     std::replace(intfName.begin(), intfName.end(), '_', '.');
     interfaceName(intfName);
     EthernetInterfaceIntf::dHCPEnabled(dhcpEnabled);
-    mACAddress(getMACAddress(intfName));
+    MacAddressIntf::mACAddress(getMACAddress(intfName));
 
     // Emit deferred signal.
     if (emitSignal)
@@ -116,7 +112,7 @@
     if (dHCPEnabled())
     {
         log<level::INFO>("DHCP enabled on the interface"),
-                        entry("INTERFACE=%s",interfaceName());
+                        entry("INTERFACE=%s",interfaceName().c_str());
         return;
     }
 
@@ -200,7 +196,7 @@
         const std::string& interfaceName) const
 {
     struct ifreq ifr{};
-    char macAddress[SIZE_MAC] {};
+    char macAddress[mac_address::size] {};
 
     int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
     if (sock < 0)
@@ -218,7 +214,7 @@
         return macAddress;
     }
 
-    snprintf(macAddress, SIZE_MAC, MAC_ADDRESS_FORMAT,
+    snprintf(macAddress, mac_address::size, mac_address::format,
             ifr.ifr_hwaddr.sa_data[0], ifr.ifr_hwaddr.sa_data[1],
             ifr.ifr_hwaddr.sa_data[2], ifr.ifr_hwaddr.sa_data[3],
             ifr.ifr_hwaddr.sa_data[4], ifr.ifr_hwaddr.sa_data[5]);
@@ -510,5 +506,71 @@
     }
 }
 
+std::string EthernetInterface::mACAddress(std::string value)
+{
+    if (!mac_address::validate(value))
+    {
+        log<level::DEBUG>("MACAddress is not valid.",
+                          entry("MAC=%s", value.c_str()));
+        return MacAddressIntf::mACAddress();
+    }
+
+    // check whether MAC is broadcast mac.
+    auto intMac = mac_address::internal::convertToInt(value);
+
+    if (!(intMac ^ mac_address::broadcastMac))
+    {
+        log<level::DEBUG>("MACAddress is a broadcast mac.",
+                          entry("MAC=%s", value.c_str()));
+        return MacAddressIntf::mACAddress();
+    }
+
+    // Allow the mac to be set if one of the condition is true.
+    //   1) Incoming Mac is of local admin type.
+    //      or
+    //   2) Incoming mac is same as eeprom Mac.
+
+    if (!(intMac & mac_address::localAdminMask))
+    {
+        try
+        {
+            auto inventoryMac = mac_address::getfromInventory(bus);
+            auto intInventoryMac = mac_address::internal::convertToInt(inventoryMac);
+
+            if (intInventoryMac != intMac)
+            {
+                log<level::DEBUG>("Given MAC address is neither a local Admin \
+                                   type nor is same as in inventory");
+                return MacAddressIntf::mACAddress();
+            }
+        }
+        catch(InternalFailure& e)
+        {
+            log<level::ERR>("Exception occured during getting of MAC \
+                               address from Inventory");
+            return  MacAddressIntf::mACAddress();
+        }
+    }
+    auto interface = interfaceName();
+    execute("/sbin/fw_setenv", "fw_setenv", "ethaddr", value.c_str());
+    //TODO: would replace below three calls
+    //      with restarting of systemd-netwokd
+    //      through https://github.com/systemd/systemd/issues/6696
+    execute("/sbin/ip", "ip", "link", "set", "dev", interface.c_str(), "down");
+    execute("/sbin/ip", "ip", "link", "set", "dev", interface.c_str(), "address",
+            value.c_str());
+
+    execute("/sbin/ip", "ip", "link", "set", "dev", interface.c_str(), "up");
+
+    auto mac = MacAddressIntf::mACAddress(std::move(value));
+    //update all the vlan interfaces
+    for(const auto& intf: vlanInterfaces)
+    {
+        intf.second->updateMacAddress();
+    }
+    return mac;
+
+}
+
 }//namespace network
 }//namespace phosphor