SetLan: Apply the network changes once the timer expires

Start the timer once the set in progress parameter set to
set complete as part of Set LAN Configuration Parameters
command.

Resolves openbmc/openbmc#2932
Resolves openbmc/openbmc#2993

Change-Id: Ie6f3b331531da23c74fa44bb52fa1ddccffabd13
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/transporthandler.cpp b/transporthandler.cpp
index bbc6ae5..8bf0d43 100644
--- a/transporthandler.cpp
+++ b/transporthandler.cpp
@@ -1,3 +1,4 @@
+#include <chrono>
 #include <stdio.h>
 #include <string.h>
 #include <stdint.h>
@@ -7,6 +8,7 @@
 
 #include "host-ipmid/ipmid-api.h"
 #include "ipmid.hpp"
+#include "timer.hpp"
 #include "transporthandler.hpp"
 #include "utils.hpp"
 #include "net.hpp"
@@ -22,16 +24,7 @@
 #include <mapper.h>
 #endif
 
-/** @struct SetChannelAccessRequest
- *
- * IPMI payload for Set Channel access command request.
- */
-struct SetChannelAccessRequest
-{
-    uint8_t channelNumber;       //!< Channel number.
-    uint8_t setting;             //!< The setting values.
-    uint8_t privilegeLevelLimit; //!< The Privilege Level Limit
-} __attribute__((packed));
+extern std::unique_ptr<phosphor::ipmi::Timer> networkTimer;
 
 const int SIZE_MAC = 18; //xx:xx:xx:xx:xx:xx
 constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
@@ -40,6 +33,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));
@@ -391,6 +385,11 @@
     ipmi_ret_t rc = IPMI_CC_OK;
     *data_len = 0;
 
+    using namespace std::chrono_literals;
+
+    // time to wait before applying the network changes.
+    constexpr auto networkTimeout = 10000000us; // 10 sec
+
     char ipaddr[INET_ADDRSTRLEN];
     char netmask[INET_ADDRSTRLEN];
     char gateway[INET_ADDRSTRLEN];
@@ -498,11 +497,19 @@
                                  entry("GATEWAY=%s", channelConf->gateway.c_str()),
                                  entry("VLAN=%d", channelConf->vlanID));
 
-                log<level::INFO>("Use Set Channel Access command to apply");
+                if (!networkTimer)
+                {
+                  log<level::ERR>("Network timer is not instantiated");
+                  return IPMI_CC_UNSPECIFIED_ERROR;
+                }
+
+                // start/restart the timer
+                networkTimer->startTimer(networkTimeout);
             }
             else if (reqptr->data[0] == SET_IN_PROGRESS) // Set In Progress
             {
                 channelConf->lan_set_in_progress = SET_IN_PROGRESS;
+                channelConf->flush = true;
             }
         }
         break;
@@ -632,15 +639,8 @@
     return rc;
 }
 
-ipmi_ret_t ipmi_set_channel_access(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)
+void applyChanges(int channel)
 {
-    ipmi_ret_t rc = IPMI_CC_OK;
-
     std::string ipaddress;
     std::string gateway;
     uint8_t prefix {};
@@ -649,25 +649,16 @@
     ipmi::DbusObjectInfo ipObject;
     ipmi::DbusObjectInfo systemObject;
 
-    if (*data_len < sizeof(SetChannelAccessRequest))
-    {
-        return IPMI_CC_INVALID;
-    }
-
-    auto requestData = reinterpret_cast<const SetChannelAccessRequest*>
-                   (request);
-    int channel = requestData->channelNumber & CHANNEL_MASK;
-
     auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
     if (ethdevice.empty())
     {
-        return IPMI_CC_INVALID_FIELD_REQUEST;
+        log<level::ERR>("Unable to get the interface name",
+                        entry("CHANNEL=%d", channel));
+        return;
     }
     auto ethIp = ethdevice + "/" + ipmi::network::IP_TYPE;
     auto channelConf = getChannelConfig(channel);
 
-    // 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());
@@ -729,9 +720,8 @@
                                     entry("INTERFACE=%s",
                                           ipmi::network::ETHERNET_INTERFACE));
                     commit<InternalFailure>();
-                    rc = IPMI_CC_UNSPECIFIED_ERROR;
                     channelConf->clear();
-                    return rc;
+                    return;
                 }
 
                 networkInterfacePath = ancestorMap.begin()->first;
@@ -938,15 +928,42 @@
                         entry("IPSRC=%d", channelConf->ipsrc));
 
         commit<InternalFailure>();
-        rc = IPMI_CC_UNSPECIFIED_ERROR;
     }
 
     channelConf->clear();
-    return rc;
+}
+
+void commitNetworkChanges()
+{
+    for (const auto &channel : channelConfig)
+    {
+        if (channel.second->flush)
+        {
+            applyChanges(channel.first);
+        }
+    }
+}
+
+void createNetworkTimer()
+{
+    if (!networkTimer)
+    {
+        std::function<void()> networkTimerCallback(
+                std::bind(&commitNetworkChanges));
+
+        networkTimer =
+            std::make_unique<phosphor::ipmi::Timer>(
+                    ipmid_get_sd_event_connection(),
+                    networkTimerCallback);
+    }
+
 }
 
 void register_netfn_transport_functions()
 {
+    // As this timer is only for transport handler
+    // so creating it here.
+    createNetworkTimer();
     // <Wildcard Command>
     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_TRANSPORT, IPMI_CMD_WILDCARD);
     ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_WILDCARD, NULL, ipmi_transport_wildcard,