GetLan: Support for get/set of ipsrc parameter

Change-Id: Id9c52bb0963c5924f80f9e273b53ed5556b16a2c
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/transporthandler.cpp b/transporthandler.cpp
index 30526b3..e571d30 100644
--- a/transporthandler.cpp
+++ b/transporthandler.cpp
@@ -3,6 +3,7 @@
 #include <stdint.h>
 #include <arpa/inet.h>
 #include <string>
+#include <experimental/filesystem>
 
 #include "host-ipmid/ipmid-api.h"
 #include "ipmid.hpp"
@@ -40,6 +41,7 @@
 
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+namespace fs = std::experimental::filesystem;
 
 void register_netfn_transport_functions() __attribute__((constructor));
 
@@ -59,19 +61,28 @@
                 std::string ipaddress;
                 if (lan_set_in_progress == SET_COMPLETE)
                 {
-                    auto ipObjectInfo = ipmi::getDbusObject(
-                                            bus,
-                                            ipmi::network::IP_INTERFACE,
-                                            ipmi::network::ROOT,
-                                            ipmi::network::IP_TYPE);
+                    try
+                    {
+                        auto ipObjectInfo = ipmi::getDbusObject(
+                                bus,
+                                ipmi::network::IP_INTERFACE,
+                                ipmi::network::ROOT,
+                                ipmi::network::IP_TYPE);
 
-                    auto properties = ipmi::getAllDbusProperties(
-                                         bus,
-                                         ipObjectInfo.second,
-                                         ipObjectInfo.first,
-                                         ipmi::network::IP_INTERFACE);
+                        auto properties = ipmi::getAllDbusProperties(
+                                bus,
+                                ipObjectInfo.second,
+                                ipObjectInfo.first,
+                                ipmi::network::IP_INTERFACE);
 
-                    ipaddress = properties["Address"].get<std::string>();
+                        ipaddress = properties["Address"].get<std::string>();
+                    }
+                    // ignore the exception, as it is a valid condtion that
+                    // system is not confiured with any ip.
+                    catch (InternalFailure& e)
+                    {
+                        // nothing to do.
+                    }
                 }
                 else if (lan_set_in_progress == SET_IN_PROGRESS)
                 {
@@ -83,32 +94,120 @@
             }
             break;
 
-            case LAN_PARM_SUBNET:
+            case LAN_PARM_IPSRC:
             {
+                std::string networkInterfacePath;
+
                 if (lan_set_in_progress == SET_COMPLETE)
                 {
-                    auto ipObjectInfo = ipmi::getDbusObject(
-                                            bus,
-                                            ipmi::network::IP_INTERFACE,
-                                            ipmi::network::ROOT,
-                                            ipmi::network::IP_TYPE);
+                    try
+                    {
+                        ipmi::ObjectTree ancestorMap;
+                        // if the system is having ip object,then
+                        // get the IP object.
+                        auto ipObject = ipmi::getDbusObject(
+                                bus,
+                                ipmi::network::IP_INTERFACE,
+                                ipmi::network::ROOT,
+                                ipmi::network::IP_TYPE);
 
-                    auto properties = ipmi::getAllDbusProperties(
-                                         bus,
-                                         ipObjectInfo.second,
-                                         ipObjectInfo.first,
-                                         ipmi::network::IP_INTERFACE);
+                        // Get the parent interface of the IP object.
+                        try
+                        {
+                            ipmi::InterfaceList interfaces;
+                            interfaces.emplace_back(
+                                    ipmi::network::ETHERNET_INTERFACE);
 
-                    auto prefix = properties["PrefixLength"].get<uint8_t>();
-                    unsigned long mask = ipmi::network::MASK_32_BIT;
-                    mask = htonl(mask << (ipmi::network::BITS_32 - prefix));
+                            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));
+                            break;
+
+                        }
+                        // for an ip object there would be single parent
+                        // interface.
+                        networkInterfacePath = ancestorMap.begin()->first;
+                    }
+                    catch (InternalFailure& e)
+                    {
+                        // 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::INTERFACE);
+
+                        networkInterfacePath = networkInterfaceObject.first;
+                    }
+
+                    auto variant = ipmi::getDbusProperty(
+                            bus,
+                            ipmi::network::SERVICE,
+                            networkInterfacePath,
+                            ipmi::network::ETHERNET_INTERFACE,
+                            "DHCPEnabled");
+
+                    auto dhcpEnabled = variant.get<bool>();
+                    // As per IPMI spec 2=>DHCP, 1=STATIC
+                    auto ipsrc = dhcpEnabled ? ipmi::network::IPOrigin::DHCP :
+                                               ipmi::network::IPOrigin::STATIC;
+
+                    memcpy(data, &ipsrc, ipmi::network::IPSRC_SIZE_BYTE);
+                }
+                else if (lan_set_in_progress == SET_IN_PROGRESS)
+                {
+                   memcpy(data, &(channelConfig.ipsrc),
+                          ipmi::network::IPSRC_SIZE_BYTE);
+                }
+            }
+            break;
+
+            case LAN_PARM_SUBNET:
+            {
+                unsigned long mask {};
+                if (lan_set_in_progress == SET_COMPLETE)
+                {
+                    try
+                    {
+                        auto ipObjectInfo = ipmi::getDbusObject(
+                                bus,
+                                ipmi::network::IP_INTERFACE,
+                                ipmi::network::ROOT,
+                                ipmi::network::IP_TYPE);
+
+                        auto properties = ipmi::getAllDbusProperties(
+                                bus,
+                                ipObjectInfo.second,
+                                ipObjectInfo.first,
+                                ipmi::network::IP_INTERFACE);
+
+                        auto prefix = properties["PrefixLength"].get<uint8_t>();
+                        mask = ipmi::network::MASK_32_BIT;
+                        mask = htonl(mask << (ipmi::network::BITS_32 - prefix));
+                    }
+                    // ignore the exception, as it is a valid condtion that
+                    // system is not confiured with any ip.
+                    catch (InternalFailure& e)
+                    {
+                        // nothing to do
+                    }
                     memcpy(data, &mask, ipmi::network::IPV4_ADDRESS_SIZE_BYTE);
                 }
                 else if (lan_set_in_progress == SET_IN_PROGRESS)
                 {
                     inet_pton(AF_INET, channelConfig.netmask.c_str(),
                               reinterpret_cast<void*>(data));
-
                 }
 
             }
@@ -120,19 +219,28 @@
 
                 if (lan_set_in_progress == SET_COMPLETE)
                 {
-                    auto systemObject = ipmi::getDbusObject(
-                            bus,
-                            ipmi::network::SYSTEMCONFIG_INTERFACE,
-                            ipmi::network::ROOT);
+                    try
+                    {
+                        auto systemObject = ipmi::getDbusObject(
+                                bus,
+                                ipmi::network::SYSTEMCONFIG_INTERFACE,
+                                ipmi::network::ROOT);
 
-                    auto systemProperties = ipmi::getAllDbusProperties(
-                            bus,
-                            systemObject.second,
-                            systemObject.first,
-                            ipmi::network::SYSTEMCONFIG_INTERFACE);
+                        auto systemProperties = ipmi::getAllDbusProperties(
+                                bus,
+                                systemObject.second,
+                                systemObject.first,
+                                ipmi::network::SYSTEMCONFIG_INTERFACE);
 
-                    gateway = systemProperties["DefaultGateway"].get<
-                                 std::string>();
+                        gateway = systemProperties["DefaultGateway"].get<
+                            std::string>();
+                    }
+                    // ignore the exception, as it is a valid condtion that
+                    // system is not confiured with any ip.
+                    catch (InternalFailure& e)
+                    {
+                        // nothing to do
+                    }
 
                 }
                 else if (lan_set_in_progress == SET_IN_PROGRESS)
@@ -142,7 +250,6 @@
 
                 inet_pton(AF_INET, gateway.c_str(),
                           reinterpret_cast<void*>(data));
-
             }
             break;
 
@@ -183,23 +290,33 @@
 
             case LAN_PARM_VLAN:
             {
+                uint16_t vlanID {};
                 if (lan_set_in_progress == SET_COMPLETE)
                 {
-                    auto ipObjectInfo = ipmi::getDbusObject(
-                                            bus,
-                                            ipmi::network::IP_INTERFACE,
-                                            ipmi::network::ROOT,
-                                            ipmi::network::IP_TYPE);
-
-                    auto vlanID = static_cast<uint16_t>(
-                            ipmi::network::getVLAN(ipObjectInfo.first));
-
-                    vlanID = htole16(vlanID);
-
-                    if (vlanID)
+                    try
                     {
-                        //Enable the 16th bit
-                        vlanID |= htole16(ipmi::network::VLAN_ENABLE_MASK);
+                        auto ipObjectInfo = ipmi::getDbusObject(
+                                bus,
+                                ipmi::network::IP_INTERFACE,
+                                ipmi::network::ROOT,
+                                ipmi::network::IP_TYPE);
+
+                        vlanID = static_cast<uint16_t>(
+                                ipmi::network::getVLAN(ipObjectInfo.first));
+
+                        vlanID = htole16(vlanID);
+
+                        if (vlanID)
+                        {
+                            //Enable the 16th bit
+                            vlanID |= htole16(ipmi::network::VLAN_ENABLE_MASK);
+                        }
+                    }
+                    // ignore the exception, as it is a valid condtion that
+                    // system is not confiured with any ip.
+                    catch (InternalFailure& e)
+                    {
+                        // nothing to do
                     }
 
                     memcpy(data, &vlanID, ipmi::network::VLAN_SIZE_BYTE);
@@ -273,6 +390,14 @@
         }
         break;
 
+        case LAN_PARM_IPSRC:
+        {
+            uint8_t ipsrc{};
+            memcpy(&ipsrc, reqptr->data, ipmi::network::IPSRC_SIZE_BYTE);
+            channelConfig.ipsrc = static_cast<ipmi::network::IPOrigin>(ipsrc);
+        }
+        break;
+
         case LAN_PARM_MAC:
         {
             char mac[SIZE_MAC];
@@ -419,7 +544,7 @@
              (reqptr->parameter == LAN_PARM_GATEWAY) ||
              (reqptr->parameter == LAN_PARM_MAC))
     {
-        uint8_t buf[ipmi::network::MAC_ADDRESS_SIZE_BYTE + 1];
+        uint8_t buf[ipmi::network::MAC_ADDRESS_SIZE_BYTE + 1] = {};
 
         *data_len = sizeof(current_revision);
         memcpy(buf, &current_revision, *data_len);
@@ -443,7 +568,7 @@
     }
     else if (reqptr->parameter == LAN_PARM_VLAN)
     {
-        uint8_t buf[ipmi::network::VLAN_SIZE_BYTE + 1];
+        uint8_t buf[ipmi::network::VLAN_SIZE_BYTE + 1] = {};
 
         *data_len = sizeof(current_revision);
         memcpy(buf, &current_revision, *data_len);
@@ -453,6 +578,17 @@
             memcpy(response, &buf, *data_len);
         }
     }
+    else if (reqptr->parameter == LAN_PARM_IPSRC)
+    {
+        uint8_t buff[ipmi::network::IPSRC_SIZE_BYTE + 1] = {};
+        *data_len = sizeof(current_revision);
+        memcpy(buff, &current_revision, *data_len);
+        if (getNetworkData(reqptr->parameter, &buff[1]) == IPMI_CC_OK)
+        {
+            *data_len = sizeof(buff);
+            memcpy(response, &buff, *data_len);
+        }
+    }
     else
     {
         log<level::ERR>("Unsupported parameter",
diff --git a/transporthandler.hpp b/transporthandler.hpp
index 24ee8be..4894190 100644
--- a/transporthandler.hpp
+++ b/transporthandler.hpp
@@ -21,6 +21,7 @@
 static const int LAN_PARM_AUTHSUPPORT = 1;
 static const int LAN_PARM_AUTHENABLES = 2;
 static const int LAN_PARM_IP          = 3;
+static const int LAN_PARM_IPSRC       = 4;
 static const int LAN_PARM_MAC         = 5;
 static const int LAN_PARM_SUBNET      = 6;
 static const int LAN_PARM_GATEWAY     = 12;
@@ -29,6 +30,7 @@
 struct ChannelConfig_t
 {
     std::string ipaddr;
+    ipmi::network::IPOrigin ipsrc = ipmi::network::IPOrigin::UNSPECIFIED;
     std::string netmask;
     std::string gateway;
     std::string macAddress;
@@ -44,5 +46,6 @@
         gateway.clear();
         macAddress.clear();
         vlanID = ipmi::network::VLAN_ID_MASK;
+        ipsrc = ipmi::network::IPOrigin::UNSPECIFIED;
     }
 };
diff --git a/types.hpp b/types.hpp
index 5b943f4..9c2237f 100644
--- a/types.hpp
+++ b/types.hpp
@@ -26,6 +26,8 @@
 using ObjectTree = std::map<DbusObjectPath,
                             std::map<DbusService, std::vector<DbusInterface>>>;
 
+using InterfaceList = std::vector<std::string>;
+
 namespace sensor
 {
 
@@ -110,10 +112,19 @@
 
 constexpr auto MAC_ADDRESS_SIZE_BYTE = 6;
 constexpr auto VLAN_SIZE_BYTE = 2;
+constexpr auto IPSRC_SIZE_BYTE = 1;
 constexpr auto BITS_32 = 32;
 constexpr auto MASK_32_BIT = 0xFFFFFFFF;
 constexpr auto VLAN_ID_MASK = 0x00000FFF;
 constexpr auto VLAN_ENABLE_MASK = 0x8000;
 
+enum class IPOrigin: uint8_t
+{
+    UNSPECIFIED = 0,
+    STATIC = 1,
+    DHCP = 2,
+};
+
+
 }//namespace network
 }//namespace ipmi
diff --git a/utils.cpp b/utils.cpp
index 2e6c5e7..d81bf76 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -276,6 +276,51 @@
     }
 }
 
+ObjectTree getAllAncestors(sdbusplus::bus::bus& bus,
+                           const std::string& path,
+                           InterfaceList&& interfaces)
+{
+    auto convertToString = [](InterfaceList& interfaces) -> std::string
+    {
+        std::string intfStr;
+        for (const auto& intf : interfaces)
+        {
+            intfStr += "," + intf;
+        }
+        return intfStr;
+    };
+
+    auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME,
+                                          MAPPER_OBJ,
+                                          MAPPER_INTF,
+                                          "GetAncestors");
+    mapperCall.append(path, interfaces);
+
+    auto mapperReply = bus.call(mapperCall);
+    if (mapperReply.is_method_error())
+    {
+        log<level::ERR>("Error in mapper call",
+                        entry("PATH=%s", path.c_str()),
+                        entry("INTERFACES=%s",
+                            convertToString(interfaces).c_str()));
+
+        elog<InternalFailure>();
+    }
+
+    ObjectTree objectTree;
+    mapperReply.read(objectTree);
+
+    if (objectTree.empty())
+    {
+        log<level::ERR>("No Object has impelmented the interface",
+                        entry("PATH=%s", path.c_str()),
+                        entry("INTERFACES=%s",
+                            convertToString(interfaces).c_str()));
+        elog<InternalFailure>();
+    }
+
+    return objectTree;
+}
 
 namespace method_no_args
 {
diff --git a/utils.hpp b/utils.hpp
index a172e22..b50fde7 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -113,6 +113,17 @@
                           const std::string& interface,
                           const std::string& match = {});
 
+/** @brief Gets the ancestor objects of the given object
+           which implements the given interface.
+ *  @param[in] bus - Dbus bus object.
+ *  @param[in] path - Child Dbus object path.
+ *  @param[in] interfaces - Dbus interface list.
+ *  @return map of object path and service info.
+ */
+ObjectTree getAllAncestors(sdbusplus::bus::bus& bus,
+                           const std::string& path,
+                           InterfaceList&& interfaces);
+
 namespace method_no_args
 {