DCMI: Add  get/set Configuration Parameters command

1.Systemd-networkd doesn't allow to configure DHCP timings, so set is
not implemented.
2.Get command to DHCP timings will return hardcoded values from
systemd-networkd source code.
3.Systemd-networkd doesn't support Random Back off, so not implemented.
4.As the info about VendorClassIdentifier is not clear in the spec,
Right now we are not supporting DHCP Option 60 and Option 43. We will
open new issue for it.

Resolves openbmc/openbmc#2752

Change-Id: I2682e5e43ceb19647e5240b095f601777213530b
Signed-off-by: Nagaraju Goruganti <ngorugan@in.ibm.com>
diff --git a/dcmihandler.cpp b/dcmihandler.cpp
index fa3b55b..c077aa5 100644
--- a/dcmihandler.cpp
+++ b/dcmihandler.cpp
@@ -13,6 +13,7 @@
 #include <cmath>
 #include "xyz/openbmc_project/Common/error.hpp"
 #include "config.h"
+#include "net.hpp"
 
 using namespace phosphor::logging;
 using InternalFailure =
@@ -29,6 +30,24 @@
 constexpr auto DCMI_PARAMETER_REVISION = 2;
 constexpr auto DCMI_SPEC_MAJOR_VERSION = 1;
 constexpr auto DCMI_SPEC_MINOR_VERSION = 5;
+constexpr auto DCMI_CONFIG_PARAMETER_REVISION = 1;
+constexpr auto DCMI_RAND_BACK_OFF_MASK = 0x80;
+constexpr auto DCMI_OPTION_60_43_MASK = 0x02;
+constexpr auto DCMI_OPTION_12_MASK = 0x01;
+constexpr auto DCMI_ACTIVATE_DHCP_MASK = 0x01;
+constexpr auto DCMI_ACTIVATE_DHCP_REPLY = 0x00;
+constexpr auto DCMI_SET_CONF_PARAM_REQ_PACKET_MAX_SIZE = 0x05;
+constexpr auto DCMI_SET_CONF_PARAM_REQ_PACKET_MIN_SIZE = 0x04;
+constexpr auto DHCP_TIMING1 = 0x04; // 4 sec
+constexpr auto DHCP_TIMING2_UPPER = 0x00; //2 min
+constexpr auto DHCP_TIMING2_LOWER = 0x78;
+constexpr auto DHCP_TIMING3_UPPER = 0x00; //64 sec
+constexpr auto DHCP_TIMING3_LOWER = 0x40;
+// When DHCP Option 12 is enabled the string "SendHostName=true" will be
+// added into n/w configuration file and the parameter
+// SendHostNameEnabled will set to true.
+constexpr auto DHCP_OPT12_ENABLED = "SendHostNameEnabled";
+
 constexpr auto DCMI_CAP_JSON_FILE = "/usr/share/ipmi-providers/dcmi_cap.json";
 
 constexpr auto SENSOR_VALUE_INTF = "xyz.openbmc_project.Sensor.Value";
@@ -244,6 +263,40 @@
     return value.get<std::string>();
 }
 
+bool getDHCPEnabled()
+{
+    sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+
+    auto ethdevice = ipmi::network::ChanneltoEthernet(
+                                ethernetDefaultChannelNum);
+    auto ethernetObj = ipmi::getDbusObject(bus, ethernetIntf, networkRoot,
+                                ethdevice);
+    auto service = ipmi::getService(bus, ethernetIntf, ethernetObj.first);
+    auto value = ipmi::getDbusProperty(bus, service,
+                                ethernetObj.first, ethernetIntf, "DHCPEnabled");
+
+    return value.get<bool>();
+}
+
+bool getDHCPOption(std::string prop)
+{
+    sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+
+    auto service = ipmi::getService(bus, dhcpIntf, dhcpObj);
+    auto value = ipmi::getDbusProperty(bus, service, dhcpObj, dhcpIntf, prop);
+
+    return value.get<bool>();
+}
+
+
+void setDHCPOption(std::string prop, bool value)
+{
+    sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+
+    auto service = ipmi::getService(bus, dhcpIntf, dhcpObj);
+    ipmi::setDbusProperty(bus, service, dhcpObj, dhcpIntf, prop, value);
+}
+
 Json parseSensorConfig()
 {
     std::ifstream jsonFile(configFile);
@@ -1063,6 +1116,158 @@
     return power;
 }
 
+ipmi_ret_t setDCMIConfParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+                             ipmi_request_t request, ipmi_response_t response,
+                             ipmi_data_len_t data_len, ipmi_context_t context)
+{
+    auto requestData = reinterpret_cast<const dcmi::SetConfParamsRequest*>
+                       (request);
+    auto responseData = reinterpret_cast<dcmi::SetConfParamsResponse*>
+                        (response);
+
+
+    if (requestData->groupID != dcmi::groupExtId || *data_len <
+            DCMI_SET_CONF_PARAM_REQ_PACKET_MIN_SIZE || *data_len >
+            DCMI_SET_CONF_PARAM_REQ_PACKET_MAX_SIZE)
+    {
+        log<level::ERR>("Invalid Group ID or Invaild Requested Packet size",
+                        entry("GROUP_ID=%d", requestData->groupID),
+                        entry("PACKET SIZE=%d", *data_len));
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    *data_len = 0;
+
+    try
+    {
+        // Take action based on the Parameter Selector
+        switch (static_cast<dcmi::DCMIConfigParameters>(
+                requestData->paramSelect))
+        {
+            case dcmi::DCMIConfigParameters::ActivateDHCP:
+
+                if ((requestData->data[0] & DCMI_ACTIVATE_DHCP_MASK) &&
+                        dcmi::getDHCPEnabled())
+                {
+                  // When these conditions are met we have to trigger DHCP
+                  // protocol restart using the latest parameter settings, but
+                  // as per n/w manager design, each time when we update n/w
+                  // parameters, n/w service is restarted. So we no need to take
+                  // any action in this case.
+                }
+                break;
+
+            case dcmi::DCMIConfigParameters::DiscoveryConfig:
+
+                if (requestData->data[0] & DCMI_OPTION_12_MASK)
+                {
+                    dcmi::setDHCPOption(DHCP_OPT12_ENABLED, true);
+                }
+                else
+                {
+                    dcmi::setDHCPOption(DHCP_OPT12_ENABLED, false);
+                }
+
+                // Systemd-networkd doesn't support Random Back off
+                if (requestData->data[0] & DCMI_RAND_BACK_OFF_MASK)
+                {
+                    return IPMI_CC_INVALID;
+                }
+                break;
+            // Systemd-networkd doesn't allow to configure DHCP timigs
+            case dcmi::DCMIConfigParameters::DHCPTiming1:
+            case dcmi::DCMIConfigParameters::DHCPTiming2:
+            case dcmi::DCMIConfigParameters::DHCPTiming3:
+            default:
+                return IPMI_CC_INVALID;
+        }
+    }
+    catch (std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+    responseData->groupID = dcmi::groupExtId;
+    *data_len = sizeof(dcmi::SetConfParamsResponse);
+
+    return IPMI_CC_OK;
+}
+
+ipmi_ret_t getDCMIConfParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+                             ipmi_request_t request, ipmi_response_t response,
+                             ipmi_data_len_t data_len, ipmi_context_t context)
+{
+
+    auto requestData = reinterpret_cast<const dcmi::GetConfParamsRequest*>
+                       (request);
+    auto responseData = reinterpret_cast<dcmi::GetConfParamsResponse*>
+                        (response);
+
+    responseData->data[0] = 0x00;
+
+    if (requestData->groupID != dcmi::groupExtId || *data_len != sizeof(
+            dcmi::GetConfParamsRequest))
+    {
+        log<level::ERR>("Invalid Group ID or Invaild Requested Packet size",
+                        entry("GROUP_ID=%d", requestData->groupID),
+                        entry("PACKET SIZE=%d", *data_len));
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    *data_len = 0;
+
+    try
+    {
+        // Take action based on the Parameter Selector
+        switch (static_cast<dcmi::DCMIConfigParameters>(
+                requestData->paramSelect))
+        {
+            case dcmi::DCMIConfigParameters::ActivateDHCP:
+                responseData->data[0] = DCMI_ACTIVATE_DHCP_REPLY;
+                *data_len = sizeof(dcmi::GetConfParamsResponse) + 1;
+                break;
+            case dcmi::DCMIConfigParameters::DiscoveryConfig:
+                if (dcmi::getDHCPOption(DHCP_OPT12_ENABLED))
+                {
+                    responseData->data[0] |= DCMI_OPTION_12_MASK;
+                }
+                *data_len = sizeof(dcmi::GetConfParamsResponse) + 1;
+                break;
+            // Get below values from Systemd-networkd source code
+            case dcmi::DCMIConfigParameters::DHCPTiming1:
+                responseData->data[0] = DHCP_TIMING1;
+                *data_len = sizeof(dcmi::GetConfParamsResponse) + 1;
+                break;
+            case dcmi::DCMIConfigParameters::DHCPTiming2:
+                responseData->data[0] = DHCP_TIMING2_LOWER;
+                responseData->data[1] = DHCP_TIMING2_UPPER;
+                *data_len = sizeof(dcmi::GetConfParamsResponse) + 2;
+                break;
+            case dcmi::DCMIConfigParameters::DHCPTiming3:
+                responseData->data[0] = DHCP_TIMING3_LOWER;
+                responseData->data[1] = DHCP_TIMING3_UPPER;
+                *data_len = sizeof(dcmi::GetConfParamsResponse) + 2;
+                break;
+            default:
+                *data_len = 0;
+                return IPMI_CC_INVALID;
+        }
+    }
+    catch (std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+
+    responseData->groupID = dcmi::groupExtId;
+    responseData->major = DCMI_SPEC_MAJOR_VERSION;
+    responseData->minor = DCMI_SPEC_MINOR_VERSION;
+    responseData->paramRevision = DCMI_CONFIG_PARAMETER_REVISION;
+
+    return IPMI_CC_OK;
+}
+
+
 ipmi_ret_t getPowerReading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
             ipmi_request_t request, ipmi_response_t response,
             ipmi_data_len_t data_len, ipmi_context_t context)
@@ -1364,6 +1569,14 @@
     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_SENSOR_INFO,
                            NULL, getSensorInfo, PRIVILEGE_USER);
 
+    // <Get DCMI Configuration Parameters>
+    ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_CONF_PARAMS,
+        NULL, getDCMIConfParams, PRIVILEGE_USER);
+
+    // <Set DCMI Configuration Parameters>
+    ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_CONF_PARAMS,
+        NULL, setDCMIConfParams, PRIVILEGE_ADMIN);
+
     return;
 }
 // 956379