Add transaction support to Set Lan Cmd and Set Channel Access Cmd

* Ipmid will cache single setting to memory by Set Lan cmd
* Add Set Channel Access cmd to ipmid
* Set Channel Access cmd is used to apply the previous cached settings

TODO: parse the request data of Set Channel Access cmd if needed.

Resolves openbmc/openbmc#444

Change-Id: Ia7ac57a551731920ffe69dad5c69c995521fed02
Signed-off-by: Nan Li <william.bjlinan@hotmail.com>
diff --git a/apphandler.cpp b/apphandler.cpp
index 9d94b1b..431cbf0 100644
--- a/apphandler.cpp
+++ b/apphandler.cpp
@@ -7,9 +7,15 @@
 #include <systemd/sd-bus.h>
 #include <mapper.h>
 #include <array>
+#include <arpa/inet.h>
+#include "transporthandler.h"
 
 extern sd_bus *bus;
 
+constexpr auto app_obj = "/org/openbmc/NetworkManager/Interface";
+constexpr auto app_ifc = "org.openbmc.NetworkManager";
+constexpr auto app_nwinterface = "eth0";
+
 void register_netfn_app_functions() __attribute__((constructor));
 
 // Offset in get device id command.
@@ -535,6 +541,117 @@
     return rc;
 }
 
+extern struct channel_config_t channel_config;
+
+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)
+{
+    ipmi_ret_t rc = IPMI_CC_OK;
+
+    sd_bus *bus = ipmid_get_sd_bus_connection();
+    sd_bus_message *reply = nullptr;
+    sd_bus_error error = SD_BUS_ERROR_NULL;
+    int r = 0;
+    char *app = nullptr;
+    int family = 0;
+    unsigned char prefixlen = 0;
+    char* ipaddr = nullptr;
+    uint32_t mask = 0xFFFFFFFF;
+    char* gateway = nullptr;
+    char tmp_netmask[INET_ADDRSTRLEN];
+
+    // Todo: parse the request data if needed.
+
+    // Using Set Channel cmd to apply changes of Set Lan Cmd.
+
+    r = mapper_get_service(bus, app_obj, &app);
+    if (r < 0) {
+        fprintf(stderr, "Failed to get %s bus name: %s\n",
+                app_obj, strerror(-r));
+        rc = IPMI_CC_UNSPECIFIED_ERROR;
+        goto finish;
+    }
+
+    r = sd_bus_call_method(bus, app, app_obj, app_ifc, "GetAddress4", &error,
+                            &reply, "s", app_nwinterface);
+    if (r < 0) {
+        fprintf(stderr, "Failed to call Get Method: %s\n", strerror(-r));
+        rc = IPMI_CC_UNSPECIFIED_ERROR;
+        goto finish;
+    }
+
+    r = sd_bus_message_read(reply, "iyss",
+                            &family, &prefixlen, &ipaddr, &gateway);
+    if (r < 0) {
+        fprintf(stderr, "Failed to get a response: %s\n", strerror(-r));
+        rc = IPMI_CC_RESPONSE_ERROR;
+        goto finish;
+    }
+
+    printf("N/W data from Cache: %s:%s:%s\n",
+            channel_config.new_ipaddr.c_str(),
+            channel_config.new_netmask.c_str(),
+            channel_config.new_gateway.c_str());
+
+    if(channel_config.new_ipaddr.empty()) {
+        channel_config.new_ipaddr.assign(ipaddr);
+    }
+
+    if(channel_config.new_netmask.empty()) {
+        mask = htonl(mask<<(32-prefixlen));
+        uint8_t* p = (uint8_t*)&mask;
+
+        snprintf(tmp_netmask, INET_ADDRSTRLEN, "%d.%d.%d.%d",
+            *p, *(p+1), *(p+2), *(p+3));
+        channel_config.new_netmask.assign(tmp_netmask);
+    }
+
+    if(channel_config.new_gateway.empty()) {
+        channel_config.new_gateway.assign(gateway);
+    }
+
+    printf("N/W data from HW %s:%d:%s:%s\n",
+            family==AF_INET?"IPv4":"IPv6", prefixlen, ipaddr,gateway);
+    printf("N/W data from Cache: %s:%s:%s\n",
+            channel_config.new_ipaddr.c_str(),
+            channel_config.new_netmask.c_str(),
+            channel_config.new_gateway.c_str());
+
+    r = sd_bus_call_method(bus,            // On the System Bus
+                            app,            // Service to contact
+                            app_obj,            // Object path
+                            app_ifc,            // Interface name
+                            "SetAddress4",  // Method to be called
+                            &error,         // object to return error
+                            &reply,         // Response message on success
+                            "ssss",         // input message (Interface,
+                                            // IP Address, Netmask, Gateway)
+                            app_nwinterface,    // eth0
+                            channel_config.new_ipaddr.c_str(),
+                            channel_config.new_netmask.c_str(),
+                            channel_config.new_gateway.c_str());
+    if(r < 0) {
+        fprintf(stderr, "Failed to set network data %s:%s:%s %s\n",
+                channel_config.new_ipaddr.c_str(),
+                channel_config.new_netmask.c_str(),
+                channel_config.new_gateway.c_str(),
+                error.message);
+        rc = IPMI_CC_UNSPECIFIED_ERROR;
+    }
+
+    channel_config.new_ipaddr.clear();
+    channel_config.new_netmask.clear();
+    channel_config.new_gateway.clear();
+
+finish:
+    sd_bus_error_free(&error);
+    reply = sd_bus_message_unref(reply);
+    free(app);
+
+    return rc;
+}
+
 // ATTENTION: This ipmi function is very hardcoded on purpose
 // OpenBMC does not fully support IPMI.  This command is useful
 // to have around because it enables testing of interfaces with
@@ -644,6 +761,10 @@
     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_MSG_FLAGS);
     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_MSG_FLAGS, NULL, ipmi_app_get_msg_flags);
 
+    printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP,
+                                            IPMI_CMD_SET_CHAN_ACCESS);
+    ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_CHAN_ACCESS, NULL,
+                                            ipmi_set_channel_access);
 
     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_CHAN_INFO);
     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_INFO, NULL, ipmi_app_channel_info);
diff --git a/apphandler.h b/apphandler.h
index f59e315..5753c27 100644
--- a/apphandler.h
+++ b/apphandler.h
@@ -28,6 +28,7 @@
     IPMI_CMD_GET_MSG_FLAGS          = 0x31,
     IPMI_CMD_READ_EVENT             = 0x35,
     IPMI_CMD_GET_CAP_BIT            = 0x36,
+    IPMI_CMD_SET_CHAN_ACCESS        = 0x40,
     IPMI_CMD_GET_CHAN_INFO          = 0x42,
 
 };
diff --git a/transporthandler.cpp b/transporthandler.cpp
index 32ed993..8ee2dbe 100644
--- a/transporthandler.cpp
+++ b/transporthandler.cpp
@@ -23,9 +23,7 @@
 
 const int SIZE_MAC = 18; //xx:xx:xx:xx:xx:xx
 
-char new_ipaddr  [INET_ADDRSTRLEN] = "";
-char new_netmask [INET_ADDRSTRLEN] = "";
-char new_gateway [INET_ADDRSTRLEN] = "";
+struct channel_config_t channel_config;
 
 const uint8_t SET_COMPLETE = 0;
 const uint8_t SET_IN_PROGRESS = 1;
@@ -39,8 +37,8 @@
 
 void register_netfn_transport_functions() __attribute__((constructor));
 
-// Helper Function to get IP Address/NetMask/Gateway from Network Manager or Cache
-// based on Set-In-Progress State
+// Helper Function to get IP Address/NetMask/Gateway from Network Manager or
+// Cache based on Set-In-Progress State
 ipmi_ret_t getNetworkData(uint8_t lan_param, uint8_t * data)
 {
     sd_bus *bus = ipmid_get_sd_bus_connection();
@@ -59,6 +57,7 @@
     if (r < 0) {
         fprintf(stderr, "Failed to get %s bus name: %s\n",
                 obj, strerror(-r));
+        rc = IPMI_CC_UNSPECIFIED_ERROR;
         goto cleanup;
     }
     r = sd_bus_call_method(bus, app, obj, ifc, "GetAddress4", &error,
@@ -70,7 +69,8 @@
         goto cleanup;
     }
 
-    r = sd_bus_message_read(reply, "iyss", &family, &prefixlen, &ipaddr, &gateway);
+    r = sd_bus_message_read(reply, "iyss",
+                            &family, &prefixlen, &ipaddr, &gateway);
     if(r < 0)
     {
         fprintf(stderr, "Failed to get a response: %s\n", strerror(-rc));
@@ -78,8 +78,12 @@
         goto cleanup;
     }
 
-    printf("N/W data from HW %s:%d:%s:%s\n", family==AF_INET?"IPv4":"IPv6", prefixlen, ipaddr,gateway);
-    printf("N/W data from Cache: %s:%s:%s\n", new_ipaddr, new_netmask, new_gateway);
+    printf("N/W data from HW %s:%d:%s:%s\n",
+            family==AF_INET?"IPv4":"IPv6", prefixlen, ipaddr,gateway);
+    printf("N/W data from Cache: %s:%s:%s\n",
+            channel_config.new_ipaddr.c_str(),
+            channel_config.new_netmask.c_str(),
+            channel_config.new_gateway.c_str());
 
     if(lan_param == LAN_PARM_IP)
     {
@@ -90,7 +94,7 @@
         }
         else if(lan_set_in_progress == SET_IN_PROGRESS)
         {
-            inet_pton(AF_INET, new_ipaddr, (void *)data);
+            inet_pton(AF_INET, channel_config.new_ipaddr.c_str(), (void *)data);
         }
     }
     else if(lan_param == LAN_PARM_SUBNET)
@@ -102,7 +106,7 @@
          }
          else if(lan_set_in_progress == SET_IN_PROGRESS)
          {
-             inet_pton(AF_INET, new_netmask, (void *)data);
+             inet_pton(AF_INET, channel_config.new_netmask.c_str(), (void *)data);
          }
     }
     else if(lan_param == LAN_PARM_GATEWAY)
@@ -114,7 +118,7 @@
          }
          else if(lan_set_in_progress == SET_IN_PROGRESS)
          {
-             inet_pton(AF_INET, new_gateway,(void *)data);
+             inet_pton(AF_INET, channel_config.new_gateway.c_str(),(void *)data);
          }
     }
     else
@@ -154,26 +158,24 @@
     ipmi_ret_t rc = IPMI_CC_OK;
     *data_len = 0;
     sd_bus *bus = ipmid_get_sd_bus_connection();
-    sd_bus_message *reply = NULL;
+    sd_bus_message *reply = nullptr;
     sd_bus_error error = SD_BUS_ERROR_NULL;
     int r = 0;
-    char *app = NULL;
+    char *app = nullptr;
+
+    char tmp_ipaddr[INET_ADDRSTRLEN];
+    char tmp_netmask[INET_ADDRSTRLEN];
+    char tmp_gateway[INET_ADDRSTRLEN];
 
     printf("IPMI SET_LAN\n");
 
     set_lan_t *reqptr = (set_lan_t*) request;
 
-    // TODO Use dbus interface once available. For now use cmd line.
-    // TODO Add the rest of the parameters like setting auth type
-    // TODO Add error handling
-
-    if (reqptr->parameter == LAN_PARM_IP)
-    {
-        snprintf(new_ipaddr, INET_ADDRSTRLEN, "%d.%d.%d.%d",
+    if (reqptr->parameter == LAN_PARM_IP) {
+        snprintf(tmp_ipaddr, INET_ADDRSTRLEN, "%d.%d.%d.%d",
             reqptr->data[0], reqptr->data[1], reqptr->data[2], reqptr->data[3]);
-    }
-    else if (reqptr->parameter == LAN_PARM_MAC)
-    {
+        channel_config.new_ipaddr.assign(tmp_ipaddr);
+    } else if (reqptr->parameter == LAN_PARM_MAC) {
         char mac[SIZE_MAC];
 
         snprintf(mac, SIZE_MAC, "%02x:%02x:%02x:%02x:%02x:%02x",
@@ -192,63 +194,36 @@
         }
         r = sd_bus_call_method(bus, app, obj, ifc, "SetHwAddress", &error,
                                 &reply, "ss", nwinterface, mac);
-        if(r < 0)
-        {
+        if (r < 0) {
             fprintf(stderr, "Failed to call the method: %s\n", strerror(-r));
             rc = IPMI_CC_UNSPECIFIED_ERROR;
         }
-    }
-    else if (reqptr->parameter == LAN_PARM_SUBNET)
+    } else if (reqptr->parameter == LAN_PARM_SUBNET)
     {
-        snprintf(new_netmask, INET_ADDRSTRLEN, "%d.%d.%d.%d",
+        snprintf(tmp_netmask, INET_ADDRSTRLEN, "%d.%d.%d.%d",
             reqptr->data[0], reqptr->data[1], reqptr->data[2], reqptr->data[3]);
-    }
-    else if (reqptr->parameter == LAN_PARM_GATEWAY)
+        channel_config.new_netmask.assign(tmp_netmask);
+    } else if (reqptr->parameter == LAN_PARM_GATEWAY)
     {
-        snprintf(new_gateway, INET_ADDRSTRLEN, "%d.%d.%d.%d",
+        snprintf(tmp_gateway, INET_ADDRSTRLEN, "%d.%d.%d.%d",
             reqptr->data[0], reqptr->data[1], reqptr->data[2], reqptr->data[3]);
-    }
-    else if (reqptr->parameter == LAN_PARM_INPROGRESS)
+        channel_config.new_gateway.assign(tmp_gateway);
+    } else if (reqptr->parameter == LAN_PARM_INPROGRESS)
     {
-        if(reqptr->data[0] == SET_COMPLETE) // Set Complete
-        {
+        if(reqptr->data[0] == SET_COMPLETE) {
             lan_set_in_progress = SET_COMPLETE;
-            // Apply the IP settings once IP Address, Netmask and Gateway  is set
-            if (!strcmp(new_ipaddr, "") || !strcmp (new_netmask, "") || !strcmp (new_gateway, ""))
-            {
-                printf("ERROR: Incomplete LAN Parameters\n");
-            }
-            else
-            {
 
-                r = sd_bus_call_method(bus,            // On the System Bus
-                                        app,            // Service to contact
-                                        obj,            // Object path
-                                        ifc,            // Interface name
-                                        "SetAddress4",  // Method to be called
-                                        &error,         // object to return error
-                                        &reply,         // Response message on success
-                                        "ssss",         // input message (Interface, IP Address, Netmask, Gateway)
-                                        nwinterface,    // eth0
-                                        new_ipaddr,
-                                        new_netmask,
-                                        new_gateway);
-                if(r < 0)
-                {
-                    fprintf(stderr, "Failed to set network data %s:%s:%s %s\n", new_ipaddr, new_netmask, new_gateway, error.message);
-                    rc = IPMI_CC_UNSPECIFIED_ERROR;
-                }
-                memset(new_ipaddr, 0, INET_ADDRSTRLEN);
-                memset(new_netmask, 0, INET_ADDRSTRLEN);
-                memset(new_gateway, 0, INET_ADDRSTRLEN);
-            }
-        }
-        else if(reqptr->data[0] == SET_IN_PROGRESS) // Set In Progress
+            printf("N/W data from Cache: %s:%s:%s\n",
+                    channel_config.new_ipaddr.c_str(),
+                    channel_config.new_netmask.c_str(),
+                    channel_config.new_gateway.c_str());
+            printf("Use Set Channel Access command to apply them\n");
+
+        } else if(reqptr->data[0] == SET_IN_PROGRESS) // Set In Progress
         {
             lan_set_in_progress = SET_IN_PROGRESS;
         }
-    }
-    else
+    } else
     {
         fprintf(stderr, "Unsupported parameter 0x%x\n", reqptr->parameter);
         rc = IPMI_CC_PARM_NOT_SUPPORTED;
diff --git a/transporthandler.h b/transporthandler.h
index ce7842b..171a636 100644
--- a/transporthandler.h
+++ b/transporthandler.h
@@ -24,4 +24,10 @@
 static const int LAN_PARM_SUBNET      = 6;
 static const int LAN_PARM_GATEWAY     = 12;
 
+struct channel_config_t {
+    std::string new_ipaddr;
+    std::string new_netmask;
+    std::string new_gateway;
+};
+
 #endif