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/ipmid.cpp b/ipmid.cpp
index a05992b..91bbbee 100644
--- a/ipmid.cpp
+++ b/ipmid.cpp
@@ -42,6 +42,9 @@
 using cmdManagerPtr = std::unique_ptr<phosphor::host::command::Manager>;
 cmdManagerPtr cmdManager;
 
+// Global timer for network changes
+std::unique_ptr<phosphor::ipmi::Timer> networkTimer = nullptr;
+
 // Command and handler tuple. Used when clients ask the command to be put
 // into host message queue
 using CommandHandler = phosphor::host::command::CommandHandler;
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,
diff --git a/transporthandler.hpp b/transporthandler.hpp
index 42bf717..f440403 100644
--- a/transporthandler.hpp
+++ b/transporthandler.hpp
@@ -47,6 +47,7 @@
     // vlan id is in 12 bits and the 16th bit is for enable mask.
     uint32_t vlanID = ipmi::network::VLAN_ID_MASK;
     uint8_t lan_set_in_progress = SET_COMPLETE;
+    bool flush = false;
 
     void clear()
     {
@@ -57,6 +58,7 @@
         vlanID = ipmi::network::VLAN_ID_MASK;
         ipsrc = ipmi::network::IPOrigin::UNSPECIFIED;
         lan_set_in_progress = SET_COMPLETE;
+        flush = false;
     }
 };
 
@@ -66,3 +68,19 @@
 // @param[in] channel the channel
 // @return the ChannelConfig_t pointer.
 struct ChannelConfig_t* getChannelConfig(int channel);
+
+/** @brief Iterate over all the channelconfig and if
+ *         user has given the data for a channel then
+ *         apply the network changes for that channel.
+ */
+void commitNetworkChanges();
+
+/* @brief  Apply the network changes which is there in the
+ *         network cache for a given channel which gets filled
+ *         through setLan command. If some of the network
+ *         parameter was not given by the setLan then this function
+ *         gets the value of that parameter which is already
+ *         configured on the system.
+ * @param[in] channel: channel number.
+ */
+void applyChanges(int channel);