multiple channel configuration support

Previously, the ipmi daemon only supported eth0 and hard-coded it
to channel 1.  This allows one to map via a configuration.  The
channel number provided is checked against a configuration to retrieve
the ethernet device identifier, e.g. eth0.

Tested: Ran on a quanta-q71l and was able to properly set MAC, IP,
Netmask, Gateway IP, and then verified the data was set for the eth1
via `ip addr show eth1`.

Change-Id: I92f63188297304e9454fd0d6fe32bc6cf84bb181
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/app/channel.cpp b/app/channel.cpp
index 91d9987..64cc6e9 100644
--- a/app/channel.cpp
+++ b/app/channel.cpp
@@ -2,6 +2,7 @@
 #include "types.hpp"
 #include "transporthandler.hpp"
 #include "utils.hpp"
+#include "net.hpp"
 
 #include <string>
 #include <arpa/inet.h>
@@ -10,13 +11,22 @@
 #include <phosphor-logging/elog-errors.hpp>
 #include "xyz/openbmc_project/Common/error.hpp"
 
-extern struct ChannelConfig_t channelConfig;
-
 constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
 
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
 
+/** @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));
+
 /** @struct GetChannelAccessRequest
  *
  *  IPMI payload for Get Channel access command request.
@@ -54,6 +64,22 @@
     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;
+    }
+    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
@@ -61,23 +87,23 @@
         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
 
         log<level::INFO>("Network data from Cache",
-                         entry("PREFIX=%s", channelConfig.netmask.c_str()),
-                         entry("ADDRESS=%s", channelConfig.ipaddr.c_str()),
-                         entry("GATEWAY=%s", channelConfig.gateway.c_str()),
-                         entry("VLAN=%d", channelConfig.vlanID),
-                         entry("IPSRC=%d", channelConfig.ipsrc));
+                         entry("PREFIX=%s", channelConf->netmask.c_str()),
+                         entry("ADDRESS=%s", channelConf->ipaddr.c_str()),
+                         entry("GATEWAY=%s", channelConf->gateway.c_str()),
+                         entry("VLAN=%d", channelConf->vlanID),
+                         entry("IPSRC=%d", channelConf->ipsrc));
 
-        if (channelConfig.vlanID != ipmi::network::VLAN_ID_MASK)
+        if (channelConf->vlanID != ipmi::network::VLAN_ID_MASK)
         {
             //get the first twelve bits which is vlan id
             //not interested in rest of the bits.
-            channelConfig.vlanID = le32toh(channelConfig.vlanID);
-            vlanID = channelConfig.vlanID & ipmi::network::VLAN_ID_MASK;
+            channelConf->vlanID = le32toh(channelConf->vlanID);
+            vlanID = channelConf->vlanID & ipmi::network::VLAN_ID_MASK;
         }
 
         // if the asked ip src is DHCP then not interested in
         // any given data except vlan.
-        if (channelConfig.ipsrc != ipmi::network::IPOrigin::DHCP)
+        if (channelConf->ipsrc != ipmi::network::IPOrigin::DHCP)
         {
             // always get the system object
             systemObject = ipmi::getDbusObject(
@@ -97,11 +123,10 @@
 
                 // if the system is having ip object,then
                 // get the IP object.
-                ipObject = ipmi::getIPObject(
-                                bus,
-                                ipmi::network::IP_INTERFACE,
-                                ipmi::network::ROOT,
-                                ipmi::network::IP_TYPE);
+                ipObject = ipmi::getDbusObject(bus,
+                                               ipmi::network::IP_INTERFACE,
+                                               ipmi::network::ROOT,
+                                               ethIp);
 
                 // Get the parent interface of the IP object.
                 try
@@ -120,7 +145,7 @@
                                           ipmi::network::ETHERNET_INTERFACE));
                     commit<InternalFailure>();
                     rc = IPMI_CC_UNSPECIFIED_ERROR;
-                    channelConfig.clear();
+                    channelConf->clear();
                     return rc;
                 }
 
@@ -138,7 +163,7 @@
                         bus,
                         ipmi::network::ETHERNET_INTERFACE,
                         ipmi::network::ROOT,
-                        ipmi::network::INTERFACE);
+                        ethdevice);
 
                 networkInterfacePath = std::move(networkInterfaceObject.first);
             }
@@ -154,21 +179,21 @@
             // check whether user has given all the data
             // or the configured system interface is dhcp enabled,
             // in both of the cases get the values from the cache.
-            if ((!channelConfig.ipaddr.empty() &&
-                 !channelConfig.netmask.empty() &&
-                 !channelConfig.gateway.empty()) ||
+            if ((!channelConf->ipaddr.empty() &&
+                 !channelConf->netmask.empty() &&
+                 !channelConf->gateway.empty()) ||
                 (enableDHCP)) // configured system interface mode = DHCP
             {
                 //convert mask into prefix
-                ipaddress = channelConfig.ipaddr;
-                prefix = ipmi::network::toPrefix(AF_INET, channelConfig.netmask);
-                gateway = channelConfig.gateway;
-                if (channelConfig.vlanID != ipmi::network::VLAN_ID_MASK)
+                ipaddress = channelConf->ipaddr;
+                prefix = ipmi::network::toPrefix(AF_INET, channelConf->netmask);
+                gateway = channelConf->gateway;
+                if (channelConf->vlanID != ipmi::network::VLAN_ID_MASK)
                 {
                     //get the first twelve bits which is vlan id
                     //not interested in rest of the bits.
-                    channelConfig.vlanID = le32toh(channelConfig.vlanID);
-                    vlanID = channelConfig.vlanID & ipmi::network::VLAN_ID_MASK;
+                    channelConf->vlanID = le32toh(channelConf->vlanID);
+                    vlanID = channelConf->vlanID & ipmi::network::VLAN_ID_MASK;
                 }
                 else
                 {
@@ -196,21 +221,20 @@
                             ipObject.first,
                             ipmi::network::IP_INTERFACE);
 
-                    ipaddress = channelConfig.ipaddr.empty() ?
+                    ipaddress = channelConf->ipaddr.empty() ?
                         properties["Address"].get<std::string>() :
-                        channelConfig.ipaddr;
+                        channelConf->ipaddr;
 
-                    prefix = channelConfig.netmask.empty() ?
+                    prefix = channelConf->netmask.empty() ?
                         properties["PrefixLength"].get<uint8_t>() :
                         ipmi::network::toPrefix(AF_INET,
-                                channelConfig.netmask);
-
+                                channelConf->netmask);
                 }
                 catch (InternalFailure& e)
                 {
                     log<level::INFO>("Failed to get IP object which matches",
                             entry("INTERFACE=%s", ipmi::network::IP_INTERFACE),
-                            entry("MATCH=%s", ipmi::network::IP_TYPE));
+                            entry("MATCH=%s", ethIp));
                 }
 
                 auto systemProperties = ipmi::getAllDbusProperties(
@@ -219,10 +243,9 @@
                         systemObject.first,
                         ipmi::network::SYSTEMCONFIG_INTERFACE);
 
-                gateway = channelConfig.gateway.empty() ?
+                gateway = channelConf->gateway.empty() ?
                         systemProperties["DefaultGateway"].get<std::string>() :
-                        channelConfig.gateway;
-
+                        channelConf->gateway;
             }
         }
 
@@ -248,7 +271,7 @@
                 bus,
                 ipmi::network::ETHERNET_INTERFACE,
                 ipmi::network::ROOT,
-                ipmi::network::INTERFACE);
+                ethdevice);
 
         // setting the physical interface mode to static.
         ipmi::setDbusProperty(bus,
@@ -264,14 +287,14 @@
         ipmi::deleteAllDbusObjects(bus,
                                    ipmi::network::ROOT,
                                    ipmi::network::IP_INTERFACE,
-                                   ipmi::network::IP_TYPE);
+                                   ethIp);
 
         if (vlanID)
         {
             ipmi::network::createVLAN(bus,
                                       ipmi::network::SERVICE,
                                       ipmi::network::ROOT,
-                                      ipmi::network::INTERFACE,
+                                      ethdevice,
                                       vlanID);
 
             auto networkInterfaceObject = ipmi::getDbusObject(
@@ -282,7 +305,7 @@
            networkInterfacePath = networkInterfaceObject.first;
         }
 
-        if (channelConfig.ipsrc == ipmi::network::IPOrigin::DHCP)
+        if (channelConf->ipsrc == ipmi::network::IPOrigin::DHCP)
         {
             ipmi::setDbusProperty(bus,
                                   ipmi::network::SERVICE,
@@ -330,13 +353,13 @@
                         entry("ADDRESS=%s", ipaddress.c_str()),
                         entry("GATEWAY=%s", gateway.c_str()),
                         entry("VLANID=%d", vlanID),
-                        entry("IPSRC=%d", channelConfig.ipsrc));
+                        entry("IPSRC=%d", channelConf->ipsrc));
 
         commit<InternalFailure>();
         rc = IPMI_CC_UNSPECIFIED_ERROR;
     }
 
-    channelConfig.clear();
+    channelConf->clear();
     return rc;
 }
 
@@ -350,17 +373,15 @@
     auto responseData = reinterpret_cast<GetChannelAccessResponse*>
             (outPayload.data());
 
-    // Channel 1 is arbitrarily assigned to ETH0 channel
-    constexpr auto channelOne = 0x01;
-
     /*
      * The value Eh is used as a way to identify the current channel that
      * the command is being received from.
      */
     constexpr auto channelE = 0x0E;
+    int channel = requestData->channelNumber;
+    auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
 
-    if (requestData->channelNumber != channelOne &&
-        requestData->channelNumber != channelE)
+    if (channel != channelE && ethdevice.empty())
     {
         *data_len = 0;
         return IPMI_CC_INVALID_FIELD_REQUEST;
@@ -406,20 +427,20 @@
         IPMI_CHANNEL_TYPE_IPMB,
         1,0x41,0xA7,0x00,0,0};
     uint8_t *p = (uint8_t*) request;
+    int channel = (*p) & CHANNEL_MASK;
+    std::string ethdevice = ipmi::network::ChanneltoEthernet(channel);
 
     printf("IPMI APP GET CHANNEL INFO\n");
 
-    // The supported channels numbers are 1 and 8.
+    // The supported channels numbers are those which are configured.
     // Channel Number E is used as way to identify the current channel
     // that the command is being is received from.
-    if (*p == 0xe || *p == 1 || *p == 8) {
-
-        *data_len = sizeof(resp);
-        memcpy(response, resp, *data_len);
-
-    } else {
+    if (channel != 0xe && ethdevice.empty()) {
         rc = IPMI_CC_PARM_OUT_OF_RANGE;
         *data_len = 0;
+    } else {
+        *data_len = sizeof(resp);
+        memcpy(response, resp, *data_len);
     }
 
     return rc;