SetLan: Make changes in set channel access to support ipsrc param

Resolves openbmc/openbmc#2138

Change-Id: I4b4b8e915bcfea6ed5f913ede5f714290483e4f9
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/apphandler.cpp b/apphandler.cpp
index 8340922..d911356 100644
--- a/apphandler.cpp
+++ b/apphandler.cpp
@@ -11,6 +11,8 @@
 #include <mapper.h>
 #include <array>
 #include <vector>
+#include <experimental/filesystem>
+
 #include <arpa/inet.h>
 #include "transporthandler.hpp"
 
@@ -30,6 +32,7 @@
 
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+namespace fs = std::experimental::filesystem;
 
 // Offset in get device id command.
 typedef struct
@@ -546,117 +549,179 @@
     std::string gateway;
     uint8_t prefix {};
     uint32_t vlanID {};
+    std::string networkInterfacePath;
+    ipmi::DbusObjectInfo ipObject;
+    ipmi::DbusObjectInfo systemObject;
 
     // Todo: parse the request data if needed.
-
     // Using Set Channel cmd to apply changes of Set Lan Cmd.
     try
     {
-
         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
 
         log<level::INFO>("Network data from Cache",
                          entry("PREFIX=%s", channelConfig.netmask.c_str()),
                          entry("ADDRESS=%s", channelConfig.ipaddr.c_str()),
-                         entry("GATEWAY=%s", channelConfig.gateway.c_str()));
+                         entry("GATEWAY=%s", channelConfig.gateway.c_str()),
+                         entry("VLAN=%d", channelConfig.vlanID),
+                         entry("IPSRC=%d", channelConfig.ipsrc));
 
-        auto systemObject = ipmi::getDbusObject(bus,
-                                 ipmi::network::SYSTEMCONFIG_INTERFACE,
-                                 ipmi::network::ROOT);
-
-        // TODO Currently IPMI supports single interface,need to handle
-        // Multiple interface through
-        // https://github.com/openbmc/openbmc/issues/2138
-
-        auto networkInterfaceObject = ipmi::getDbusObject(
-                    bus,
-                    ipmi::network::ETHERNET_INTERFACE,
-                    ipmi::network::ROOT,
-                    ipmi::network::INTERFACE);
-
-
-
-        // check wthether user has given all the data
-        if (!channelConfig.ipaddr.empty() &&
-            !channelConfig.netmask.empty() &&
-            !channelConfig.gateway.empty())
+        if (channelConfig.vlanID != ipmi::network::VLAN_ID_MASK)
         {
-            //convert mask into prefix
-            ipaddress = channelConfig.ipaddr;
-            prefix = ipmi::network::toPrefix(AF_INET, channelConfig.netmask);
-            gateway = channelConfig.gateway;
-
-            if (channelConfig.vlanID != ipmi::network::VLAN_ID_MASK)
-            {
-                //get the first twelve bits which is vlan id
-                //not interested in rest of the bits.
-                vlanID = channelConfig.vlanID & ipmi::network::VLAN_ID_MASK;
-                channelConfig.vlanID = le32toh(channelConfig.vlanID);
-            }
-
+            //get the first twelve bits which is vlan id
+            //not interested in rest of the bits.
+            channelConfig.vlanID = le32toh(channelConfig.vlanID);
+            vlanID = channelConfig.vlanID & ipmi::network::VLAN_ID_MASK;
         }
-        else
+
+        // if the asked ip src is DHCP then not interested in
+        // any given data except vlan.
+        if (channelConfig.ipsrc != ipmi::network::IPOrigin::DHCP)
         {
-            // We have partial filled cache so get the remaning
-            // info from the system.
+            // always get the system object
+            systemObject = ipmi::getDbusObject(
+                    bus,
+                    ipmi::network::SYSTEMCONFIG_INTERFACE,
+                    ipmi::network::ROOT);
 
-            // gets the network data from the system as user has
-            // not given all the data then use the data fetched from the
-            // system but it is implementation dependent,IPMI spec doesn't
-            // force it.
-
-            // if system is not having any ip object don't throw error,
+            // the below code is to determine the mode of the interface
+            // as the handling is same, if the system is configured with
+            // DHCP or user has given all the data.
             try
             {
-                auto ipObjectInfo = ipmi::getDbusObject(bus,
-                        ipmi::network::IP_INTERFACE,
+                ipmi::ObjectTree ancestorMap;
+
+                ipmi::InterfaceList interfaces {
+                    ipmi::network::ETHERNET_INTERFACE };
+
+                // if the system is having ip object,then
+                // get the IP object.
+                ipObject = ipmi::getDbusObject(bus,
+                                               ipmi::network::IP_INTERFACE,
+                                               ipmi::network::ROOT,
+                                               ipmi::network::IP_TYPE);
+
+                // Get the parent interface of the IP object.
+                try
+                {
+                    ancestorMap = ipmi::getAllAncestors(bus,
+                                                        ipObject.first,
+                                                        std::move(interfaces));
+                }
+                catch (InternalFailure& e)
+                {
+                    // if unable to get the parent interface
+                    // then commit the error and return.
+                    log<level::ERR>("Unable to get the parent interface",
+                                    entry("PATH=%s", ipObject.first.c_str()),
+                                    entry("INTERFACE=%s",
+                                          ipmi::network::ETHERNET_INTERFACE));
+                    commit<InternalFailure>();
+                    rc = IPMI_CC_UNSPECIFIED_ERROR;
+                    channelConfig.clear();
+                    return rc;
+                }
+
+                networkInterfacePath = ancestorMap.begin()->first;
+            }
+            catch (InternalFailure& e)
+            {
+                // TODO Currently IPMI supports single interface,need to handle
+                // Multiple interface through
+                // https://github.com/openbmc/openbmc/issues/2138
+
+                // if there is no ip configured on the system,then
+                // get the network interface object.
+                auto networkInterfaceObject = ipmi::getDbusObject(
+                        bus,
+                        ipmi::network::ETHERNET_INTERFACE,
                         ipmi::network::ROOT,
-                        ipmi::network::IP_TYPE);
+                        ipmi::network::INTERFACE);
 
-                auto properties = ipmi::getAllDbusProperties(bus,
-                        ipObjectInfo.second,
-                        ipObjectInfo.first,
-                        ipmi::network::IP_INTERFACE);
+                networkInterfacePath = std::move(networkInterfaceObject.first);
+            }
 
-                ipaddress = channelConfig.ipaddr.empty() ?
-                    std::move(properties["Address"].get<std::string>()) :
-                    channelConfig.ipaddr;
+            // get the configured mode on the system.
+            auto enableDHCP = ipmi::getDbusProperty(
+                    bus,
+                    ipmi::network::SERVICE,
+                    networkInterfacePath,
+                    ipmi::network::ETHERNET_INTERFACE,
+                    "DHCPEnabled").get<bool>();
 
-                prefix = channelConfig.netmask.empty() ?
-                    properties["PrefixLength"].get<uint8_t>() :
-                    ipmi::network::toPrefix(AF_INET,
-                                            channelConfig.netmask);
-
+            // check whether user has given all the data
+            // or the configured system interface is dhcp enabled,
+            // in both of the cases get the values from the cache.
+            if ((!channelConfig.ipaddr.empty() &&
+                 !channelConfig.netmask.empty() &&
+                 !channelConfig.gateway.empty()) ||
+                (enableDHCP)) // configured system interface mode = DHCP
+            {
+                //convert mask into prefix
+                ipaddress = channelConfig.ipaddr;
+                prefix = ipmi::network::toPrefix(AF_INET, channelConfig.netmask);
+                gateway = channelConfig.gateway;
                 if (channelConfig.vlanID != ipmi::network::VLAN_ID_MASK)
                 {
                     //get the first twelve bits which is vlan id
                     //not interested in rest of the bits.
-                    vlanID = channelConfig.vlanID & ipmi::network::VLAN_ID_MASK;
                     channelConfig.vlanID = le32toh(channelConfig.vlanID);
+                    vlanID = channelConfig.vlanID & ipmi::network::VLAN_ID_MASK;
                 }
                 else
                 {
-                    vlanID = ipmi::network::getVLAN(ipObjectInfo.first);
+                    vlanID = ipmi::network::getVLAN(networkInterfacePath);
                 }
 
             }
-            catch (InternalFailure& e)
+            else // asked ip src = static and configured system src = static
+                // or partially given data.
             {
-                log<level::INFO>("Failed to get IP object which matches",
-                        entry("INTERFACE=%s", ipmi::network::IP_INTERFACE),
-                        entry("MATCH=%s", ipmi::network::IP_TYPE));
+                // We have partial filled cache so get the remaining
+                // info from the system.
+
+                // Get the network data from the system as user has
+                // not given all the data then use the data fetched from the
+                // system but it is implementation dependent,IPMI spec doesn't
+                // force it.
+
+                // if system is not having any ip object don't throw error,
+                try
+                {
+                    auto properties = ipmi::getAllDbusProperties(
+                            bus,
+                            ipObject.second,
+                            ipObject.first,
+                            ipmi::network::IP_INTERFACE);
+
+                    ipaddress = channelConfig.ipaddr.empty() ?
+                        properties["Address"].get<std::string>() :
+                        channelConfig.ipaddr;
+
+                    prefix = channelConfig.netmask.empty() ?
+                        properties["PrefixLength"].get<uint8_t>() :
+                        ipmi::network::toPrefix(AF_INET,
+                                channelConfig.netmask);
+
+                }
+                catch (InternalFailure& e)
+                {
+                    log<level::INFO>("Failed to get IP object which matches",
+                            entry("INTERFACE=%s", ipmi::network::IP_INTERFACE),
+                            entry("MATCH=%s", ipmi::network::IP_TYPE));
+                }
+
+                auto systemProperties = ipmi::getAllDbusProperties(
+                        bus,
+                        systemObject.second,
+                        systemObject.first,
+                        ipmi::network::SYSTEMCONFIG_INTERFACE);
+
+                gateway = channelConfig.gateway.empty() ?
+                        systemProperties["DefaultGateway"].get<std::string>() :
+                        channelConfig.gateway;
+
             }
-
-            auto systemProperties = ipmi::getAllDbusProperties(
-                    bus,
-                    systemObject.second,
-                    systemObject.first,
-                    ipmi::network::SYSTEMCONFIG_INTERFACE);
-
-            gateway = channelConfig.gateway.empty() ?
-                std::move(systemProperties["DefaultGateway"].get<std::string>()) :
-                channelConfig.gateway;
-
         }
 
         // Currently network manager doesn't support purging of all the
@@ -668,40 +733,102 @@
         // Multiple interface through
         // https://github.com/openbmc/openbmc/issues/2138
 
+        // instead of deleting all the vlan interfaces and
+        // all the ipv4 address,we will call reset method.
         //delete all the vlan interfaces
-        //delete all the ipv4 addresses
-        ipmi::deleteAllDbusObjects(bus, ipmi::network::ROOT,
+
+        ipmi::deleteAllDbusObjects(bus,
+                                   ipmi::network::ROOT,
                                    ipmi::network::VLAN_INTERFACE);
-        ipmi::deleteAllDbusObjects(bus, ipmi::network::ROOT,
+
+        // set the interface mode  to static
+        auto networkInterfaceObject = ipmi::getDbusObject(
+                bus,
+                ipmi::network::ETHERNET_INTERFACE,
+                ipmi::network::ROOT,
+                ipmi::network::INTERFACE);
+
+        // setting the physical interface mode to static.
+        ipmi::setDbusProperty(bus,
+                              ipmi::network::SERVICE,
+                              networkInterfaceObject.first,
+                              ipmi::network::ETHERNET_INTERFACE,
+                              "DHCPEnabled",
+                              false);
+
+        networkInterfacePath = networkInterfaceObject.first;
+
+        //delete all the ipv4 addresses
+        ipmi::deleteAllDbusObjects(bus,
+                                   ipmi::network::ROOT,
                                    ipmi::network::IP_INTERFACE,
                                    ipmi::network::IP_TYPE);
+
         if (vlanID)
         {
-            ipmi::network::createVLAN(bus, ipmi::network::SERVICE,
+            ipmi::network::createVLAN(bus,
+                                      ipmi::network::SERVICE,
                                       ipmi::network::ROOT,
-                                      ipmi::network::INTERFACE, vlanID);
+                                      ipmi::network::INTERFACE,
+                                      vlanID);
 
-            networkInterfaceObject = ipmi::getDbusObject(
+            auto networkInterfaceObject = ipmi::getDbusObject(
                     bus,
                     ipmi::network::VLAN_INTERFACE,
                     ipmi::network::ROOT);
+
+           networkInterfacePath = networkInterfaceObject.first;
         }
 
-        ipmi::network::createIP(bus, networkInterfaceObject.second,
-                                networkInterfaceObject.first,
-                                ipv4Protocol, ipaddress, prefix);
+        if (channelConfig.ipsrc == ipmi::network::IPOrigin::DHCP)
+        {
+            ipmi::setDbusProperty(bus,
+                                  ipmi::network::SERVICE,
+                                  networkInterfacePath,
+                                  ipmi::network::ETHERNET_INTERFACE,
+                                  "DHCPEnabled",
+                                  true);
+        }
+        else
+        {
+            //change the mode to static
+            ipmi::setDbusProperty(bus,
+                                  ipmi::network::SERVICE,
+                                  networkInterfacePath,
+                                  ipmi::network::ETHERNET_INTERFACE,
+                                  "DHCPEnabled",
+                                  false);
 
-        ipmi::setDbusProperty(bus, systemObject.second, systemObject.first,
-                              ipmi::network::SYSTEMCONFIG_INTERFACE,
-                              "DefaultGateway", gateway);
+            if (!ipaddress.empty())
+            {
+                ipmi::network::createIP(bus,
+                                        ipmi::network::SERVICE,
+                                        networkInterfacePath,
+                                        ipv4Protocol,
+                                        ipaddress,
+                                        prefix);
+            }
+
+            if (!gateway.empty())
+            {
+                ipmi::setDbusProperty(bus,
+                                      systemObject.second,
+                                      systemObject.first,
+                                      ipmi::network::SYSTEMCONFIG_INTERFACE,
+                                      "DefaultGateway",
+                                      std::string(gateway));
+            }
+        }
 
     }
     catch (InternalFailure& e)
     {
         log<level::ERR>("Failed to set network data",
                         entry("PREFIX=%d", prefix),
-                        entry("ADDRESS=%s", ipaddress),
-                        entry("GATEWAY=%s", gateway));
+                        entry("ADDRESS=%s", ipaddress.c_str()),
+                        entry("GATEWAY=%s", gateway.c_str()),
+                        entry("VLANID=%d", vlanID),
+                        entry("IPSRC=%d", channelConfig.ipsrc));
 
         commit<InternalFailure>();
         rc = IPMI_CC_UNSPECIFIED_ERROR;